Modding

From Summertime Saga Wiki
(Redirected from Modding API)
Jump to: navigation, search
Warning red icon.pngThis API is depreciated: the features are incomplete and will not be updated for future versions. USAGE IS HIGHLY DISCOURAGED.

The modding of the game is based on the Summertime Saga API. It is recommended to read the corresponding article beforehand in order to familiarize yourself with the specificities of the code.

Hooking into the game

Registration and enabling of the mod

In an init -9 python or later, use the class method "register" of the ModManager class to register your mod to the game. This registration makes the mod show up in the (upcoming) Mods menu on the main menu so the players have the choice to enable or disable the mod for the game.

Example:

init python:
    ModManager.register("ikarumod")

A manifest file named modname_manifest.json must be added in the scripts/data folder.

Manifest file

The manifest file details in which labels the mod should hook into the game, and which screens. It also defines the name of the main function you wish to use to hook into the game.main() function, if any.

Preferred mod load order

Adding a key named load_order to the manifest will allow your mod to be loaded after a specific amount of mods, or before.

The value of this key must be a string leading with either >, < or = followed by an integer. Additionally, the values inf and -inf can be used to set the load order as last, and absolutely first. If several mods are defined with -inf in load_order, then a random order is chosen. The default value is inf, which means the mods are added last to the list in no particular order.

Main function name

This key should be a string containing the name of the main function of your mod. The function is searched for ini the globals() of the game (global namespace)

Init label name

This key should give the name of a label your mod uses at init time, which means after the game is fully initialized, either at the start of the game or after the load of the game.
This label is called in a new context, and it must return, otherwise other mods won’t be loaded

Adding to the game’s json files

Keys in the manifest named items, achievements and text_messages can be used to add data to the game json files. Each of these keys expects a full json dictionary formatted in the same way as their respective models.

Example of manifest:

{
    "name":"ikarumod",
    "version": "0.1.0",
    "load_order": ">3",
    "main_function_name": "ikarumod_main",
    "init_label_name":"ikarumod_init_label",
    "text_filter":"ikarumod_text_filter",
    "items": {
        "item1":{
            ...
        },
        "item2":{
            ...
        }
    },
    "text_messages":{
        "text_message1":{
            ...
        }
    },
    "screen_hooks":{
        "upstairs_bedroom":"upstairs_bedroom_ikarumod",
        "bedroom":"bedroom_ikarumod"
    },
    "label_hooks":{
        "bedroom":"bedroom_ikarumod"
    },
    "achievements":{
        "angler-2"{
            ...
        }
    }
}

Mod init order

Information icon.pngMods are assumed to be in an initial random order.

For every mod that is enabled, create a Mod instance with that mod’s name, parse the load order from the manifest, then insert that mod in the ModManager.mods list in the proper position. Then, for every mod in the ModManager.mods list, call their init label, if defined. Finally, update the game stores (i.e. achievements, items and text_messages) with every data of the mod. Updates overwrite the keys, so the load order can be used to overwrite the game’s (although ill‐advised) or another mod’s items/text_messages/achievements.

Text filter

The text filter key in the manifest allows you to create your own filter function without overwriting other mods. If undefined, this defaults to lambda text:text, otherwise the value of this key should be a string of the name of the function you wish to pass in as a text filter. If the function cannot be found in the global namespace (i.e. the globals() dictionary), then a ModLoaderError("No Function named {text_filter} found in the global namespace. Is it defined properly?") exception will be raised.

Screens

Create your screens with the following convention: modname + _ + in‐game screen name (the Ren’Py definition name). Screens are being included into the main game screens with the use statement. If you wish to add new locations, you have to define a screen for it, in which case, you can inspire yourself with the existing screens in the game.
To ensure proper game progression, you shoul only add imagebuttons. Please refer to Summertime Saga API § User-defined screen actions for more information on which screen actions the game defines.

The mod screen hook is quite simple as it uses RenPy’s use statement for every modded screen. The screen hook is called at the end of each screen, so background set up and imagebuttons for the game are already properly shown and defined. You can then add your own imagebuttons, and other screen features to it.

Hookable screens are, at least for now, only the location screens, and do not include the user interface.

Example:

init python:
    ModManager.register_main(ikarumod_main)

In this example, ikarumod_main is assumed to be a callable, which should be a python function. The function is called with no arguments at the end of game.main() method.

