Due to data-driven structure with exposed config files, Phantom Brigade has always been fairly easy to modify. By modifying any file in the Configs folder, you can create a wide range of mods (altering scenarios, rebalancing equipment, adding new units or localization, etc.). However, this approach to modding has some issues and limitations:
simulation.yaml
, no other mod could modify anything reliant on that fileThis new modding system is an attempt to solve all these issues. It closely follows modding systems in popular moddable Unity games. To start, you need to do the following:
mods.yaml
file and place it into C:\Users\[USER]\AppData\Local\PhantomBrigade\Settings
folder. See examples of file content above.enabled
equals true
loadFromApplicationPath
equals false
list
is empty or matches existing folders in required format (more on that below)metadata.yaml
file that provides some essential information about content of the mod to the game, allowing for loading it and for drawing of mod management UIa.b.c
, for example 0.14.2
, 0.16.0
etc. - do not include any suffixes like -b3103E
you see in build info watermark)mods.yaml
has enabled
property set to true
, new main menu option will appear in the game - Mods menumetadata.yaml
in a given folderlist
property in mods.yaml
- the UI is just an interface modifying that property in the config.mods.yaml
from disk and update the UI accordingly.mods.yaml
to disk.Warning! The game already applies mods on startup (based on config state at that point). The option to “Apply Mods” available in the menu above is not the recommended way to applying changes in mod configuration and could cause unforeseen issues. If you change something in the mod menu, e.g. disable, enable or reorder mods, we recommend to hit “Save Config” and restart the game to see the effects. Furthermore, there is no support for loading library mods (code injection mods) more than once per session, as it is impossible to undo unpredictable changes they might make to the runtime - any such mod will be skipped if the game already loaded a code mod during the current session. However, this option can be useful for quickly checking changes while developing config mods and in other such cases.
Mod directories listed in list
property in mods.yaml
(included and activated via mod menu or manual editing of this config) will be analyzed by the game on startup. Based on information provided in metadata.yaml
, the game will check the mod for compatibility with current version and will traverse contents of its folder, attempting to load it. See subsequent sections for details on supported mod content types.
Mods can include several so-called content types. These are represented by subfolders in the mod folder and their presence is declared in metadata.yaml
. The set of supported content types will be expanded over time to include more (like localizations), but for now, the four following types are supported.
includesConfigOverrides
ConfigOverrides/
This is the simplest possible type of mod content, quite similar to editing Configs folder directly.
includesConfigEdits
ConfigEdits/
This content type allows for much more fine grained modifications resistant to multiple mods modifying the same file, file changing shape when game is updated etc.
Mods/[ModName]/ConfigEdits
and put your .yaml matching path/name of existingsimulation.yaml
contains hundreds of parameters, and many different mods will definitely want to modify it, whether it's to change allowed squad size of physics settings or loot generation multipliers.simulation.yaml
file modifying a line called squadSize
and another can have simulation.yaml
describing changes to couple other fields - and every change will be seamlessly merged. It's as if every single collection entry or field in our configs is a separate .yaml or a separate folder, allowing for very targeted changes.The format of these edit configs is relatively simple.
removed
to true to remove targeted config from the game (for instance, if your mod replaces parts of the arsenal and needs to remove built-in weapons from the game)edits
field to perform fine grained changes in targeted config-
, just like any serialized List<T>
in other configs.- 'hidden: true'
. This enables you to use reserved characters anywhere within the line.:
- left side is a path, and right side is a value
squadSize
to a value of 3
you can just type: - ‘squadSize: 3’
..
- for instance, here is a modification to a field nested 1 level deep: - 'customPlayerSlot.spawnTagsUsed: true'
.- ‘states.no_hostiles.visible: false’
(no_hostiles
is a dictionary key here); unitChecks.0.value: 1
(0
is a list index here!)edits
field is not a dictionary. For instance, it's totally fine to modify tags field once (e.g. removing something) and then modify it again (e.g. to add something)string
, bool
, int
, float
, Vector2
, Vector3
, Vector4
true
int.TryParse
and float.TryParse
. An example would be 6
, 8.62
etc.(3.1, 4, 7.2)
etc.!d
(for “default”). Use it like so: - 'customExitBehaviour: !d'
- ‘field: 7’
- overwrite field named field
with value of 7List<T>
, the following operations are supported:- ‘list.4: 7’
- overwrite index 4 in list named list
with value of 7- ‘list.0: 7 !+’
- insert value of 7 to the very beginning of the list- 'list.8: 7 !+'
- insert value of 7 to index 8 (if list has more entries) or just to the very end of the list (if list has fewer entries than 8)- 'list.3: !-
- remove entry at index 3HashSet<string>
, the following operations are supported:- 'tags: context_facility !+'
- attempt to insert the string context_facility
to the hash set tags
- 'tags: context_fort !-'
- attempt to remove the string context_fort
from the hash setDictionary<T>
, the following operations are supported:- ‘dictionary.existing_key: true’
- overwrite value at key existing_key
in dictionary named dictionary
with value of true
- ‘dictionary.new_key: true !+’
- attempt to insert value of true
with new key new_key
into the dictionary- 'dictionary.existing_key: !-
- attempt to remove the entry with key existing_key
includesLibraries
Libraries/
This is a very powerful type of mod content enabled by C#/Unity's reliance on editable interpreted language (IL): ability to load libraries that can patch game logic and execute new code.
Mods/[ModName]/Libraries/MyLibrary.dll
ModLink
- if found, it's instantiated, filled with data, and has a special entry method invokedModLink
provides an entry point to modders: you can declare an override to OnLoad
method in it, executing arbitrary code; you get access to full mod metadata (so you know where you are, what mod you're in, what other mods are loaded) and access to the Harmony patcher object, allowing you to modify code in PB.General setup of a new library goes as follows:
0Harmony.dll
downloaded from 2.1 release on Harmony website (future releases of Phantom Brigade might include it directly in the Managed folder above)Assembly-CSharp.dll
, Assembly-CSharp-firstpass.dll
System.dll
and UnityEngine.dll
from your install folder, e.g. PB/PhantomBrigade_Data/Managed/
Entitas.dll
from your install folderModLink.cs
fileid
field in your metadata.yaml
, e.g. ModTest
ModLink
, e.g. public class ModTestLink : ModLink
OnLoad
with this signature: public override void OnLoad (Harmony harmonyInstance)
metadata
field that can tell you what folder you're working with, what mod is this library loaded in, etc; access ModManager.loadedMods
to interact with other mods, access any game classes, perform custom patching procedures using Harmony
object passed into the method etc.Patches
[HarmonyPatch (typeof (PhantomBrigade.GameController), MethodType.Normal), HarmonyPatch ("Initialize")]
OnLoad
override and comment out base.OnLoad (harmonyInstance)
call in it.includesTextures
Textures/
This content type allows you to overwrite existing textures or extend the set of available textures:
image
field in steps
of DataContainerOverworldEvent
(in Configs/DataDecomposed/Overworld/Events)image
field of DataContainerOverworldEntityBlueprint
in (Configs/DataDecomposed/Overworld/Entities)includesLocalizationEdits
LocalizationEdits/
This type of content payload enables you to add text to the localization database. It is rare (and will be even rare in the future) that data files under Confgs/Data
and Configs/DataDecomposed
contain any player facing text, since storing text that way prevents the possibility of localization and easy text modification. Most of the text in the game will eventually be stored in localization database, under Configs/Text
, split into versions for different languages. This poses a problem to mods, however - if you add a new item, and the text for it is not stored in the main data config, how do you add player facing text for it?
For an example, if you're adding a new thruster subsystem, you definitely want it to have a name and a description. But name and description do not come from subsystem config, since the game supports their localization - the game takes the subsystem name like internal_aux_thruster_mod
, decides on localization keys based on it, like internal_aux_thruster_mod__name
and internal_aux_thruster_mod__text
and tries to fetch text using these keys from the appropriate text sector of the loaded language (e.g. Configs/Text/English/Sectors/equipment_subsystems.yaml
. Without ability to mod these text sectors, your new item can't have a user-facing name/description.
How this type of content can be used is highlighted on the screenshot above, but just in case, here is a step by step overview:
ConfigOverrides\DataDecomposed\Equipment\Subsystems
called internal_aux_thruster_mod.yaml. Such a file doesn't exist in the base game - it's a new item, not a modified one. The game can't show any text for it.Configs\Text\English\Sectors
in the game install folder for name of an existing subsystem, like internal_aux_thruster_hotrod, to see which sector file such text is stored in and how the text keys are structured.Internally, localization edits are applied every time the game loads language database and a registered per language key, which means that:
MyMod\LocalizationEdits\English\equipment_subsystems.yaml
file to add text to players using English but you can also add a second file at MyMod\LocalizationEdits\Japanese\equipment_subsystems.yaml
to add support for Japanese localization. Or someone can make a new mod extending your mod, containing localization for a first mod for a specific language.Here is an example: this item doesn't exist in base game and was added by a mod, and the game properly displays custom name and description depicted in file screenshots in previous image:
includesLocalizations
Localizations/
This content type allows you to add entire new localizations to the game. General principle is as follows:
Configs/Text
into MyMod/Localizations, rename it to the language you are localizing the game into, e.g. MyMod/Localizaions/French
Some additional notes:
data.toggle-localization-debug
, which will render all localized text in a different color and randomize its contentKeep an eye out on game log - it will contain plenty of information from mod manager in case anything goes wrong, or confirmation of successful loading if everything goes well. Look for messages towards very beginning of the log, prefixed with “Mod Manager”. To look at the log, press Shift+F11 or open the log file in application data folder.
It might also be useful to enable developerMode
property in debug.yaml
in AppData\Local\PhantomBrigade\Settings to get access to additional options, debug information and console commands while developing a mod.
Note that no bug reports or crashes from a game in developer mode or with active mods will be processed by BYG.