

Outward mod that allows you to modify enemy stats (health, damage, resistances, etc.) and change enemy factions using Mods Communicator to send events.
SL.OnSceneLoaded method and evaluates the
BalancingRules and FactionRules. These rules
determine whether your stat modifications should be applied to an enemy.
The mod acts as a central Enemies Balancer that stores simple, abstracted logic and handles more complex game mechanics internally. Its state can be saved or loaded using XML.
When you publish an event, the rules are applied immediately to all matching enemies in the current scene.
Multiple matching rules stack and are all applied to the same enemy.
By default (without specific targeting), rules apply to all regular enemies (non-boss,
non-unique). Use isForBosses, isForBossPawns,
isForStoryBosses, isForUniqueArenaBosses, or
isForUniqueEnemies to target special enemy types.
Other mods can inspect, modify, or react to the BalancingRuleRegistryManager's
dynamic state and see how different mods interact with it.
Firstly, install Mods
Communicator
After that, you can publish and subscribe to Enemies Balancer events.
All events are registered and visible in Mods Communicator's logging system. However, it is still recommended to read the event descriptions below and review the examples. Many event fields are optional and give you extra control, but they are not required.
ValueModifierType determines how the stat modification is applied:
Direct - Sets stat to exact valueScale - Multiplies stat (1.5 = 150%)Add - Adds value to current statDefault: Scale when using dictionary approach
| Stat Name | Description |
|---|---|
| MaxHealth | Maximum health |
| MaxStamina | Maximum stamina |
| MaxMana | Maximum mana |
| HealthRegen | Health regeneration rate |
| StaminaRegen | Stamina regeneration rate |
| ManaRegen | Mana regeneration rate |
| Stat Name | Description |
|---|---|
| PhysicalDamage | Physical damage output |
| EtherealDamage | Ethereal damage output |
| DecayDamage | Decay damage output |
| ElectricDamage | Electric damage output |
| FrostDamage | Frost damage output |
| FireDamage | Fire damage output |
| RawDamage | Raw damage output |
| Stat Name | Description |
|---|---|
| PhysicalResistance | Physical resistance (%) |
| EtherealResistance | Ethereal resistance (%) |
| DecayResistance | Decay resistance (%) |
| ElectricResistance | Electric resistance (%) |
| FrostResistance | Frost resistance (%) |
| FireResistance | Fire resistance (%) |
| Stat Name | Description |
|---|---|
| PhysicalProtection | Physical protection (flat) |
| EtherealProtection | Ethereal protection (flat) |
| DecayProtection | Decay protection (flat) |
| ElectricProtection | Electric protection (flat) |
| FrostProtection | Frost protection (flat) |
| FireProtection | Fire protection (flat) |
| Stat Name | Description |
|---|---|
| ColdProtection | Cold temperature protection |
| HeatProtection | Heat temperature protection |
| ColdRegenRate | Cold regeneration rate |
| HeatRegenRate | Heat regeneration rate |
| CorruptionProtection | Corruption protection |
| Waterproof | Waterproof rating |
| Stat Name | Description |
|---|---|
| Impact | Impact damage |
| ImpactResistance | Impact resistance |
| MovementSpeed | Movement speed |
| AttackSpeed | Attack speed |
| SkillCooldownModifier | Skill cooldown modifier |
| DodgeInvulnerabilityModifier | Dodge invulnerability modifier |
| GlobalStatusResistance | Global status resistance |
Check the parameter tables to understand which values are required. Event descriptions below are intentionally abstract — always refer to the tables for the exact field requirements.
gymmed.enemies_balancer_*
Make sure to publish only after Enemies Balancer has initialized. I
recommend adding it as a dependency in your mod, or publishing it before/after
ResourcesPrefabManager.Load has finished.
AddBalanceRule
Full balancing rule with all targeting options and stat modifications.
Requires at least one targeting parameter and one stat modification.
using OutwardModsCommunicator;
...
var payload = new EventPayload
{
["enemyName"] = "Hyena",
["statModifications"] = new Dictionary<string, float?>
{
{ "MaxHealth", 2.0f },
{ "PhysicalResistance", 15.0f }
},
["modifierType"] = ValueModifierType.Scale,
};
EventBus.Publish("gymmed.enemies_balancer_*", "AddBalanceRule", payload);
using OutwardModsCommunicator;
...
var payload = new EventPayload
{
["enemyName"] = "Hyena",
["statType"] = "MaxHealth",
["value"] = 2.0f,
["modifierType"] = ValueModifierType.Scale,
};
EventBus.Publish("gymmed.enemies_balancer_*", "AddBalanceRule", payload);
AddBalanceRuleByEnemyNameusing OutwardModsCommunicator;
...
var payload = new EventPayload
{
["enemyName"] = "Hyena",
["statModifications"] = new Dictionary<string, float?>
{
{ "MaxHealth", 500f }
},
["modifierType"] = ValueModifierType.Direct,
};
EventBus.Publish("gymmed.enemies_balancer_*", "AddBalanceRuleByEnemyName", payload);
AddBalanceRuleByEnemyIdusing OutwardModsCommunicator;
...
var payload = new EventPayload
{
["enemyId"] = "eCz766tEIEOWfK81om19wg",
["statModifications"] = new Dictionary<string, float?>
{
{ "MaxHealth", 2.0f }
},
["modifierType"] = ValueModifierType.Scale,
};
EventBus.Publish("gymmed.enemies_balancer_*", "AddBalanceRuleByEnemyId", payload);
AddBalanceRuleForBossesusing OutwardModsCommunicator;
...
var payload = new EventPayload
{
["isForBosses"] = true,
["statModifications"] = new Dictionary<string, float?>
{
{ "MaxHealth", 1.5f }
},
["modifierType"] = ValueModifierType.Scale,
};
EventBus.Publish("gymmed.enemies_balancer_*", "AddBalanceRuleForBosses", payload);
AddBalanceRuleForUniquesusing OutwardModsCommunicator;
...
var payload = new EventPayload
{
["isForUniqueEnemies"] = true,
["statModifications"] = new Dictionary<string, float?>
{
{ "MaxHealth", 2.0f }
},
["modifierType"] = ValueModifierType.Scale,
};
EventBus.Publish("gymmed.enemies_balancer_*", "AddBalanceRuleForUniques", payload);
AddBalanceRuleForAreausing OutwardModsCommunicator;
...
var payload = new EventPayload
{
["area"] = AreaManager.AreaEnum.Abrassar,
["statModifications"] = new Dictionary<string, float?>
{
{ "MaxHealth", 1.2f }
},
["modifierType"] = ValueModifierType.Scale,
};
EventBus.Publish("gymmed.enemies_balancer_*", "AddBalanceRuleForArea", payload);
ModifyVitalStatsusing OutwardModsCommunicator;
...
var payload = new EventPayload
{
["maxHealth"] = 1.5f,
["healthRegen"] = 1.2f,
["isForBosses"] = true,
["modifierType"] = ValueModifierType.Scale,
};
EventBus.Publish("gymmed.enemies_balancer_*", "ModifyVitalStats", payload);
ModifyEnvironmentalStatsusing OutwardModsCommunicator;
...
var payload = new EventPayload
{
["coldProtection"] = 30f,
["heatProtection"] = 30f,
["modifierType"] = ValueModifierType.Direct,
};
EventBus.Publish("gymmed.enemies_balancer_*", "ModifyEnvironmentalStats", payload);
ModifyCombatStatsusing OutwardModsCommunicator;
...
var payload = new EventPayload
{
["impact"] = 20f,
["movementSpeed"] = 1.1f,
["modifierType"] = ValueModifierType.Add,
};
EventBus.Publish("gymmed.enemies_balancer_*", "ModifyCombatStats", payload);
LoadBalanceRulesFromXmlfilePath parameter.using OutwardModsCommunicator;
...
var payload = new EventPayload
{
["filePath"] = "assemblyLocation/filePath.xml",
};
EventBus.Publish("gymmed.enemies_balancer_*", "LoadBalanceRulesFromXml", payload);
BalancingRuleRegistryManager balance rules into an XML document.SaveBalanceRulesToXmlfilePath
parameter. If omitted, the file will be stored at:
BepInEx/config/gymmed.Mods_Communicator/Enemies_Balancer/BalanceRules-date.xml
using OutwardModsCommunicator;
...
var payload = new EventPayload
{
//["filePath"] = "",
};
EventBus.Publish("gymmed.enemies_balancer_*", "SaveBalanceRulesToXml", payload);
AddFactionRulenewFaction parameter with the target faction.
using OutwardModsCommunicator;
...
var payload = new EventPayload
{
["enemyName"] = "Hyena",
["newFaction"] = Character.Factions.Player,
};
EventBus.Publish("gymmed.enemies_balancer_*", "AddFactionRule", payload);
using OutwardModsCommunicator;
...
var payload = new EventPayload
{
["isForBosses"] = true,
["newFaction"] = Character.Factions.Player,
};
EventBus.Publish("gymmed.enemies_balancer_*", "AddFactionRule", payload);
Character.Factions.NONE - No factionCharacter.Factions.Player - Player factionCharacter.Factions.Bandits - BanditsCharacter.Factions.Mercs - MercenariesCharacter.Factions.Tuanosaurs - TuanosaursCharacter.Factions.Deer - Deer/WildlifeCharacter.Factions.Hounds - HoundsCharacter.Factions.Merchants - MerchantsCharacter.Factions.Golden - Golden OrderCharacter.Factions.CorruptionSpirit - Corruption Spiritsgymmed.enemies_balancerBalanceRuleAppendedusing OutwardModsCommunicator;
...
public awake()
{
...
EventBus.Subscribe(
"gymmed.enemies_balancer",
"BalanceRuleAppended",
YourMethod
);
}
...
public static void YourMethod(EventPayload payload)
{
if (payload == null) return;
string balanceRuleId = payload.Get<string>("balanceRuleId", null);
// Your code...
}
BalanceRuleRemovedusing OutwardModsCommunicator;
...
public awake()
{
...
EventBus.Subscribe(
"gymmed.enemies_balancer",
"BalanceRuleRemoved",
YourMethod
);
}
...
public static void YourMethod(EventPayload payload)
{
if (payload == null) return;
string balanceRuleId = payload.Get<string>("balanceRuleId", null);
// Your code...
}
FactionRuleAppendedusing OutwardModsCommunicator;
...
public awake()
{
...
EventBus.Subscribe(
"gymmed.enemies_balancer",
"FactionRuleAppended",
YourMethod
);
}
...
public static void YourMethod(EventPayload payload)
{
if (payload == null) return;
string factionRuleId = payload.Get<string>("factionRuleId", null);
// Your code...
}
FactionRuleRemovedusing OutwardModsCommunicator;
...
public awake()
{
...
EventBus.Subscribe(
"gymmed.enemies_balancer",
"FactionRuleRemoved",
YourMethod
);
}
...
public static void YourMethod(EventPayload payload)
{
if (payload == null) return;
string factionRuleId = payload.Get<string>("factionRuleId", null);
// Your code...
}
enemyName and that enemy is unique
make sure to provide one of isForBosses,
isForBossPawns, isForStoryBosses,
isForUniqueArenaBosses, isForUniqueEnemies parameters.UID + _ + Area.GetName().Trim().Replace(' ', '_')
because even UID repeat(is not truly unique) in different scenes and adding
location prevents collision.isForBosses parameter.
isForStoryBosses. They are all compared by UID. Djinn has dummy data and is not tested, but he never enters the state to be looted?
isForBossPawns. They are all compared by UID.
isForUniqueArenaBosses. They are all compared by UID.
isForUniqueEnemies. They are all compared by UID.
OutwardEnemiesBalancer.dll to BepInEx/plugins/OutwardEnemiesBalancer/XML configuration file is NOT loaded automatically. You must publish the LoadBalanceRulesFromXml event via Mods Communicator to load rules.
SaveBalanceRulesToXml event
3. On next session, load the XML using LoadBalanceRulesFromXml event
This approach ensures your XML syntax is always correct.
XML configuration file is located at:
BepInEx/config/gymmed.Mods_Communicator/Enemies_Balancer/BalanceRules.xml
<?xml version="1.0" encoding="utf-8"?>
<BalanceRules>
<Rule>
<Id>unique-rule-id</Id>
<EnemyName>Hyena</EnemyName>
<StatModifications>
<Stat StatName="MaxHealth" Value="2.0" ModifierType="Scale"/>
<Stat StatName="PhysicalResistance" Value="15"/>
</StatModifications>
</Rule>
<FactionRule>
<Id>faction-rule-id</Id>
<EnemyName>Hyena</EnemyName>
<NewFaction>Player</NewFaction>
</FactionRule>
</BalanceRules>