Hooking into the main function is usually useful for code you want executed every time the game returns to a main screen, i.e. at the end of location labels for instance. This is where you can repeatedly check if the condition for an achievement has been fulfilled. If the provided function is not a callable, a ModLoaderError exception will be raised.

Imported modules and directory structure

Third‐party modules

Platform agnostic

  • os
  • pygame
  • sys
  • from time : time, clock
  • from copy : copy, deepcopy
  • datetime
  • re
  • random
  • math
  • from collections : defaultdict, OrderedDict and Counter
  • weakref
  • codecs
  • hashlib
  • json
  • itertools
  • operator
  • textwrap
  • deuces

Desktop builds

  • certifi
  • requests

Mobile builds

  • android
  • pyjnius

Directory structure

  • game
    • audio: SFX and musics
    • fonts: fonts for the game.
    • images
      • achievements: achievement‐related images.
      • cookie_jar: cookie jar buttons, popups and thumbnails.
      • backgrounds: backgrounds and close‐ups.
      • boxes: popups and general purpose buttons (like the go back button).
      • buttons: most minigame assets, and menu buttons.
      • cellphone: cellphone images assets.
      • characters: character poses. One folder per character.
      • map: map locations and map background.
      • objects: character buttons, item buttons, doors, etc.
      • vfx: special visual effects (like the rain in Roxxy’s trailing scene).
    • python-packages: third party python modules.
    • scripts
      • characters: one folder for each character, containing an fsm file, a character.rpy file for miscellaneous stuff, and a layeredimage definition file. May contain a file for the character’s button and the according dialogues.
      • core: core files are put there, mostly what has been documented in the Modding API section of this manifesto.
      • data: JSON files that contain data about the game. Items, achievements, keymap or text messages are defined here.
      • defines: general image definitions, transforms, etc.
      • locations: one folder for every location, sorted in a tree‐like structure. Each location has a main file, a screen file and a dialogues file.
      • minigames: one folder for every minigame, the minigame dialogues and screen files are in that folder.
      • script.rpy
      • pregnancy_announcements.rpy
    • changelog.txt
    • pledge_list.txt

Translating the game

Label calling in the game

The game offers a function in the Game class to select different dialogues based on the language class attribute defined there. To change that attribute, it’s just a matter of writing this piece of code in a separate ".rpy" file.

Example:

init 1 python:
    Game.language = "es" # for Spanish for instance, "fr" for French, etc

Every time a dialogue is called, the game looks for the label name and the language string at the end. For instance, the label bank_liu_account_info has the English version in; and to overwrite that dialogue, you’d have bank_liu_account_info_fr or bank_liu_account_info_es depending on the value of the language string.

Any dialogue can be translated with this method. Keeping a consistent directory structure is recommended, because of the large number of dialogues in the game. You also have to copy over the posing. For your convenience, every dialogue label is stored in a dialogues.rpy file for that location. Just copy the file and edit the dialogue and the label name.

Cutscenes and minigame instructions

You can’t edit the cutscenes directly. Well, you could, but you shouldn’t! Using the config.say_menu_text_filter variable is the best way. Just register a function to that variable with one argument, text, which contains the text of the displayable. Then edit the text how you see fit.

Example of translation:

init 10 python:
    fr_translations = {"Using the key and stool, I was able to get into our attic.\nI had never been up there before.\nI was filled with excitement wondering what treasures {b}[deb_name]{/b} and dad had stashed away.": "En utilisant la clé et le tabouret, je pus aller dans le grenier.\nJe n'y avais jamais été auparavant.\nUn sentiment d'excitation m'envahissait alors que je me demandais quels trésors {b}[deb_name]{/b} et papa avaient caché là-haut."}

    def fr_text_filter(text):
        if text in fr_translations.keys():
            return fr_translations[text]
        else:
            return text
    
    config.say_menu_text_filter = fr_text_filter

Keep in mind however that Ren’Py engine doesn’t handle large dictionaries. elif statements can be used to split the content into several dictionaries if necessary.

Example:

def fr_text_filter(text):
    if text in fr_translations_1.keys():
        return fr_translations_1[text]
    elif text in fr_translations_2.keys():
        return fr_translations_2[text]
    else:
        return text

Location names

In an init 1 python (or later) block, you can set the display name of any location object in the game. This allows renaming and translation of any game location, thus displaying that name in relevant parts of the user interface.

Example:

init -1 python:
    L_map.display_name = "New Map Name"