Modding Custom Battles
Finding The Right Files
Look in limbus_data
at the following folders, primarily the three bolded ones
- "battle-ab"
- "battle-dungeon"
- "battle-exp-dungeon"
- "battle-mirrordungeon"
- "battle-railway-dungeon"
- "battle-story"
- "battle-thread-dungeon"
Getting Started
Once again I recommend grabbing the template Encounter I included here and tinkering with it to accomplish what you want. Generally, we can simply overwrite Railway Battles, Story Battles or Mirror Dungeon battles if we want to
Important Note: This is the one exception that I was talking about when it comes to only working in
custom_limbus_data
and custom_limbus_locale
. If we put a battle file directly to BepInEx/plugins/Lethe/encounter.json
Lethe will recognize it.
-
If you go to the story drive and scroll down the option to play this custom encounter is then available, and it updates whenever any changes is made to the file. This is the preferable way of playing a custom battle.
-
Another note is that it only recognizes this file if it is called
encounter
and placed within the config file, otherwise it won't load.
{
"id": 10532,
"stageLevel": 40,
"stageType": "Abnormality",
"isBatonPassOn": false,
"includeBoss": 1,
"participantInfo": {
"min": 6,
"max": 6
},
"waveList": [
{
"battleMapInfo": {
"mapName": "Cp5_DrillingVessel_Outside",
"mapSize": 28
},
"unitList": [
{
"unitID": 8097,
"unitCount": 2,
"unitLevel": 40
},
{
"unitID": 8098,
"unitCount": 3,
"unitLevel": 40
}
],
"bgmList": [
"Battle_Cp5_MiddleBossBattle"
],
"allyPositionID": 48,
"enemyPositionID": 49
},
{
"battleMapInfo": {
"mapName": "Cp5_DrillingVessel_Outside",
"mapSize": 28
},
"unitList": [
{
"unitID": 80099,
"unitCount": 1,
"unitLevel": 60
},
{
"unitID": 8097,
"unitCount": 1,
"unitLevel": 40
},
{
"unitID": 8098,
"unitCount": 1,
"unitLevel": 40
}
],
"subUnitList": [
{
"unitID": 8097,
"unitCount": 1,
"unitLevel": 40
},
{
"unitID": 8098,
"unitCount": 1,
"unitLevel": 40
}
],
"bgmList": [
"Battle_Cp5_MiddleBossBattle"
],
"allyPositionID": 48,
"enemyPositionID": 47
}
],
"staminaCost": 20,
"recommendedLevel": 40,
"turnLimit": 99,
"stageScriptList": [],
"eventScriptName": [],
"abnormalityEventList": []
}
Explaining The Terms
Usually a battle located within the games' files work similar to a personality.json, being a simple entry in a list. The exception to this, however, is my template (or really, any battles you put in the config file directly) as they only need a single entry.
Close down the information within the waveList
for now, and it will be a lot more readable.
-
id
once again, it's an ID number. This doesn't really matter unless you're overwriting something from vanilla. -
stageLevel
is also a bit of a useless number, it's simply the "Recommended Level" that Limbus gives you. -
stageType
is an important one, as it determines whether your battle is a focused/Abnormality battle (Abnormality) or a regular/human battle (Normal). For this guide, I'm going to assume you're making an abnormality battle, but most of this is still fully applicable to human battles.- Important Note: The only type of enemy that can appear in a "Regular" battle is an "enemy", not an " abnormality".
- Conversely, an, "enemy," cannot appear in an, "Abnormality," battle.
-
isBatonPassOn
is just a boolean value. If enabled, it turns your battle into a Chain Battle (yes that is what they call Chain Battles) -
includeBoss
determines whether the red boss "Proelium Fatale" (or whatever variation that may exist) text appears when starting the stage. If you don't want to use this, then you can either set the boolean value to 0 or delete the entry entirely. -
participantInfo
determines how many sinners you can field in a single battle. Themin
option does nothing, only themax
option does anything. -
staminaCost
is meaningless, so isrecommendedLevel
. However, these are both self-explanatory. -
turnLimit
just determines how many turns someone can take before the fight is guaranteed to result in a loss.
Scripts, Scripts, Scripts
Pretty much every single abnormality will have scripts for their battle, either to make something work that they couldn't in their passive, or to add abnormality events.
Limbus implements events in an incredibly weird way, both requiring you to have the right eventScriptName
(which
determines when a script triggers) and the right event ID itself (which also needs to be written here.)
stageScriptList
In general, this will just be copied and pasted; however, you might be interested in scripts like 403
which adds Railway 4
deployment buffs (and also backup, but I'll get to that), Chapter7SetAllyDefaultMp
which is used for Canto 7
deployment buffs, or the one I mainly want to talk about, being Refill_Abnormality
which enables enemy backup.
I'll get more into why backup is weird, but that requires me to explain how waves work, as that's where the enemies are actually stored.
Waves
A wave contains all the information that most people care about, for example:
{
"battleMapInfo": {
"mapName": "Cp5_DrillingVessel_Outside",
"mapSize": 28
},
"unitList": [
{
"unitID": 8097,
"unitCount": 2,
"unitLevel": 40
},
{
"unitID": 8098,
"unitCount": 3,
"unitLevel": 40
}
],
"bgmList": [
"Battle_Cp5_MiddleBossBattle"
],
"allyPositionID": 48,
"enemyPositionID": 49
}
-
battleMapInfo
is pretty self-explanatory, it's the battle background we fight on.mapSize
is kind of a deprecated but certain fights do specific their map size, so if you're copying and pasting a map you'll probably be taking its map size with it, though it's completely fine to just remove themapsize
-
unitList
contains all the abnormalities that immediately appear when we open up the stage, pointing to an abnormalities ID number, how many copies of that abnormalities appear, and what the level of that abno is. -
Note: If you want to go and find an abnormality go into
limbus_locale
and then theenemyList
(yes enemies and abnormalities are in the same locale file) and search for the specific abnormality ID you want to find. Make sure to copy and paste the abnormality's ID, not their abnormality part's ID. -
bgmList
just determines what music plays. If you only have one entry inside your list that's the only music that will play. If you put in two entries into the list, then what music plays will depend on if you're winning or losing the battle. -
allyPositionID
determines where on the map our allies spawn. Typically specific encounters on specific maps will have their own unique value; however, considering that some maps don't, it is also fine to either keep this at 1, or just remove it. -
enemyPositionID
is the same thing asallyPositionID
but for abnormalities instead. If you're having issues when modifying this value, put it at 2 or keep it at the default.
Enemy Backup is stupid... like, really stupid
If you open up the wave list you might have noticed a little thing called subUnitList
inside the encounter. Now
this is where the game stored stuff like umbrellas (from Drifting Fox) or any enemies that get summoned by a skill
(like Ricardo with Assemble for example).
{
"subUnitList": [
{
"unitID": 8097,
"unitCount": 1,
"unitLevel": 40
},
{
"unitID": 8098,
"unitCount": 1,
"unitLevel": 40
},
{
"unitID": 8097,
"unitCount": 1,
"unitLevel": 40
},
{
"unitID": 8098,
"unitCount": 1,
"unitLevel": 40
}
]
}
But it's also where enemies in backup are pulled from. Whenever an enemy dies, the next entry in the list will come in to replace their spot (Though, if the unitCount of the next entry in the list is higher than 1, it'll summon multiple of them).
It's important to note, however, that backup only functions for one wave. If you have three waves, each of which have subUnit(s), then only the first wave with subUnit(s) will have functioning backup.
Why? I don't know, project moon coding.