Mod Information - KristalTeam/Kristal GitHub Wiki
File Structure
Files in Kristal mods need to follow a specific structure to be loaded properly. The following folders describe what kinds of files should go where.
assets
: All sprites and sounds go in this folder. This folder is subdivided into fonts
, music
, sounds
, and sprites
. All textures go inside assets/sprites
, regardless of how they're used (with the exception of preview.png
and icon.png
, which go in the mod base).
scripts
: All code files go in this folder. This folder is subdivided into battle
, world
, shops
, objects
, drawfx
, globals
, and data
, which can have further subdivision. The rest of the wiki will detail where files go and how to use them, but this is a brief summary of each folder:
battle
: Divided intocutscenes
,bullets
,encounters
,enemies
, andwaves
. Files defined in these folders will be used for making encounters and defining what happens in them.world
: Divided intocutscenes
,scripts
,events
,bullets
,maps
, andtilesets
. Files defined in these folders will be used for making overworld rooms and objects that appear in them.shops
: Files defined in this folder will each create a shop.objects
: Files defined in this folder will create a global variable named after the file for the Object class that is defined.drawfx
: Files defined in this folder will create a global variable named after the file for the DrawFX class that is defined.globals
: Files defined in this folder will create global variables named after each file containing whatever data the file returns.data
: Divided intoactors
,party
,items
, andspells
. Files defined in these folders will define data that the mod can use to create objects that it may need.
libraries
: All library mods go in this folder. Each library should be either a folder or a .zip. See Libraries below for more detail.
Additionally, some files go in the mod base:
mod.json
: Contains data Kristal uses to load the mod.
mod.lua
: Defines global functions and variables used throughout the mod.
preview.png
: Optional image used on the mod loader screen, when hovering over the mod. Can be a series of images for an animation.
preview.lua
: Optional .lua file used on the mod loader screen, when hovering over the mod. See the example preview for an example of what this could look like.
icon.png
: Optional image used for the icon of the mod on the mod loader screen. Can be a series of images for an animation.
If neither preview.png
or preview.lua
are provided, the mod loader will display the usual background instead.
mod.json and mod.lua
mod.json
is the file that contains the data relevant to initially loading your mod. See the example mod for an example of how to format it. The following fields are used by Kristal:
name
: The name that appears in the mod loader to represent your mod. Defaults to the name of the folder if unspecified.
subtitle
: An optional short description of your mod that will display below the name.
id
: An optional string defining the ID used for your mod. If undefined, the mod's ID will be its folder's name.
encounter
: A string referring to the ID of an encounter. If defined, the mod will immediately start in that encounter when creating a new file. See Encounters for more detail.
map
: A string referring to the ID of a map to load upon starting a new file. See Overworld for more detail. Overrides encounter
if defined.
shop
: A string referring to the ID of a shop to load upon starting a new file. See Shops for more detail. If defined, it will override map
, and instead use the map
string as the room to place the player when they exit the shop.
party
: An array of strings, containing a list of party members that your mod starts with. Each string refers to the ID of a party member (see Party Members for more detail.).
maxFollowers
: The max allowed followers in the party for the mod.
inventory
: A dictionary defining arrays labeled item
, key
, armor
, or weapon
, with each array listing item IDs that the party should start with by default. See Items for more detail.
equipment
: A dictionary defining equipment that each party member starts with being equipped by default. Each index of the dictionary should refer to the ID of a party member, and each value should be a dictionary that can define a weapon
string, and an armor
array.
chapter
: The chapter number of the mod. Used to change game behavior (eg. stats are changed, some sprites are different, etc).
transition
: A boolean value that determines whether your mod will start with the Kris and Susie falling into the dark world. False by default.
hidden
: If true, the mod will not appear in the mod list.
hardReset
: Forces LÖVE to restart upon reloading the mod.
config
: A table used to change configurable values for either Kristal or libraries installed in a mod. Each index in this table should refer to the ID of a library, and the value of each index should be a dictionary defining specific indexes for that library's config. See Libraries below for more detail.
You can use the config
table to alter many properties of the engine for your mod, as well. To do so, you would use kristal
as the ID for the index of the config
dictionary, like so:
{
"id": "example_mod_id",
"config": {
"kristal": {
"ralseiStyle": 1
}
}
}
The following is a list of all values that can be defined in Kristal's config:
susieStyle
: How Susie should look by default if used. Should be a number correlating to a Deltarune chapter (eg. 1 will make her have bangs over her eyes). Defaults to the mod.json's chapter
value.
ralseiStyle
How Ralsei should look by default if used. Should be a number correlating to a Deltarune chapter. Defaults to the mod.json's chapter
value.
growStronger
: Whether the party should level up when defeating enemies with violence, and display the "You became stronger" message. Defaults to false.
growStrongerChara
: A string defining a character that will be named in the "X became stronger" message if they're in the party. If undefined, the message will always be "You became stronger". Defaults to nil.
smallSaveMenu
: Whether the smaller save menu from chapter 1 should be used. Defaults to true if chapter
is 1, and false otherwise.
partyActions
: Whether the party should have X-Actions in encounters by default. Defaults to false if chapter
is 1, and true otherwise.
mercyMessages
: Whether mercy percentages should appear above enemies when mercy is added. Defaults to false if chapter
is 1, and true otherwise.
mercyBar
: Whether enemies should have a mercy bar visible next to their health bar. Defaults to false if chapter
is 1, and true otherwise.
enemyBarPercentages
: Whether enemies' health and mercy bars should display percentage text. Defaults to false if chapter
is 1, and true otherwise.
speechBubble
: What speech bubble style should be used for enemy textboxes by default. Defaults to round
if chapter
is 1, and cyber
otherwise.
enableStorage
: Whether the inventory should have a storage
storage to place extra items in. Defaults to false if chapter
is 1, and true otherwise.
enemyAuras
: Whether enemies should emit a red aura around them in the overworld by default. Defaults to false if chapter
is 1, and true otherwise.
pushBlockInputLock
: Whether push blocks should lock player input while the block is moving. Defaults to true.
mod.lua
is an optional file that will be run when the mod is loaded. It can define functions inside the global Mod
class, which can be called later during specific Kristal functions. A basic mod.lua file looks something like this:
function Mod:init()
print("mod loaded!")
-- could set global variables here, or initialize hooks, or whatever you like
end
The following functions are functions that can be defined:
init()
: Called before the mod loads.
postInit(new_file)
: Called after the mod loads. new_file
is a boolean that will be true if a new file has been loaded.
save(data)
: Called when the game is saved. data
is a table containing all save data for the save. Editing data
allows you to change save data before it's saved to the save file.
load(data, new_file, index)
: Called when the game loads a save. data
is a table containing all save data for the save, new_file
is a boolean that will be true if a new file has been loaded, and index
is the index of the file chosen, if the mod has save files; if it doesn't, then index
will always be 1.
preUpdate()
: Called every frame, before anything else updates. If this returns true
, then nothing else will update.
postUpdate()
: Called every frame, after everything else updates.
preDraw()
: Called every frame, before anything else draws. If this returns true
, then nothing else will draw.
postDraw()
: Called every frame, after everything else draws.
onKeyPressed(key)
: Called every time a key is pressed.
onKeyReleased(key)
: Called every time a key is released.
onTextInput(key)
: Called when text is inputted.
onMousePressed(x, y, button, istouch, presses)
: Called when a mouse button is pressed. See love.mousepressed for more detail.
onMouseMoved(x, y, dx, dy, istouch)
: Called every frame the mouse moves. See love.mousemoved for more detail.
onMouseReleated(x, y, button, istouch, presses)
: Called when a mouse button is released. See love.mousereleased for more detail.
onRegistered()
: Called after all mod scripts have been loaded. There are also several similar functions that get called when specific categories of scripts have finished loading. Each type of script is loaded in a particular order; the following is a list of the registered scripts and the functions that get called for them, in order:
onRegisterGlobals()
onRegisterObjects()
onRegisterDrawFX()
onRegisterActors()
onRegisterPartyMembers()
onRegisterItems()
onRegisterSpells()
onRegisterEncounters()
onRegisterEnemies()
onRegisterWaves()
onRegisterBullets()
onRegisterCutscenes()
onRegisterEventScripts()
onRegisterTilesets()
onRegisterMaps()
onRegisterEvents()
onRegisterControllers()
onRegisterShops()
registerDebugOptions(debug)
: Called when the DebugSystem class is created, to be used to create debug commands that can be used in-game (see Debug Menus for more detail). debug
is the DebugSystem instance that should be used to register commands.
regiserTextCommands(text)
: Called every time Text is created. text
is the Text instance that is being created. See Text:registerCommand()
for more detail.
loadObject(world, name, data)
: Called every time an overworld object loads. world
refers to the current Game.world
instance, name
is the name of the object, and data
is a table of the object's properties. Should return an object if it successfully loaded; otherwise, return nil
. See Events for more detail.
getActionButtons(battler, types)
: Called for each party member when the game retrieves the list of action buttons for the member for battle. battler
is an instance of the PartyBattler, and types
is a table of strings referring to the list of actions the battler would normally have. Returning a table of strings will cause the battler passed into the function to have the action buttons specified by the table (eg. returning {"act", "defend"}
if battler.chara.id
is kris
will make it so Kris only has ACT and DEFEND buttons in battles). This is not necessary for basic action bars; see Party Members for more detail.
onFootstep(chara, num)
: Called each time a character takes a step. chara
refers to the Character instance that is stepping, and num
is a number that alternates between 1 and 2 each step.
onTextSound(sound, node)
: Called during dialogue text for character node in the text string. sound
is a string referring to the sound path for the character that is currently talking, and node
is the current node for the text (see Text Object for more detail). If this function returns true, sound will not be played automatically, and must be played manually in the function; this can be used to override sound behavior for certain characters by returning true if sound
is their sound path.
onShadowCrystal(item, light)
: Called when the player uses either a Shadow Crystal or Glass item in the overworld. item
is the Item instance being used, and light
is a boolean determining whether the party is in a light world or not. If this function returns true, then the default text will not display when using the item, allowing the coder to define custom text for their mod.
onConvertToLight(inventory)
: Called when the player transitions from a dark world to a light world. inventory
is the light world Inventory instance that is being created. Game.inventory
will still refer to the dark world inventory when this is called. Can be used to add certain items to the player's light inventory based on the presence of items in their dark inventory.
onConvertToDark(inventory)
: Called when the player transitions from a light world to a dark world. inventory
is the dark world Inventory instance that is being created. Game.inventory
will still refer to the light world inventory when this is called. Can be used to add certain items to the player's dark inventory based on the presence of items in their light inventory.
Libraries
Libraries are essentially small mods that other mods can use, to add objects or functions or any other types of custom stuff to the engine. Libraries can be added to mods by putting them in your mod's libraries
folder. They are packaged the same way as normal mods, with assets
and scripts
folders being in the base folder of the library; the only difference is mod.json
and mod.lua
should be named lib.json
and lib.lua
for libraries.
lib.json
and lib.lua
function differently from mod.json
and mod.lua
as well. Instead of using a global Mod
or Lib
variable when defining functions in lib.lua
, you instead need to define your own table, like so:
local Lib = {}
function Lib:init()
print("library loaded")
end
return Lib
The changes to lib.json
are much more significant, however. Most values defined in a mod's mod.json
are irrelevant to libraries; the only values that should be defined in a lib.json
are id
and config
. id
is an optional string defining the ID of the library, defaulting to the library's folder's name if undefined. config
is a table that defines values that mod users can configure for their own mod. Each index in this table defines the name of the value, and the value for each index defines the default value for the configuration if the mod using the library does not define it. Library config options can be later retrieved by using Game:getConfig()
(see Game functions below for more detail).
Save Data
Every mod keeps track of a play session in a save file. Kristal automatically saves and loads certain information, most of which are kept track of in the global Game
class. The fields that the game saves and loads to Game
are:
chapter
: The chapter number of the mod.
name
: The name of the save file.
level
: The level of the save file.
playtime
: How long the mod has been played.
room_name
: The name of the room the save file starts in.
room_id
: The internal name of the room the save file starts in, used to load the room (see Overworld for more detail).
gold
: The amount of gold on the save file.
xp
: The amount of experience on the save file. Unused by default.
level_up_count
: The amount of level ups needed for certain party members to get stat boosts from winning battles by fighting.
inventory
: A table of the save file's inventory. See Inventory for more information.
flags
: A table of any extra information the mod wants to store.
Additionally, the Game
table has the following functions available related to save files:
Game:setFlag(flag, value)
: Sets a save data flag to the specified value.
Game:getFlag(flag, default)
: Gets a save data flag's value, returning default
if the flag doesn't exist.
Game:addFlag(flag, amount)
: Adds to the value of a flag (assuming an initial value of 0 if the flag is previously undefined). amount
is the amount to add to the value, defaulting to 1.
Game:saveQuick(...)
: Creates a quick save that can be reloaded. ...
refers to the position the player will spawn at when the quick save is loaded, and can be used in 2 different ways:
- If
...
is a string, the player will spawn at a marker with the name specified. See Overworld for more detail. - If
...
is either a table with 2 numbers, or a series of 2 numbers, the player will spawn at the coordinates specified, in pixels from the topleft of the room.
Game:loadQuick()
: Loads the most recent quicksave, or most recent save if there is no quicksave.
Game:save(...)
: Creates a table of all current save data in the game (eg. current equipped armor on party members, current room, etc.), passes it into Mod:save()
if it's defined, then returns the table. ...
functions the same as for Game:saveQuick()
. Does not actually save the game data to a save file; see Kristal.saveGame()
below for more detail.
Game:load(data, index)
: Loads the game with data provided by the data
argument. data
is a table containing save data to load the game from (see Save Data above for what can be defined), and index
is an optional number that will define what save file is currently loaded.
Game
and Kristal
Variables and Functions
The Game
class is the class that keeps track of all gameplay related functions, such as updating the current game state and giving the player control, and it has variables and functions available that are separate from saving the game.
stage
: An object that is responsible for being the highest parent object. This object is updated directly by the game, and is the parent of other important objects, such as World
and Battle
. See Objects for more detail.
world
: The current instance of the World
object. Will never be nil, even when an encounter is happening. See World for more detail.
battle
: The current instance of the Battle
object. Will be nil if no encounter is happening. See Battle for more detail.
inventory
: The current instance of the Inventory
object. See Inventory for more detail.
music
: A globally active Music instance.
lock_input
: If true, the player will not be able to control the overworld player.
started
: Determines whether the mod has started yet. Will be false during the initial transition if transition
is true in mod.json.
gameOver(x, y)
: Creates a game over screen, with a heart sprite breaking at the specified coordinates.
encounter(encounter, transition, enemy)
: Starts an encounter. encounter
can be either the ID of an encounter or an existing Encounter instance, transition
is an optional boolean determining whether the game should transition into the encounter or start it immediately (defaulting to true), and enemy
is an optional EnemyBattler instance that will be moved to its starting position in the specified encounter.
addPartyMember(chara, index)
: Adds a party member to the current party. chara
is either a string referring to a party member ID or a Character instance, and index
is an optional number determining where in the party order the party member should be placed.
removePartyMember(chara)
: Removes the specified party member from the party. chara
is either a string referring to a party member ID or a Character instance.
movePartyMember(chara, index)
: Moves the specified party member to the specified party position. chara
is either a string referring to a party member ID or a Character instance, and index
is a number determining where in the party order the party member should be placed.
getSoulPartyMember()
: Returns the party member with the highest soul_priority
value. Used to determine which party member the Soul object should be associated with.
Kristal
is a global table that is used to keep track of data that is more external to gameplay, such as saving and loading data to and from files. This table also has multiple helpful variables and functions available:
Config
: A table containing the values for the player's game settings. Each index is a string referring to the internal name of a setting, and the value is the current value the player has set for it. Kristal's config file can be found at either AppData/Roaming/LOVE/kristal
or AppData/Roaming/kristal
if you want to see all config values possible, but the following is a list of values that may be helpful to check in mods:
debug
: Whether the player has debug mode enabled. Useful for implementing debug features.autoRun
: Whether the player has auto-run enabled.simplifyVFX
: Whether the player has Simplify VFX enabled. This is intended to be used by mods to conditionally simplify graphical effects if something may cause lag on lower-end devices.
Version
: A table of values, with indexes defining the major
, minor
, and patch
values of the current Kristal version.
returnToMenu()
: Exits the mod and returns to the main menu of Kristal. Does not save the game.
saveGame(id, data)
: Saves the game. id
is an optional number defining which save file to save to (defaulting to whichever save file the player is playing on), and data
is an optional table of save data to save to the file (defaulting to what Game:save()
returns).
loadGame(id)
: Loads the data saved to the specified file. id
is a number referring to the save file index that should be loaded (defaulting to the currently loaded save file if undefined).
getModOption(name)
: Returns the value for the specified name
in mod.json if it is defined.
getLibConfig(lib_id, name, merge)
: Returns the value defined in the configuration of the specified library for the specified name
. If the mod's mod.json
defines this value for the library in its config
, it will return that value; otherwise, it will return the library's default value. For table values, if merge
is false, the value defined in mod.json
will completely override the table defined in lib.json
; otherwise, it will merge the two tables together (prioritizing mod.json
values if two values with the same index are defined).
Important Tips
Some facets of Kristal modding are incredibly important to understanding how to make a mod, but they may not be entirely intuitive to learn. It is very important for modders to read this section to be able to understand both the documentation and the code itself.
Global Tables and Classes
The wiki will frequently denote global variables in paragraphs with code blocks, eg. Object
or Utils
. These global variables will likely refer to either a table, a class, or an (extension of) Object. These variables, unless otherwise specified, can be used globally within the code. Each type also has specific rules regarding how they can be used in code:
- Tables, such as
Utils
orAssets
, exist mostly to organize functions or data in an easily accessible manner. To call functions for them, you call the function without passing in the table itself, by using a.
before the function (eg.Utils.round()
). - Classes, such as
Music
orEncounter
, are simple classes that do not extend anything else. They do not contain any functions or variables other than the ones specified by the wiki (though sometimes the wiki will not document variables that are largely intended for internal Kristal use only). The variables themselves cannot be used to reference variables; instead, an "instance" needs to be created, through calling the variable as if it is a function (eg.local mus = Music()
). The variable defined by this can then be used to call functions that will apply to that specific instance. - Objects, such as
Sprite
orBullet
, are special classes containing many functions and variables that can be used. See Objects for more details.
Majority of Kristal's global variables are Objects; hence, it is strongly recommended to familiarize yourself with the Objects page, as it defines all relevant functions and variables.
Consistency across Framerates
Kristal, unlike Deltarune, runs at an uncapped framerate; while Deltarune runs at a constant 30 FPS, Kristal can run at any FPS. In order to maintain consistency between framerates, Kristal implements global DT
and DTMULT
variables. DT
represents the amount of time passed since the last update, and DTMULT
is a value equal to DT*30
, which is used to make objects move at an assumption of 30 FPS, to maintain consistency with Deltarune. When updating anything over a period of time, you should multiply the change values by DTMULT
, so that the mod will behave consistently at any framerate (eg. to move an object horizontally at 4 pixels per frame at 30fps, you would do obj.x = obj.x + 4*DTMULT
).