Modding Cookbook
Make your mod
[edit]- Get hands on the project source code, hopefully via the modkit download on itch.io or another channel from Wickworks.
- Open it in the Godot editor (4.7 currently)
- Go to Project Settings → Plugins and enable Mod Loader Dev Tool (it'll be the only thing that's unchecked)
- There's now a tab call "Mod Tool" at the top-center of the window. Press the "Create new Mod" button, fill out the fields, and press Create.
- See Mod Types for the difference between an Asset Pack, Content Pack, and Substantial mod. You can change what type it is later by adding scripts or toggling requires_restart.
- Fill out any information you'd like to provide in manifest fields, and press "Save to manifest.json" in the top-right.
- In the FileSystem window, you should see folder with the name of your mod under unpacked/, with a manifest.json and a mod_main.gd file.
- (optional) Update thumbnail.png with the image you want to represent your mod.
Replace existing resources and scenes
[edit](.png, .svg, .tres, .tscn, .json)
- Navigate to the file you want to replace in the FileSystem window.
- Right-click it and select "ModTool: Create Asset Override" from the context menu.
- This will copy the resource to your mod's folder under res/ + the original path.
- Modify the resource in your res/ folder as desired.
It will be automatically swapped in to the game's virtual filesystem at the vanilla path when the mod is activated by your mod_main.gd's add_overwrite_extend_mod_resources, powered by Resource.take_over_path.
Warning: If multiple mods replace the same resource, only the last one will take effect.
Add new resources and scenes
[edit](.png, .svg, .tres, .tscn, .json)
Add them to your mod's res/ directory in a folder mirroring where they'd be in the base game data. For example, if you wanted to add a new available sprite for the napoleon at:
res://assets/frames/ha/mf_napoleon/
You'd add your image at:
res://unpacked/Mod-Name/res/assets/frames/ha/mf_napoleon/
It will be automatically added to the game's virtual filesystem at the vanilla path when the mod is activated by your mod_main.gd's add_overwrite_extend_mod_resources, powered by Resource.take_over_path. If you're making an asset pack, the default mod_main that will be used handles this instead.
Warning: If multiple mods add a new resource with the same path + name, only the last one will take effect.
Remove existing resources
[edit](.png, .svg, .tres, .tscn, .json)
TODO: some way to mark a resource as something to remove. An extra extension?
Modify existing resource
[edit](.tres)
- Navigate to the resource you want to replace in the FileSystem window.
- Right-click it and select "ModTool: Create Resource Extension" from the context menu.
- This will create a new script in your mod's folder under res/ + the original path, with the extension .tres_ext.gd
- Modify the scene through the generated script's modify_resource function, as desired.
For example, to add the Barbarossa as an alt frame of the Napoleon license:
func modify_resource(resource:Resource) -> Resource:
var license:UnlockTree = resource
var barb := load('res://content/frames/ha/mf_barbarossa/mf_barbarossa.tres')
license.rank_2.granted_frames.append(barb)
return license
Note: modify_resource is run after resources are moved in the virtual filesystem from your mod's res/ directory into their res:// locations, so I *believe* that if you load new resources from your mod you should use their res://content/... paths instead of the unpacked location.
This will work even if two different mods extend the same resource.
Modify existing scenes
[edit](.tscn)
- Navigate to the scene you want to replace in the FileSystem window.
- Right-click it and select "ModTool: Create Scene Extension" from the context menu.
- This will create a new script in your mod's folder under res/ + the original path, with the extension .tscn_ext.gd
- Modify the scene through the generated script's modify_scene function, as desired.
Note: if you add new nodes to the modified scene, you MUST set the owner of that child to the scene after adding it as a child, e.g.
func modify_scene(scene:Node) -> Node:
# Modify an existing node.
var label: Label = scene_instance.get_node("Maybe/Some/NodeHere")
label.text += " modified"
# Add a new node.
var new_node: Node = Node.new()
scene_instance.add_child(new_node)
new_node.owner = scene # Must come after add_child for all added nodes.
return scene_instance
This will work even if two different mods extend the same scene.
Extend existing scripts
[edit]https://wiki.godotmodding.com/guides/modding/script_extensions/
- Navigate to the script you want to replace in the FileSystem window.
- Right-click it and select "ModTool: Create Script Extension" from the context menu.
- This will create a new script to your mod's folder under res/ + the original path, extending the vanilla script, with the extension .gd_ext.gd
- Modify the extending script in your res/ folder as desired.
This will work even if two different mods extend the same script, via add_overwrite_extend_mod_resources, powered by ModLoaderMod.install_script_extension.
If you change an autoload, that autoload will be reloaded after all mods are applied.
WARNING: Due to a bug in Godot, we can't extend scripts with a class_name. See Hooks and class name scripts for details.
Replace existing scripts
[edit]- Navigate to the script you want to replace in the FileSystem window.
- Right-click it and select "ModTool: Create Asset Override" from the context menu.
- This will copy the script to your mod's folder under res/ + the original path.
- Modify the replacing script in your res/ folder as desired.
This will likely conflict with any other mods that also attempt to replace or extend this script. However, this is the only way to change static vars. Godot appears to cache consts at a script-path level, so as far as I can tell there's no way to change a const on an object/script after it's been declared, even by completely replacing the script.
You should very probably include definitions for everything the original script did, or you'll crash as soon as something asks for a now-missing value (unless this replacement is also extending something that covers those functions).
If you change an autoload, that autoload will be reloaded after all mods are applied.
Localizations & text
[edit]Generating a mod from a template will create a localization/localizations.csv file in your mod folder. These key/value pairs are used to figure out what text to show in the game for kits/frames/everything. You can see the shipped assets/localization/localizations.csv file for examples of all the base game text.
You can add your own keys in a likewise format in your own file — I recommend an external spreadsheet editor over godot's text editor to automatically handle internal commas and quotation marks. Alternatively, there is a second tab next to the Manifest Editor in the Mod Tool section called "Localization Editor". I haven't thoroughly tested it, but it gives some basic controls for adding new key/values in the expected format and a button to regenerate localizations (possibly necessary to see the changes show up).
Add custom sound fx
[edit]https://www.fmod.com/docs/2.01/studio/supporting-downloadable-and-user-generated-content.html
I haven't actually tested it, but if you open the Lancer Tactics FMOD project and export your own bank as described above, you should be able to add your .bank file anywhere in your mod's res/ directory and it'll be automatically picked up and loaded in (and subsequently unloaded if the mod is disabled).
New banks must be named by either prefixing or suffixing the bank they're adding onto. For example, if I'm making a new music bank, since the baseline bank is called "music.bank" mine has to look like "my_music.bank" or "music_thunderdome.bank". This is because the FMOD addon crashes if we ever try to play an event not in the banks so we have to cache what events are available for each category.
If you feel motivated to test this, let me know how it goes!
Modify how COMP/CON imports pilots
[edit]Lancer Tactics is strict about what sorts of prefixes it looks for in resources. If you're working with a more loosey-goosey ID that was made for COMP/CON (such as the practice of adding prefixes based on the supplement), you can make a mod that tells LT how to map the C/C ID.
- Extend the compcon_importer script:
res://ui/page/pilot_roster/compcon_importer.gd - Extend the get_compcon_id_remap function.
- Call super.get_compcon_id_remap() within it, then add whatever C/C -> LT IDs to the dictionary that you'd like.
Player configuration
[edit]https://wiki.godotmodding.com/guides/modding/config_json/
You can allow the player to configure your mod through inputs in the mod manager, ie, toggle booleans, pick from dropdowns, and enter text+numbers that your mod can then consume.
Define the config as described in the above documentation inside of your manifest.json file, and get the player's current values with ModLoaderConfig.get_current_config("your_mod_id").
Lancer Tactics currently only supports numbers, strings, and boolean inputs. Let me know if you need more than that!
Dependencies
[edit]https://wiki.godotmodding.com/guides/modding/using_other_mods/#dependencies-and-load-order
If your mod requires another mod to function, you need to do two things:
- Add the other mod's folder ID (in the Namespace-ModName format) in the "Dependencies" section of your manifest. This will tell the mod loader to load their mod first.
- Tell mod.io about the dependency by going to
mod.io/g/lancer-tactics/m/[YOUR MOD SLUG HERE]/admin/settings#dependenciesand selecting the other mod from the search bar.
Export + upload your mod to share
[edit]Mods are loaded by player's games by looking for .zip files in their user mods/ folder. In order to get them set up with the mod, we need to create that zip and get it into their folder. There are three options:
Manually
[edit]In the bottom-center of the Mod Tool tab, there's an "Export as .zip" button. Pressing it will pack the current mod into a zip file at the designated location.
You can drag the zipped mod to your own user data mods/ folder, or send it to your friends so they can do so for their own games. No need to get a third party involved.
Mod.io, via the Mod Tool
[edit]In the bottom-right of the Mod Tool tab, there's a "Upload" section.
- Authenticate with mod.io from the Omninet page in the running game, if you haven't already. This will save a token to your settings.cfg
- Verify that it shows you as logged in as the appropriate email, and press "Check status" button to verify your connection to mod.io.
- Press "Upload to mod.io" and confirm.
You should now be able to see and manage your mod online at https://mod.io/content#mods
Mod.io, via the browser
[edit]You can upload a mod from outside the Mod Tool by uploading the exported .zip file you made in the manual option above. Mod.io's flow for doing so is fairly intuitive, but to be loaded by the game you must include this json in the Metadata section for the mod, filling in values as appropriate:
{
"game_version":"X.X.X",
"mod_id":"YourNamespace-YourMod",
"revision":1,
"version":"1.0.0"
}
- game_version: The version of Lancer Tactics you expect this mod to work on.
- mod_id: The ID of your mod, in the Namespace-Modname format (matches its folder when unpacked)
- revision: Doesn't matter too much; just for information. Incremented by the game each time it uploads something new.
- version: The semantic versioning of your mod.