Mods make various changes to the game. For example, they change the interface, add costumes for characters, add new characters, and change scenes that are already in the game.
Official mods can be found in the game itself. In the main menu at the top there is a tab Mods.
By default you should have 3 mod sources: Local Mods, Patron Mods and Official.
By default, mods are located in the “Kunoichi Trainer/mods” folder. The “mods” folder may not exist. In this case, it must be created manually.
Also in the game settings, if you scroll to the bottom, you can specify a different path to the folder with mods.
Each mod should be in a separate folder named the same as the mod's id.
There should be a manifest.json
file in the mod folder. This file must contain the same information as in the source, except for the file_name
field.
{ "id": "mod-id", "name": "Mod name", "version": "1.0", "description": "Description of the mod", "game_version": "0.16" }
Code files must be in .rpym or .rpymc format. This is the same as .rpy and .rpyc. Don't know about .rpy and .rpyc? See RenPy documentation.
You write mod code in .rpym files, when you start the game they will be compiled into .rpymc.
The files can simply be in a folder, or in a .rpa archive.
To do this, you need to add a new parameter merge_folders
in manifest.json
, which is an array of strings.
This parameter specifies a list of folders, files from which will be recognized by the game, as if they were in the game itself from the very beginning.
To change the image in the game, you need to know the location of the original image. In folder, specified in the merge_folders
parameter, you need to place the new image in the same path as the original image.
A simple mod that replaces the background of the room in which the player appears at the very beginning of the game.
The original background is stored in this path:
images/bg/beginning_room.webp
Mod folder structure:
mods └───mod-id │ │ manifest.json │ └───my_folder │ └───images │ └───bg │ └───beginning_room.webp
Where mod-id/my_folder/images/bg/beginning_room.webp
is the new image we want to replace the original one with.
The content of the manifest.json
file is:
{ "id": "mod-id", "name": "Mod name", "version": "1.0", "description": "Description of the mod", "game_version": "0.16", "merge_folders": ["my_folder"] }
That's all it takes to replace the image.
To do this, you need to create a class (in the .rpym file) that inherits the Module
class and subscribe to the necessary events. See List of events
For example, a module that reacts to a change in the time of day and a change in location.
init python: class DemoModule(Module): id="demo" # Each module must have a unique id subscribe_on_events=["time_update", "change_location"] def on_event(self, event): if event.type == "time_update": if event.is_morning: renpy.notify("Morning has come") elif event.is_evening: renpy.notify("Evening came") elif event.type == "change_location": renpy.notify("New location: " + event.new_location.id)
To do this, you need to create a variable from the Item class if it's just an item, or from the Outfit class if it's an item of clothing.
item_
.Adding a regular item to the Tenten shop.
init python: item_pen = Item( name=__("Handle"), cost=5, icon="my_mod_folder/pen.webp", descr=__("Pen with ink."), shop="tenten", )
Adding clothes to the Ten-Ten store.
init python: item_ino_my_new_costume = Outfit( name=__("New costume for Ino"), cost=100, descr=__("Item description"), char="Ino", lust=10, layer="costume", val="my_new_costume", shop="tenten", ptr=False, icon=KTCharInventoryPreview( char_name="ino", layers=ino_preview_layers + [("costume", "my_new_costume")], crop=ino_costume_inv_preview ), )
Mod folder structure:
mods └───mod-id │ │ manifest.json │ │ item.rpym │ └───merge_folder │ └───images │ └───characters │ └───ino │ └───costume │ └───my_new_costume.webp
With this approach, the item will only be in the store if you start a new game. To add an item to the store, if the player is loading an already existing save, use Module. See. How to interact with game events and event game_loaded.
You will get something like this:
def on_event(self, event): if event.type == "game_loaded" and not inv_shop.has_item(item_my_item): inv_shop.add(item_my_item)
if game.time.is_morning: "Now is the day" elif game.time.is_evening: "It is evening now" $ game.time.set_morning() "Now is the day" $ game.time.set_evening() "Now it's evening" $ game.time.next_cycle() "If there was a day, now it is evening. If it was evening, it is now day and the counter of days has increased." $ game.time.next_day() "It is now exactly a day and the counter of days has increased by 1." $ game.time.skip_days(10) "The day counter has increased by 10."