====== Mods ======
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.
===== Sources =====
By default you should have 3 mod sources: **Local Mods**, **Patron Mods** and **Official**.
* Local mods - These are mods that are already installed
* Patron Mods - Official source of mods available only to Patrons.
* Official - Official source of mods available to everyone.
==== List of sources ====
* Official mods: http://mods.kunoichi-trainer.ninja/official/manifest.json
* Game translations: http://mods.kunoichi-trainer.ninja/translations/manifest.json
* Mods for mod developers: https://bitbucket.org/dinaki/dev-mods/raw/master/manifest.json
[[mods:create_source|How to create your own source]]
===== Mods =====
==== How to install an official mod ====
- Start the game
- At the top, select the **Mods** tab.
- Select the source of the mods. For example, Official.
- Select the mod.
- Click the **Install** button.
- Reload the game.
==== How to install a third-party mod ====
- Unpack the archive with the mod into a folder with mods. See. [[#where_mods_are_stored|where the mods folder is located]].
==== Where mods are stored ====
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.
==== How to disable a mod ====
- Star the game.
- At the top, select the **Mods** tab.
- Select the source **Local Mods**.
- Select a mod.
- Click **Disable**.
-------
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.
=== Example ===
{
"id": "mod-id",
"name": "Mod name",
"version": "1.0",
"description": "Description of the mod",
"game_version": "0.16"
}
==== Scripts ====
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 [[https://www.renpy.org/doc/html/language_basics.html#files|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.
==== How to replace an image from a game ====
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.
=== Example ===
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.
==== How to interact with game events ====
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 [[mods:events|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)
==== How to add an item to the store ====
To do this, you need to create a variable from the [[mods:classes#item|Item]] class if it's just an item, or from the [[mods:classes#outfit|Outfit]] class if it's an item of clothing.
* It is desirable that the variable name starts with ''item_''.
* Item name must be unique and must not be repeated.
=== Examples ===
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 [[mods:classes#module|Module]]. See. [[mods#how_to_interact _with_game_events|How to interact with game events]] and [[mods:events#game_loaded|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)
===== Game variables =====
==== Time of the Day ====
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."