using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using OutwardEnemiesBalancer.Balancing;
using OutwardEnemiesBalancer.Balancing.Internal;
using OutwardEnemiesBalancer.Balancing.Serializable;
using OutwardEnemiesBalancer.Events;
using OutwardEnemiesBalancer.Managers;
using OutwardEnemiesBalancer.Utility.Data;
using OutwardEnemiesBalancer.Utility.Enums;
using OutwardEnemiesBalancer.Utility.Extensions;
using OutwardEnemiesBalancer.Utility.Helpers.Static;
using OutwardModsCommunicator.EventBus;
using OutwardModsCommunicator.Managers;
using SideLoader;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("OutwardEnemiesBalancer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OutwardEnemiesBalancer")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("c5450fe0-edcf-483f-b9ea-4b1ef9d36da7")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace OutwardEnemiesBalancer
{
public class BalancingRule
{
public string id;
public string enemyID;
public string enemyName;
public AreaFamily areaFamily;
public AreaEnum? area;
public Factions? faction;
public bool isBoss;
public bool isBossPawn;
public bool isStoryBoss;
public bool isUniqueArenaBoss;
public bool isUniqueEnemy;
public List<string> exceptNames;
public List<string> exceptIds;
public Dictionary<string, float?> statModifications;
public ValueModifierType modifierType = ValueModifierType.Scale;
public BalancingRule(string id = null)
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
if (string.IsNullOrEmpty(id))
{
UID val = UID.Generate();
this.id = ((UID)(ref val)).Value;
}
else
{
this.id = id;
}
statModifications = new Dictionary<string, float?>();
}
public bool Matches(Character character)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)character == (Object)null)
{
return false;
}
UID uID;
if (!string.IsNullOrEmpty(enemyID))
{
uID = character.UID;
return ((UID)(ref uID)).Value == enemyID;
}
if (!string.IsNullOrEmpty(enemyName) && !character.Name.Equals(enemyName, StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (exceptIds != null)
{
List<string> list = exceptIds;
uID = character.UID;
if (list.Contains(((UID)(ref uID)).Value))
{
return false;
}
}
if (exceptNames != null && exceptNames.Contains(character.Name))
{
return false;
}
if (faction.HasValue && character.Faction != faction.Value)
{
return false;
}
if (area.HasValue)
{
Area currentArea = AreaManager.Instance.CurrentArea;
Area val = AreaManager.Instance.GetArea(area.Value);
if (currentArea == null || val == null || currentArea.ID != val.ID)
{
return false;
}
}
if (areaFamily != null && !AreaFamiliesHelpers.DoesAreaFamilyMatch(areaFamily))
{
return false;
}
if (isBoss)
{
if (!BossRegistryManager.Instance.IsBoss(character))
{
return false;
}
return true;
}
if (BossRegistryManager.Instance.IsBoss(character))
{
return false;
}
if (isUniqueArenaBoss)
{
if (!UniqueArenaBossesHelper.Enemies.TryGetEnum(character, out var _))
{
return false;
}
}
else if (BossRegistryManager.Instance.IsBossOfCategory(character, BossCategories.Arena))
{
return false;
}
if (isStoryBoss)
{
if (!StoryBossesHelper.Enemies.TryGetEnum(character, out var _))
{
return false;
}
}
else if (BossRegistryManager.Instance.IsBossOfCategory(character, BossCategories.Story))
{
return false;
}
if (isBossPawn)
{
if (!BossPawnsHelper.Enemies.TryGetEnum(character, out var _))
{
return false;
}
}
else if (BossRegistryManager.Instance.IsBossOfCategory(character, BossCategories.Pawn))
{
return false;
}
UniqueEnemies result5;
if (isUniqueEnemy)
{
if (!UniqueEnemiesHelper.Enemies.TryGetEnum(character, out var _))
{
return false;
}
}
else if (UniqueEnemiesHelper.Enemies.TryGetEnum(character, out result5))
{
return false;
}
return true;
}
}
public class FactionRule
{
public string id;
public string enemyID;
public string enemyName;
public AreaFamily areaFamily;
public AreaEnum? area;
public Factions? targetFaction;
public Factions newFaction;
public bool isBoss;
public bool isBossPawn;
public bool isStoryBoss;
public bool isUniqueArenaBoss;
public bool isUniqueEnemy;
public List<string> exceptNames;
public List<string> exceptIds;
public FactionRule(string id = null)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
if (string.IsNullOrEmpty(id))
{
UID val = UID.Generate();
this.id = ((UID)(ref val)).Value;
}
else
{
this.id = id;
}
exceptNames = new List<string>();
exceptIds = new List<string>();
}
public bool Matches(Character character)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)character == (Object)null)
{
return false;
}
UID uID;
if (!string.IsNullOrEmpty(enemyID))
{
uID = character.UID;
return ((UID)(ref uID)).Value == enemyID;
}
if (!string.IsNullOrEmpty(enemyName) && !character.Name.Equals(enemyName, StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (exceptIds != null)
{
List<string> list = exceptIds;
uID = character.UID;
if (list.Contains(((UID)(ref uID)).Value))
{
return false;
}
}
if (exceptNames != null && exceptNames.Contains(character.Name))
{
return false;
}
if (targetFaction.HasValue && character.Faction != targetFaction.Value)
{
return false;
}
if (area.HasValue)
{
Area currentArea = AreaManager.Instance.CurrentArea;
Area val = AreaManager.Instance.GetArea(area.Value);
if (currentArea == null || val == null || currentArea.ID != val.ID)
{
return false;
}
}
if (areaFamily != null && !AreaFamiliesHelpers.DoesAreaFamilyMatch(areaFamily))
{
return false;
}
if (isBoss)
{
if (!BossRegistryManager.Instance.IsBoss(character))
{
return false;
}
return true;
}
if (BossRegistryManager.Instance.IsBoss(character))
{
return false;
}
if (isUniqueArenaBoss)
{
if (!UniqueArenaBossesHelper.Enemies.TryGetEnum(character, out var _))
{
return false;
}
}
else if (BossRegistryManager.Instance.IsBossOfCategory(character, BossCategories.Arena))
{
return false;
}
if (isStoryBoss)
{
if (!StoryBossesHelper.Enemies.TryGetEnum(character, out var _))
{
return false;
}
}
else if (BossRegistryManager.Instance.IsBossOfCategory(character, BossCategories.Story))
{
return false;
}
if (isBossPawn)
{
if (!BossPawnsHelper.Enemies.TryGetEnum(character, out var _))
{
return false;
}
}
else if (BossRegistryManager.Instance.IsBossOfCategory(character, BossCategories.Pawn))
{
return false;
}
UniqueEnemies result5;
if (isUniqueEnemy)
{
if (!UniqueEnemiesHelper.Enemies.TryGetEnum(character, out var _))
{
return false;
}
}
else if (UniqueEnemiesHelper.Enemies.TryGetEnum(character, out result5))
{
return false;
}
return true;
}
}
[BepInPlugin("gymmed.enemies_balancer", "Enemies Balancer", "0.0.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class OutwardEnemiesBalancer : BaseUnityPlugin
{
[HarmonyPatch(typeof(ResourcesPrefabManager), "Load")]
public class ResourcesPrefabManager_Load
{
private static void Postfix(ResourcesPrefabManager __instance)
{
BalancingRulesSerializer.Instance.LoadPlayerBalanceRules();
BalancingRulesSerializer.Instance.LoadFactionRules(PathsManager.DefaultBalanceRulesPath);
}
}
public const string GUID = "gymmed.enemies_balancer";
public const string NAME = "Enemies Balancer";
public const string VERSION = "0.0.1";
public const string EVENTS_LISTENER_GUID = "gymmed.enemies_balancer_*";
public static string prefix = "[Enemies-Balancer]";
internal static ManualLogSource Log;
internal void Awake()
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
Log = ((BaseUnityPlugin)this).Logger;
LogMessage("Hello world from Enemies Balancer 0.0.1!");
new Harmony("gymmed.enemies_balancer").PatchAll();
PathsManager.Initialize();
EventBusRegister.RegisterEvents();
EventBusSubscriber.AddSubscribers();
SL.OnSceneLoaded += OnSceneLoaded;
}
private void OnSceneLoaded()
{
try
{
CharacterBalancerManager.Instance.ApplyBalancingRules();
FactionBalancerManager.Instance.ApplyFactionRules();
}
catch (Exception ex)
{
LogMessage("Error applying rules: " + ex.Message);
}
}
internal void Update()
{
}
public static void LogMessage(string message)
{
Log.LogMessage((object)(prefix + " " + message));
}
public static void LogSL(string message)
{
SL.Log(prefix + " " + message);
}
public static string GetProjectLocation()
{
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
}
}
}
namespace OutwardEnemiesBalancer.Utility.Helpers
{
public abstract class EnemyEnumHelperBase<T> where T : Enum
{
protected abstract Dictionary<T, string> EnemyNames { get; }
public T GetEnumFromCharacter(Character character)
{
if ((Object)(object)character == (Object)null)
{
return default(T);
}
foreach (KeyValuePair<T, string> enemyName in EnemyNames)
{
if (character.Name.Equals(enemyName.Value, StringComparison.OrdinalIgnoreCase))
{
return enemyName.Key;
}
}
return default(T);
}
public string GetEnemyName(T enemy)
{
if (!EnemyNames.TryGetValue(enemy, out var value))
{
return enemy.ToString();
}
return value;
}
}
}
namespace OutwardEnemiesBalancer.Utility.Helpers.Static
{
public static class AreaFamiliesHelpers
{
public static AreaFamily GetAreaFamilyByKeyWord(string keyword)
{
AreaFamily[] areaFamilies = AreaManager.AreaFamilies;
foreach (AreaFamily val in areaFamilies)
{
string[] familyKeywords = val.FamilyKeywords;
for (int j = 0; j < familyKeywords.Length; j++)
{
if (familyKeywords[j].Equals(keyword, StringComparison.OrdinalIgnoreCase))
{
return val;
}
}
}
return null;
}
public static AreaFamily GetAreaFamilyByName(string name)
{
AreaFamily[] areaFamilies = AreaManager.AreaFamilies;
foreach (AreaFamily val in areaFamilies)
{
if (val.FamilyName.Equals(name, StringComparison.OrdinalIgnoreCase))
{
return val;
}
}
return null;
}
public static bool DoesAreaFamilyMatch(AreaFamily family)
{
AreaFamily activeAreaFamily = GetActiveAreaFamily();
if (activeAreaFamily == null || family == null)
{
return false;
}
if (activeAreaFamily.FamilyName == family.FamilyName)
{
return true;
}
return false;
}
public static AreaFamily GetActiveAreaFamily()
{
AreaFamily[] areaFamilies = AreaManager.AreaFamilies;
foreach (AreaFamily val in areaFamilies)
{
string[] familyKeywords = val.FamilyKeywords;
foreach (string value in familyKeywords)
{
if (SceneManagerHelper.ActiveSceneName.Contains(value))
{
return val;
}
}
}
return null;
}
}
public static class AreaHelpers
{
public static bool IsAreaInAreaFamily(AreaEnum area)
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
AreaFamily[] areaFamilies = AreaManager.AreaFamilies;
for (int i = 0; i < areaFamilies.Length; i++)
{
string[] familyKeywords = areaFamilies[i].FamilyKeywords;
foreach (string value in familyKeywords)
{
if (AreaManager.Instance.GetArea(area).SceneName.Contains(value))
{
return true;
}
}
}
return false;
}
public static AreaEnum? GetAreaEnumFromAreaDefaultName(string areaName)
{
Area[] areas = AreaManager.Instance.Areas;
foreach (Area val in areas)
{
if (val.DefaultName == areaName)
{
return (AreaEnum)val.ID;
}
}
return null;
}
public static AreaEnum? GetAreaEnumFromAreaName(string areaName)
{
Area[] areas = AreaManager.Instance.Areas;
foreach (Area val in areas)
{
if (val.GetName() == areaName)
{
return (AreaEnum)val.ID;
}
}
return null;
}
public static AreaEnum GetAreaEnumFromArea(Area area)
{
return (AreaEnum)area.ID;
}
public static List<AreaEnum> GetAreasFromEnumDictionary<T>(Dictionary<T, string> locations) where T : Enum
{
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
List<AreaEnum> list = new List<AreaEnum>();
foreach (KeyValuePair<T, string> location in locations)
{
Area[] areas = AreaManager.Instance.Areas;
foreach (Area val in areas)
{
if (val.GetName() == location.Value)
{
list.Add(GetAreaEnumFromArea(val));
}
}
}
return list;
}
public static HashSet<AreaEnum> GetUniqueAreasFromEnumDictionary<T>(Dictionary<T, string> locations) where T : Enum
{
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
HashSet<AreaEnum> hashSet = new HashSet<AreaEnum>();
foreach (KeyValuePair<T, string> location in locations)
{
Area[] areas = AreaManager.Instance.Areas;
foreach (Area val in areas)
{
if (val.GetName() == location.Value)
{
hashSet.Add(GetAreaEnumFromArea(val));
}
}
}
return hashSet;
}
}
public static class BalancingRuleHelpers
{
public static bool TryToFillRuleWithId(BalancingRule rule, EventPayload payload)
{
string text = payload.Get<string>(EnemyBalanceParamsHelper.Get(EnemyBalanceParams.BalanceRuleId).key, (string)null);
if (!string.IsNullOrEmpty(text))
{
rule.id = text;
return true;
}
return false;
}
public static bool TryToFillRuleWithEnemyId(BalancingRule rule, EventPayload payload, bool enforceNonEmpty = false)
{
(string, Type, string) tuple = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.EnemyId);
string text = payload.Get<string>(tuple.Item1, (string)null);
if (string.IsNullOrEmpty(text))
{
if (enforceNonEmpty)
{
OutwardEnemiesBalancer.LogSL("BalancingRuleHelpers@TryToFillRuleWithEnemyId didn't receive " + tuple.Item1 + "!");
}
return false;
}
rule.enemyID = text;
return true;
}
public static bool TryToFillRuleWithEnemyName(BalancingRule rule, EventPayload payload, bool enforceNonEmpty = false)
{
(string, Type, string) tuple = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.EnemyName);
string text = payload.Get<string>(tuple.Item1, (string)null);
if (string.IsNullOrEmpty(text))
{
if (enforceNonEmpty)
{
OutwardEnemiesBalancer.LogSL("BalancingRuleHelpers@TryToFillRuleWithEnemyName didn't receive " + tuple.Item1 + "!");
}
return false;
}
rule.enemyName = text;
return true;
}
public static bool FillRuleWithEnvironmentConditions(BalancingRule rule, EventPayload payload)
{
(string, Type, string) tuple = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.AreaFamily);
(string, Type, string) tuple2 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.Faction);
(string, Type, string) tuple3 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.AreaEnum);
rule.areaFamily = payload.Get<AreaFamily>(tuple.Item1, (AreaFamily)null);
rule.faction = EventPayloadEnumHelper.GetEnum<Factions>(payload, tuple2.Item1, (Factions?)null);
rule.area = EventPayloadEnumHelper.GetEnum<AreaEnum>(payload, tuple3.Item1, (AreaEnum?)null);
if (rule.areaFamily == null && !rule.faction.HasValue)
{
return rule.area.HasValue;
}
return true;
}
public static bool FillRuleForStrongEnemyTypes(BalancingRule rule, EventPayload payload)
{
(string, Type, string) tuple = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.IsForBosses);
(string, Type, string) tuple2 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.IsForBossesPawns);
(string, Type, string) tuple3 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.IsForStoryBosses);
(string, Type, string) tuple4 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.IsForUniqueArenaBosses);
(string, Type, string) tuple5 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.IsForUniqueEnemies);
rule.isBoss = payload.Get<bool>(tuple.Item1, false);
rule.isBossPawn = payload.Get<bool>(tuple2.Item1, false);
rule.isStoryBoss = payload.Get<bool>(tuple3.Item1, false);
rule.isUniqueArenaBoss = payload.Get<bool>(tuple4.Item1, false);
rule.isUniqueEnemy = payload.Get<bool>(tuple5.Item1, false);
if (!rule.isBoss && !rule.isBossPawn && !rule.isStoryBoss && !rule.isUniqueArenaBoss)
{
return rule.isUniqueEnemy;
}
return true;
}
public static bool FillRuleWithExceptions(BalancingRule rule, EventPayload payload)
{
(string, Type, string) tuple = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ExceptIds);
(string, Type, string) tuple2 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ExceptNames);
rule.exceptIds = payload.Get<List<string>>(tuple.Item1, (List<string>)null);
rule.exceptNames = payload.Get<List<string>>(tuple2.Item1, (List<string>)null);
if (rule.exceptIds == null)
{
return rule.exceptNames != null;
}
return true;
}
public static bool FillRuleWithIdExceptions(BalancingRule rule, EventPayload payload)
{
rule.exceptIds = payload.Get<List<string>>(EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ExceptIds).key, (List<string>)null);
return rule.exceptIds != null;
}
public static void FillRuleWithStatModifications(BalancingRule rule, EventPayload payload)
{
ValueModifierType? @enum = payload.GetEnum<ValueModifierType>(EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ModifierType).key);
if (@enum.HasValue)
{
rule.modifierType = @enum.Value;
}
(string, Type, string) tuple = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.StatModifications);
(string, Type, string) tuple2 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.StatType);
(string, Type, string) tuple3 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.Value);
Dictionary<string, float?> dictionary = payload.Get<Dictionary<string, float?>>(tuple.Item1, (Dictionary<string, float?>)null);
if (dictionary != null)
{
foreach (KeyValuePair<string, float?> item in dictionary)
{
rule.statModifications[item.Key] = item.Value;
}
}
string text = payload.Get<string>(tuple2.Item1, (string)null);
float? num = payload.Get<float?>(tuple3.Item1, (float?)null);
if (!string.IsNullOrEmpty(text) && num.HasValue)
{
rule.statModifications[text] = num.Value;
}
}
public static void FillRuleWithStatModificationsFromVitalStats(BalancingRule rule, EventPayload payload)
{
(string, Type, string) tuple = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.MaxHealth);
(string, Type, string) tuple2 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.MaxStamina);
(string, Type, string) tuple3 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.MaxMana);
(string, Type, string) tuple4 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.HealthRegen);
(string, Type, string) tuple5 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.StaminaRegen);
(string, Type, string) tuple6 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ManaRegen);
AddStatMod(rule, payload, EnemyBalanceStatType.MaxHealth.ToString(), tuple.Item1);
AddStatMod(rule, payload, EnemyBalanceStatType.MaxStamina.ToString(), tuple2.Item1);
AddStatMod(rule, payload, EnemyBalanceStatType.MaxMana.ToString(), tuple3.Item1);
AddStatMod(rule, payload, EnemyBalanceStatType.HealthRegen.ToString(), tuple4.Item1);
AddStatMod(rule, payload, EnemyBalanceStatType.StaminaRegen.ToString(), tuple5.Item1);
AddStatMod(rule, payload, EnemyBalanceStatType.ManaRegen.ToString(), tuple6.Item1);
}
private static void AddStatMod(BalancingRule rule, EventPayload payload, string statName, string paramKey)
{
float? num = payload.Get<float?>(paramKey, (float?)null);
if (num.HasValue)
{
rule.statModifications[statName] = num.Value;
}
}
public static void FillRuleWithStatModificationsFromEnvironmentalStats(BalancingRule rule, EventPayload payload)
{
(string, Type, string) tuple = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ColdProtection);
(string, Type, string) tuple2 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.HeatProtection);
(string, Type, string) tuple3 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.CorruptionResistance);
(string, Type, string) tuple4 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.Waterproof);
AddStatMod(rule, payload, EnemyBalanceStatType.ColdProtection.ToString(), tuple.Item1);
AddStatMod(rule, payload, EnemyBalanceStatType.HeatProtection.ToString(), tuple2.Item1);
AddStatMod(rule, payload, EnemyBalanceStatType.CorruptionProtection.ToString(), tuple3.Item1);
AddStatMod(rule, payload, EnemyBalanceStatType.Waterproof.ToString(), tuple4.Item1);
}
public static void FillRuleWithStatModificationsFromCombatStats(BalancingRule rule, EventPayload payload)
{
(string, Type, string) tuple = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.Impact);
(string, Type, string) tuple2 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ImpactResistance);
(string, Type, string) tuple3 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.MovementSpeed);
(string, Type, string) tuple4 = EnemyBalanceParamsHelper.Get(EnemyBalanceParams.AttackSpeed);
AddStatMod(rule, payload, EnemyBalanceStatType.Impact.ToString(), tuple.Item1);
AddStatMod(rule, payload, EnemyBalanceStatType.ImpactResistance.ToString(), tuple2.Item1);
AddStatMod(rule, payload, EnemyBalanceStatType.MovementSpeed.ToString(), tuple3.Item1);
AddStatMod(rule, payload, EnemyBalanceStatType.AttackSpeed.ToString(), tuple4.Item1);
}
public static bool ValidateAndAppendRule(BalancingRule rule)
{
if (string.IsNullOrEmpty(rule.enemyID) && string.IsNullOrEmpty(rule.enemyName) && rule.areaFamily == null && !rule.faction.HasValue && !rule.area.HasValue && !rule.isBoss && !rule.isBossPawn && !rule.isStoryBoss && !rule.isUniqueArenaBoss && !rule.isUniqueEnemy)
{
OutwardEnemiesBalancer.LogSL("BalancingRuleHelpers@ValidateAndAppendRule: Rule has no targeting criteria!");
return false;
}
if (rule.statModifications.Count == 0)
{
OutwardEnemiesBalancer.LogSL("BalancingRuleHelpers@ValidateAndAppendRule: Rule has no stat modifications!");
return false;
}
BalancingRuleRegistryManager.Instance.AppendBalancingRule(rule);
return true;
}
}
public static class EventPayloadEnumHelper
{
public static T? GetEnum<T>(this EventPayload payload, string key, T? defaultValue = null) where T : struct, Enum
{
if (!((Dictionary<string, object>)(object)payload).TryGetValue(key, out object value))
{
return defaultValue;
}
if (!TryConvertToEnum<T>(value, out var result))
{
return defaultValue;
}
return result;
}
public static T GetEnum<T>(this EventPayload payload, string key, T defaultValue) where T : struct, Enum
{
if (!((Dictionary<string, object>)(object)payload).TryGetValue(key, out object value))
{
return defaultValue;
}
if (!TryConvertToEnum<T>(value, out var result))
{
return defaultValue;
}
return result;
}
public static bool TryGetEnum<T>(this EventPayload payload, string key, out T result) where T : struct, Enum
{
result = default(T);
if (!((Dictionary<string, object>)(object)payload).TryGetValue(key, out object value))
{
return false;
}
return TryConvertToEnum<T>(value, out result);
}
private static bool TryConvertToEnum<T>(object value, out T result) where T : struct, Enum
{
result = default(T);
if (value is T val)
{
if (!Enum.IsDefined(typeof(T), val))
{
OutwardEnemiesBalancer.LogSL($"EventPayloadEnumHelper: Value '{val}' is not a valid {typeof(T).Name} enum value.");
return false;
}
result = val;
return true;
}
if (value is string text)
{
if (Enum.TryParse<T>(text, ignoreCase: true, out var result2))
{
if (!Enum.IsDefined(typeof(T), result2))
{
OutwardEnemiesBalancer.LogSL("EventPayloadEnumHelper: Value '" + text + "' is not a valid " + typeof(T).Name + " enum value.");
return false;
}
result = result2;
return true;
}
OutwardEnemiesBalancer.LogSL("EventPayloadEnumHelper: Could not parse '" + text + "' to " + typeof(T).Name + " enum.");
return false;
}
OutwardEnemiesBalancer.LogSL("EventPayloadEnumHelper: Cannot convert " + (value?.GetType().Name ?? "null") + " to " + typeof(T).Name + " enum.");
return false;
}
}
}
namespace OutwardEnemiesBalancer.Utility.Extensions
{
public static class EnemyHelperExtensions
{
public static Dictionary<TEnum, TResult> ToDictionaryBySelector<TEnum, TResult>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict, Func<EnemyIdentificationGroupData, TResult> selector) where TEnum : Enum
{
return dict.ToDictionary((KeyValuePair<TEnum, EnemyIdentificationGroupData> kvp) => kvp.Key, (KeyValuePair<TEnum, EnemyIdentificationGroupData> kvp) => selector(kvp.Value));
}
public static List<TResult> GetAll<TResult, TEnum>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict, Func<EnemyIdentificationData, TResult> selector) where TEnum : Enum
{
return dict.SelectMany((KeyValuePair<TEnum, EnemyIdentificationGroupData> kvp) => kvp.Value.Enemies).Select(selector).Distinct()
.ToList();
}
public static List<string> GetAll<TEnum>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict) where TEnum : Enum
{
return dict.GetAll((EnemyIdentificationData e) => e.ID);
}
public static bool TryGetEnum<TEnum>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict, Character character, out TEnum result) where TEnum : Enum
{
string enumIdentificator = BossRegistryManager.GetEnemyBossIdentificator(character);
foreach (KeyValuePair<TEnum, EnemyIdentificationGroupData> item in dict)
{
if (item.Value.Matches(character, (EnemyIdentificationData idData, Character character) => enumIdentificator.Equals(BossRegistryManager.GetIdentificatorFromEnemyIdentification(idData))))
{
result = item.Key;
return true;
}
}
result = default(TEnum);
return false;
}
public static Dictionary<TEnum, string> GetFirstDisplayNameFromGroup<TEnum>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict) where TEnum : Enum
{
return dict.ToDictionary((KeyValuePair<TEnum, EnemyIdentificationGroupData> kvp) => kvp.Key, (KeyValuePair<TEnum, EnemyIdentificationGroupData> kvp) => kvp.Value.Enemies.First().DisplayName);
}
public static Dictionary<TEnum, string> GetFirstNameLocFromGroup<TEnum>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict) where TEnum : Enum
{
return dict.ToDictionary((KeyValuePair<TEnum, EnemyIdentificationGroupData> kvp) => kvp.Key, (KeyValuePair<TEnum, EnemyIdentificationGroupData> kvp) => kvp.Value.Enemies.First().LocKey);
}
public static Dictionary<TEnum, string> GetFirstWikiLocationsFromGroup<TEnum>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict) where TEnum : Enum
{
return dict.ToDictionary((KeyValuePair<TEnum, EnemyIdentificationGroupData> kvp) => kvp.Key, (KeyValuePair<TEnum, EnemyIdentificationGroupData> kvp) => kvp.Value.Enemies.First().WikiLocation);
}
public static Dictionary<TEnum, string> GetFirstGameLocationsFromGroup<TEnum>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict) where TEnum : Enum
{
return dict.ToDictionary((KeyValuePair<TEnum, EnemyIdentificationGroupData> kvp) => kvp.Key, (KeyValuePair<TEnum, EnemyIdentificationGroupData> kvp) => kvp.Value.Enemies.First().GameLocation);
}
public static List<string> GetAllDisplayNames<TEnum>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict) where TEnum : Enum
{
return dict.GetAll((EnemyIdentificationData e) => e.DisplayName);
}
public static List<string> GetAllIds<TEnum>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict) where TEnum : Enum
{
return dict.GetAll((EnemyIdentificationData e) => e.ID);
}
public static List<string> GetAllGameLocations<TEnum>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict) where TEnum : Enum
{
return dict.GetAll((EnemyIdentificationData e) => e.GameLocation);
}
public static List<string> GetAllWikiLocations<TEnum>(this Dictionary<TEnum, EnemyIdentificationGroupData> dict) where TEnum : Enum
{
return dict.GetAll((EnemyIdentificationData e) => e.WikiLocation);
}
}
}
namespace OutwardEnemiesBalancer.Utility.Enums
{
public enum BossCategories
{
Story,
Arena,
Pawn
}
public struct BossID
{
public BossCategories Category;
public EnemyIdentificationGroupData EnemyData;
public Enum EnumValue;
public BossID(BossCategories category, EnemyIdentificationGroupData data, Enum enumValue = null)
{
Category = category;
EnemyData = data;
EnumValue = enumValue;
}
public override string ToString()
{
string text = "";
foreach (EnemyIdentificationData enemy in EnemyData.Enemies)
{
text = text + "Name " + enemy.DisplayName + " Id " + enemy.ID + " enum " + EnumValue.ToString();
}
return $"{Category}:{text}";
}
}
public enum BossPawns
{
Elite_Krypteia_warrior,
Elite_Krypteia_Witch,
Elite_Obsidian_Elemental
}
public static class BossPawnsHelper
{
public static readonly Dictionary<BossPawns, EnemyIdentificationGroupData> Enemies = new Dictionary<BossPawns, EnemyIdentificationGroupData>
{
{
BossPawns.Elite_Krypteia_warrior,
new EnemyIdentificationGroupData("Balira", "KrypteiaGuard", "name_unpc_balira_01", "AbvgKMnPLUiffB6LzjaguQ", "Tower of Regrets Arena", "Unknown Arena", "CalderaDungeonsBosses")
},
{
BossPawns.Elite_Krypteia_Witch,
new EnemyIdentificationGroupData("Balira", "KrypteiaMage", "name_unpc_balira_01", "MfBjNPYsvkODdyLjYrlXXw", "Tower of Regrets Arena", "Unknown Arena", "CalderaDungeonsBosses")
},
{
BossPawns.Elite_Obsidian_Elemental,
new EnemyIdentificationGroupData(new EnemyIdentificationData("Obsidian Elemental", "ObsidianElemental", "Wildlife_ObsidianElemental", "RM13rq4JTEqbuANnncMCKA", "Burning Tree Arena", "Unknown Arena", "EmercarDungeonsBosses"), new EnemyIdentificationData("Obsidian Elemental (1)", "ObsidianElemental", "Wildlife_ObsidianElemental", "Qrq3e4nUpkS8CH3yd8J-ow", "Burning Tree Arena", "Unknown Arena", "EmercarDungeonsBosses"))
}
};
}
public enum EnemyBalanceParams
{
BalanceRuleId,
EnemyId,
EnemyName,
AreaEnum,
AreaFamily,
Faction,
IsForBosses,
IsForBossesPawns,
IsForStoryBosses,
IsForUniqueArenaBosses,
IsForUniqueEnemies,
ExceptIds,
ExceptNames,
StatModifications,
ModifierType,
StatType,
Value,
MinClamp,
MaxClamp,
MaxHealth,
MaxStamina,
MaxMana,
HealthRegen,
StaminaRegen,
ManaRegen,
DamageType,
DamageValue,
ResistanceValue,
ProtectionValue,
ColdProtection,
HeatProtection,
CorruptionResistance,
Waterproof,
Impact,
ImpactResistance,
MovementSpeed,
AttackSpeed,
LoadBalanceRulesXmlPath,
StoreBalanceRulesXmlPath,
NewFaction
}
public static class EnemyBalanceParamsHelper
{
private static readonly Dictionary<EnemyBalanceParams, (string key, Type type, string description)> _registry = new Dictionary<EnemyBalanceParams, (string, Type, string)>
{
[EnemyBalanceParams.BalanceRuleId] = ("balanceRuleId", typeof(string), "Optional. Unique identifier for the balancing rule."),
[EnemyBalanceParams.EnemyId] = ("enemyId", typeof(string), "Optional. Specific enemy UID to target."),
[EnemyBalanceParams.EnemyName] = ("enemyName", typeof(string), "Optional. Enemy display name to target."),
[EnemyBalanceParams.AreaEnum] = ("area", typeof(AreaEnum?), "Optional. Specific area filter."),
[EnemyBalanceParams.AreaFamily] = ("areaFamily", typeof(AreaFamily), "Optional. Area family (region) filter."),
[EnemyBalanceParams.Faction] = ("faction", typeof(Factions?), "Optional. Faction filter."),
[EnemyBalanceParams.IsForBosses] = ("isForBosses", typeof(bool), "Optional. Apply to all bosses."),
[EnemyBalanceParams.IsForBossesPawns] = ("isForBossPawns", typeof(bool), "Optional. Apply to boss pawns."),
[EnemyBalanceParams.IsForStoryBosses] = ("isForStoryBosses", typeof(bool), "Optional. Apply to story bosses."),
[EnemyBalanceParams.IsForUniqueArenaBosses] = ("isForUniqueArenaBosses", typeof(bool), "Optional. Apply to unique arena bosses."),
[EnemyBalanceParams.IsForUniqueEnemies] = ("isForUniqueEnemies", typeof(bool), "Optional. Apply to unique enemies."),
[EnemyBalanceParams.ExceptIds] = ("listExceptIds", typeof(List<string>), "Optional. List of enemy UIDs to exclude."),
[EnemyBalanceParams.ExceptNames] = ("listExceptNames", typeof(List<string>), "Optional. List of enemy names to exclude."),
[EnemyBalanceParams.StatModifications] = ("statModifications", typeof(Dictionary<string, float?>), "Optional. Dictionary of stat name to value. Use with modifierType for scaling."),
[EnemyBalanceParams.ModifierType] = ("modifierType", typeof(ValueModifierType), "Optional. Default Scale. How to modify: Direct, Scale, Add."),
[EnemyBalanceParams.StatType] = ("statType", typeof(string), "Optional. Stat name (string) to modify. Use with value parameter."),
[EnemyBalanceParams.Value] = ("value", typeof(float?), "Optional. Modification value for single stat."),
[EnemyBalanceParams.MinClamp] = ("minClamp", typeof(float?), "Optional. Minimum clamp for the result."),
[EnemyBalanceParams.MaxClamp] = ("maxClamp", typeof(float?), "Optional. Maximum clamp for the result."),
[EnemyBalanceParams.MaxHealth] = ("maxHealth", typeof(float?), "Optional. Max health modification."),
[EnemyBalanceParams.MaxStamina] = ("maxStamina", typeof(float?), "Optional. Max stamina modification."),
[EnemyBalanceParams.MaxMana] = ("maxMana", typeof(float?), "Optional. Max mana modification."),
[EnemyBalanceParams.HealthRegen] = ("healthRegen", typeof(float?), "Optional. Health regen modification."),
[EnemyBalanceParams.StaminaRegen] = ("staminaRegen", typeof(float?), "Optional. Stamina regen modification."),
[EnemyBalanceParams.ManaRegen] = ("manaRegen", typeof(float?), "Optional. Mana regen modification."),
[EnemyBalanceParams.DamageType] = ("damageType", typeof(Types), "Optional. Damage type for grouped damage event."),
[EnemyBalanceParams.DamageValue] = ("damageValue", typeof(float?), "Optional. Damage value for grouped damage event."),
[EnemyBalanceParams.ResistanceValue] = ("resistanceValue", typeof(float?), "Optional. Resistance value for grouped damage event."),
[EnemyBalanceParams.ProtectionValue] = ("protectionValue", typeof(float?), "Optional. Protection value for grouped damage event."),
[EnemyBalanceParams.ColdProtection] = ("coldProtection", typeof(float?), "Optional. Cold protection modification."),
[EnemyBalanceParams.HeatProtection] = ("heatProtection", typeof(float?), "Optional. Heat protection modification."),
[EnemyBalanceParams.CorruptionResistance] = ("corruptionResistance", typeof(float?), "Optional. Corruption resistance modification."),
[EnemyBalanceParams.Waterproof] = ("waterproof", typeof(float?), "Optional. Waterproof modification."),
[EnemyBalanceParams.Impact] = ("impact", typeof(float?), "Optional. Impact damage modification."),
[EnemyBalanceParams.ImpactResistance] = ("impactResistance", typeof(float?), "Optional. Impact resistance modification."),
[EnemyBalanceParams.MovementSpeed] = ("movementSpeed", typeof(float?), "Optional. Movement speed modification."),
[EnemyBalanceParams.AttackSpeed] = ("attackSpeed", typeof(float?), "Optional. Attack speed modification."),
[EnemyBalanceParams.LoadBalanceRulesXmlPath] = ("filePath", typeof(string), "Required. Path to load balance rules XML."),
[EnemyBalanceParams.StoreBalanceRulesXmlPath] = ("filePath", typeof(string), "Optional. Path to save balance rules XML."),
[EnemyBalanceParams.NewFaction] = ("newFaction", typeof(Factions), "Required. The faction to change enemies to.")
};
public static (string key, Type type, string description) Get(EnemyBalanceParams param)
{
return _registry[param];
}
public static (string key, Type type, string description)[] Combine(params object[] items)
{
List<(string, Type, string)> list = new List<(string, Type, string)>();
foreach (object obj in items)
{
if (obj is (string, Type, string) item)
{
list.Add(item);
continue;
}
if (obj is (string, Type, string)[] collection)
{
list.AddRange(collection);
continue;
}
throw new ArgumentException("Unsupported item type: " + obj?.GetType().FullName);
}
return list.ToArray();
}
}
public enum StoryBosseses
{
Crimson_Avatar,
Djinn,
Forge_Master,
Light_Mender,
Plague_Doctor
}
public static class StoryBossesHelper
{
public static readonly Dictionary<StoryBosseses, EnemyIdentificationGroupData> Enemies = new Dictionary<StoryBosseses, EnemyIdentificationGroupData>
{
{
StoryBosseses.Crimson_Avatar,
new EnemyIdentificationGroupData("Burning Man", "CrimsonAvatar (1)", "Undead_BurningMan", "4eeggsSn2Eyah4IjjqvpYQ", "Scarlet Sanctuary", "Scarlet Sanctuary")
},
{
StoryBosseses.Djinn,
new EnemyIdentificationGroupData("Gold Lich", "LichGold", "Undead_LichGold", "EwoPQ0iVwkK-XtNuaVPf3g", "Oil Refinery", "Oil Refinery")
},
{
StoryBosseses.Forge_Master,
new EnemyIdentificationGroupData("Jade Lich", "LichRust", "Undead_LichJade", "shyc5M7b-UGVHBZsJMdP4Q", "Forgotten Research Laboratory", "Forgotten Research Laboratory")
},
{
StoryBosseses.Light_Mender,
new EnemyIdentificationGroupData("Gold Lich", "LichGold", "Undead_LichGold", "EwoPQ0iVwkK-XtNuaVPf3g", "Spire of Light", "Spire of Light")
},
{
StoryBosseses.Plague_Doctor,
new EnemyIdentificationGroupData("Jade Lich", "LichJade", "Undead_LichJade", "8sjFFBPMvkuJcrcyIYs-KA", "Dark Ziggurat", "Dark Ziggurat Interior")
}
};
}
public enum UniqueArenaBosses
{
Ash_Giant_Highmonk,
Brand_Squire,
Breath_of_Darkness,
Calixa_Boss,
Concealed_Knight,
Elite_Alpha_Tuanosaur,
Elite_Ash_Giant,
Elite_Beast_Golem,
Elite_Boozu,
Elite_Burning_Man,
Elite_Crescent_Shark,
Elite_Crimson_Avatar,
Elite_Gargoyle_Alchemist,
Elite_Gargoyle_Mage,
Elite_Gargoyle_Warrior,
Elite_Mantis_Shrimp,
Elite_Sublime_Shell,
Elite_Torcrab,
Grandmother,
Immaculate_Dreamer,
Immaculates_Bird,
Light_Mender,
Plague_Doctor,
Troglodyte_Queen
}
public static class UniqueArenaBossesHelper
{
public static readonly Dictionary<UniqueArenaBosses, EnemyIdentificationGroupData> Enemies = new Dictionary<UniqueArenaBosses, EnemyIdentificationGroupData>
{
{
UniqueArenaBosses.Ash_Giant_Highmonk,
new EnemyIdentificationGroupData("Ash Giant Priest", "EliteAshGiantPriest", "Giant_Priest", "UnIXpnDMzUSfBu4S-ZDgsA", "Giant's Village Arena (south)", "Unknown Arena", "HallowedDungeonsBosses")
},
{
UniqueArenaBosses.Brand_Squire,
new EnemyIdentificationGroupData("Desert Bandit", "EliteBrandSquire", "Bandit_Desert_Basic", "sb0TOkOPS06jhp56AOYJCw", "Conflux Mountain Arena", "Unknown Arena", "ChersoneseDungeonsBosses")
},
{
UniqueArenaBosses.Breath_of_Darkness,
new EnemyIdentificationGroupData("Breath of Darkness", "AncientDwellerDark", "Elite_Dweller", "JmeufMpL_E6eYnqCYP2r3w", "The Vault of Stone", "The Vault of Stone")
},
{
UniqueArenaBosses.Calixa_Boss,
new EnemyIdentificationGroupData("Cyrene", "EliteCalixa", "Cyrene", "eCz766tEIEOWfK81om19wg", "Levant Arena", "Unknown Arena", "AbrassarDungeonsBosses")
},
{
UniqueArenaBosses.Concealed_Knight,
new EnemyIdentificationGroupData("???", "NewBandit", "name_unpc_unknown_01", "XVuyIaCAVkatv89kId9Uqw", "Shipwreck (Castaway)", "CierzoTutorial", "CierzoTutorial")
},
{
UniqueArenaBosses.Elite_Alpha_Tuanosaur,
new EnemyIdentificationGroupData("Alpha Tuanosaur", "EliteTuanosaurAlpha", "Wildlife_TuanosaurAlpha", "El8bA54i4E6vZraXsVZMow", "Ziggurat Passage Arena", "Unknown Arena", "HallowedDungeonsBosses")
},
{
UniqueArenaBosses.Elite_Ash_Giant,
new EnemyIdentificationGroupData(new EnemyIdentificationData("Ash Giant", "EliteAshGiantPaf", "Giant_Guard", "3vXChaIK90qgq03PmsHFCg", "Unknown Arena", "Unknown Arena", "HallowedDungeonsBosses"), new EnemyIdentificationData("Ash Giant", "EliteAshGiantPif", "Giant_Guard", "851czvFVDUaB42CgVzfKdg", "Unknown Arena", "Unknown Arena", "HallowedDungeonsBosses"), new EnemyIdentificationData("Ash Giant", "EliteAshGiantPouf", "Giant_Guard", "kNmmOHZzKU-82F3OoX9NXw", "Unknown Arena", "Unknown Arena", "HallowedDungeonsBosses"))
},
{
UniqueArenaBosses.Elite_Beast_Golem,
new EnemyIdentificationGroupData("Beast Golem", "EliteBeastGolem", "Golem_Beast", "n83g2QJhwUyUrN469WC4jA", "Parched Shipwrecks Arena", "Unknown Arena", "AbrassarDungeonsBosses")
},
{
UniqueArenaBosses.Elite_Boozu,
new EnemyIdentificationGroupData("Blade Dancer", "BoozuProudBeast", "Wildlife_BladeDancer", "2Ef5z9OfYkev7M7Oi9GN-A", "Mana Lake Arena", "Unknown Arena", "AntiqueFieldDungeonsBosses")
},
{
UniqueArenaBosses.Elite_Burning_Man,
new EnemyIdentificationGroupData("Burning Man", "EliteBurningMan", "Undead_BurningMan", "JmeufMpL_E6eYnqCYP2r3w", "Burning Tree Arena", "Unknown Arena", "EmercarDungeonsBosses")
},
{
UniqueArenaBosses.Elite_Crescent_Shark,
new EnemyIdentificationGroupData(new EnemyIdentificationData("Crescent Shark", "EliteCrescentShark", "Wildlife_CrescentShark", "RM13rq4JTEqbuANnncMCKA", "Electric Lab Arena", "Unknown Arena", "AbrassarDungeonsBosses"), new EnemyIdentificationData("Crescent Shark", "EliteCrescentShark (1)", "Wildlife_CrescentShark", "ElDi5-rvqEqJKcXhEdgwBQ", "Electric Lab Arena", "Unknown Arena", "AbrassarDungeonsBosses"), new EnemyIdentificationData("Crescent Shark", "EliteCrescentShark (2)", "Wildlife_CrescentShark", "z3sfjJtqQEmUZ_S6g2RPIg", "Electric Lab Arena", "Unknown Arena", "AbrassarDungeonsBosses"))
},
{
UniqueArenaBosses.Elite_Crimson_Avatar,
new EnemyIdentificationGroupData("Burning Man", "CrimsonAvatarElite (1)", "Undead_BurningMan", "JmeufMpL_E6eYnqCYP2r3w", "Vault of Stone Arena", "Unknown Arena", "CalderaDungeonsBosses")
},
{
UniqueArenaBosses.Elite_Gargoyle_Alchemist,
new EnemyIdentificationGroupData("Shell Horror", "GargoyleBossMelee (1)", "Horror_Shell", "k75QmVu5t0e_zIjHnUFbIQ", "New Sirocco Arena", "Unknown Arena", "CalderaDungeonsBosses")
},
{
UniqueArenaBosses.Elite_Gargoyle_Mage,
new EnemyIdentificationGroupData("Shell Horror", "GargoyleBossMelee (1)", "Horror_Shell", "AKBYHSSMJUaH9ddLiz_SZA", "New Sirocco Arena", "Unknown Arena", "CalderaDungeonsBosses")
},
{
UniqueArenaBosses.Elite_Gargoyle_Warrior,
new EnemyIdentificationGroupData("Shell Horror", "GargoyleBossMelee (1)", "Horror_Shell", "Z6yTTWK4u0GjDPfZ9X332A", "New Sirocco Arena", "Unknown Arena", "CalderaDungeonsBosses")
},
{
UniqueArenaBosses.Elite_Mantis_Shrimp,
new EnemyIdentificationGroupData("Mantis Shrimp", "EliteMantisShrimp", "Wildlife_Shrimp", "RM13rq4JTEqbuANnncMCKA", "Voltaic Hatchery Arena", "Unknown Arena", "ChersoneseDungeonsBosses")
},
{
UniqueArenaBosses.Elite_Sublime_Shell,
new EnemyIdentificationGroupData("Nicolas", "CageArmorBoss (1)", "Nicolas", "X-dfltOoGUm7YlCE_Li1zQ", "Isolated Windmill Arena", "Unknown Arena", "AntiqueFieldDungeonsBosses")
},
{
UniqueArenaBosses.Elite_Torcrab,
new EnemyIdentificationGroupData("Wildlife_Torcrab", "TorcrabGiant (1)", "Wildlife_Torcrab", "gQDvpLQh3kimgwMmvXJc4g", "River of Red Arena", "Unknown Arena", "CalderaDungeonsBosses")
},
{
UniqueArenaBosses.Grandmother,
new EnemyIdentificationGroupData("Ghost", "Grandmother", "Undead_Ghost", "7G5APgUksEGdQrBxKXr04g", "Tower of Regrets Arena", "Unknown Arena", "CalderaDungeonsBosses")
},
{
UniqueArenaBosses.Immaculate_Dreamer,
new EnemyIdentificationGroupData("Immaculate", "EliteImmaculate", "Horror_Immaculate", "9jsiejBtHkOzeo4tOyyweg", "Cabal of Wind Temple Arena", "Unknown Arena", "EmercarDungeonsBosses")
},
{
UniqueArenaBosses.Immaculates_Bird,
new EnemyIdentificationGroupData("Immaculate", "EliteSupremeShell (1)", "Horror_Immaculate", "JsyOv_Cwu0K0HlXyZInRQQ", "Immaculate's Camp Arena", "Unknown Arena", "AntiqueFieldDungeonsBosses")
},
{
UniqueArenaBosses.Light_Mender,
new EnemyIdentificationGroupData("Gold Lich", "LichGold (1)", "Undead_LichGold", "v9mN1u1uMkaxsncBXhIM9A", "Spire of Light", "Unknown Arena", "EmercarDungeonsBosses")
},
{
UniqueArenaBosses.Plague_Doctor,
new EnemyIdentificationGroupData("Jade Lich", "LichJade (1)", "Undead_LichJade", "GfWl16_MZ0uS7UYIKpS5Lg", "Dark Ziggurat", "Unknown Arena", "EmercarDungeonsBosses")
},
{
UniqueArenaBosses.Troglodyte_Queen,
new EnemyIdentificationGroupData("Mana Troglodyte", "TroglodyteMana (1)", "Troglodyte_Mana", "pcQlY_whLUCC-FZel18VMg", "Blister Burrow Arena", "Unknown Arena", "ChersoneseDungeonsBosses")
}
};
}
public enum UniqueEnemies
{
Accursed_Wendigo,
Altered_Gargoyle,
Ancestral_General,
Ancestral_Soldier,
Bloody_Alexis,
Calygrey_Hero,
Chromatic_Arcane_Elemental,
Cracked_Gargoyle,
Elemental_Parasite,
Executioner_Bug,
Ghost_of_Vanasse,
Giant_Horror,
Glacial_Tuanosaur,
Golden_Matriarch,
Grandmother_Medyse,
Greater_Grotesque,
Guardian_of_the_Compass,
Kazite_Admiral,
Lightning_Dancer,
Liquid_Cooled_Golem,
Luke_the_Pearlescent,
Mad_Captains_Bones,
Matriarch_Myrmitaur,
Quartz_Elemental,
Razorhorn_Stekosaur,
Royal_Manticore,
Rusted_Enforcer,
Sandrose_Horror,
She_Who_Speaks,
That_Annoying_Troglodyte,
The_Crusher,
The_First_Cannibal,
The_Last_Acolyte,
Thunderbolt_Golem,
Titanic_Guardian_Mk_7,
Troglodyte_Archmage,
Tyrant_of_the_Hive,
Vile_Illuminator,
Virulent_Hiveman,
Volcanic_Gastrocin
}
public static class UniqueEnemiesHelper
{
public static readonly Dictionary<UniqueEnemies, EnemyIdentificationGroupData> Enemies = new Dictionary<UniqueEnemies, EnemyIdentificationGroupData>
{
{
UniqueEnemies.Accursed_Wendigo,
new EnemyIdentificationGroupData("Accursed Wendigo", "WendigoAccursed", "Unique_DefEd_Wendigo", "ElxncPIfuEuWkexSKkqFXg", "Corrupted Tombs", "Corrupted Tombs")
},
{
UniqueEnemies.Altered_Gargoyle,
new EnemyIdentificationGroupData("Altered Gargoyle", "GargoyleAltered", "Unique_DefEd_AlteredGargoyle", "dhQVMNRU5kCIWsWRFYpDdw", "Ziggurat Passage", "Ziggurat Passage")
},
{
UniqueEnemies.Ancestral_General,
new EnemyIdentificationGroupData("Ancestral General", "SkeletonBig (1)", "Unique_DefEd_AncestorBig", "XVuyIaCAVkatv89kId9Uqw", "Necropolis", "Necropolis")
},
{
UniqueEnemies.Ancestral_Soldier,
new EnemyIdentificationGroupData("Ancestral Soldier", "SkeletonSmall (1)", "Unique_DefEd_AncestorSmall", "IwZYxBIQZkaXXMWT9HC5nA", "Necropolis", "Necropolis")
},
{
UniqueEnemies.Bloody_Alexis,
new EnemyIdentificationGroupData("Bloody Alexis", "HumanArmoredThug", "Unique_DefEd_ArmoredThug", "VfYPPb4wcESdDVSiEq4UhA", "Undercity Passage", "Undercity Passage")
},
{
UniqueEnemies.Calygrey_Hero,
new EnemyIdentificationGroupData("Calygrey Hero", "LionmanElite (1)", "Elite_Calygrey", "pMfhK69Stky7MvE9Ro0XMQ", "Steam Bath Tunnels", "Steam Bath Tunnels")
},
{
UniqueEnemies.Chromatic_Arcane_Elemental,
new EnemyIdentificationGroupData("Chromatic Arcane Elemental", "ElementalChromatic", "Unique_DefEd_ChromaElemental", "RM13rq4JTEqbuANnncMCKA", "Compromised Mana Transfer Station", "Compromised Mana Transfer Station")
},
{
UniqueEnemies.Cracked_Gargoyle,
new EnemyIdentificationGroupData("Cracked Gargoyle", "GargoyleCracked", "Unique_DefEd_CrackedGargoyle", "-McLNdZsNEa3itw-ny7YBw", "Ark of the Exiled", "Ark of the Exiled")
},
{
UniqueEnemies.Elemental_Parasite,
new EnemyIdentificationGroupData("Crescent Shark", "ElementalParasite", "Wildlife_CrescentShark", "YDPy9S-An0-qGuvrJAM8yA", "Crumbling Loading Docks", "Crumbling Loading Docks")
},
{
UniqueEnemies.Executioner_Bug,
new EnemyIdentificationGroupData("Executioner Bug", "ExecutionerBug", "Unique_DefEd_ExecutionerBug", "MWplmyrxokSXELL63oWcAg", "The Slide", "The Slide")
},
{
UniqueEnemies.Ghost_of_Vanasse,
new EnemyIdentificationGroupData("Ghost of Vanasse", "GhostOfVanasse", "Undead_GhostVanasse", "R5UngwGS5EGWCH13toZO8Q", "Chersonese", "Chersonese")
},
{
UniqueEnemies.Giant_Horror,
new EnemyIdentificationGroupData("Giant_Horror", "GiantHorror", "Giant_Horror", "SntMM-EzuE-ptgmqp4qkYQ", "Crumbling Loading Docks", "Crumbling Loading Docks")
},
{
UniqueEnemies.Glacial_Tuanosaur,
new EnemyIdentificationGroupData("Glacial Tuanosaur", "TuanosaurIce", "Unique_DefEd_GlacialTuano", "1IKBT9DYc0yIkESwKtU40g", "Conflux Chambers", "Conflux Chambers")
},
{
UniqueEnemies.Golden_Matriarch,
new EnemyIdentificationGroupData("Golden Matriarch", "SpecterMeleeMatriarch", "Unique_DefEd_GoldenMatriarch", "GI-aE4Ry7UOIyAYYk7emFg", "Voltaic Hatchery", "Voltaic Hatchery")
},
{
UniqueEnemies.Grandmother_Medyse,
new EnemyIdentificationGroupData("Grandmother Medyse", "JellyFishMother", "Unique_DefEd_GrandmotherMedyse", "kp9R4kaoG02YfLdS9ROM4w", "Sulphuric Caverns", "Sulphuric Caverns")
},
{
UniqueEnemies.Greater_Grotesque,
new EnemyIdentificationGroupData("Greater Grotesque", "ImmaculateHorrorGreater", "Unique_DefEd_GreaterGrotestue", "JmeufMpL_E6eYnqCYP2r3w", "Lost Golem Manufacturing Facility", "Lost Golem Manufacturing Facility")
},
{
UniqueEnemies.Guardian_of_the_Compass,
new EnemyIdentificationGroupData("Guardian of the Compass", "GolemBoss", "Golem_Basic2", "BINT--E9xUaCgp4onBM54g", "The Walled Garden", "Abrassar")
},
{
UniqueEnemies.Kazite_Admiral,
new EnemyIdentificationGroupData("Kazite Admiral", "HumanKaziteCaptain", "Unique_DefEd_KaziteCaptain", "XVuyIaCAVkatv89kId9Uqw", "Abandoned Living Quarters", "Abandoned Living Quarters")
},
{
UniqueEnemies.Lightning_Dancer,
new EnemyIdentificationGroupData("Lightning Dancer", "BladeDancerLight", "Unique_DefEd_LightningDancer", "QRzc3AYY10CWyXOMgrIQTg", "Ancient Foundry", "Ancient Foundry")
},
{
UniqueEnemies.Liquid_Cooled_Golem,
new EnemyIdentificationGroupData("Liquid-Cooled Golem", "GolemShieldedIce", "Unique_DefEd_LiquidGolem", "8ztut4_yiEmK0-NFLa-XNQ", "Destroyed Test Chambers", "Destroyed Test Chambers")
},
{
UniqueEnemies.Luke_the_Pearlescent,
new EnemyIdentificationGroupData("Luke the Pearlescent", "NewBanditEquip_WhiteScavengerCaptainBoss_A (1)", "Bandit_Standard_Captain2", "XVuyIaCAVkatv89kId9Uqw", "Ruins of Old Levant", "Abrassar")
},
{
UniqueEnemies.Mad_Captains_Bones,
new EnemyIdentificationGroupData("Mad Captain's Bones", "SkeletFighter", "Undead_Skeleton2", "JM_HjGXMlkq7a1Yb6gijgQ", "Pirates' Hideout", "Chersonese Misc. Dungeons")
},
{
UniqueEnemies.Matriarch_Myrmitaur,
new EnemyIdentificationGroupData("Matriarch Myrmitaur", "MyrmElite (1)", "Elite_Myrm", "6sB4_5lOJU2bWuMHnOL4Ww", "Myrmitaur's Haven", "Myrmitaur's Haven")
},
{
UniqueEnemies.Quartz_Elemental,
new EnemyIdentificationGroupData("Quartz Elemental", "ObsidianElementalQuartz", "Unique_DefEd_QuartzElemental", "LhhpSt8BO0aRN5mbeSuDrw", "The Grotto of Chalcedony", "The Grotto of Chalcedony")
},
{
UniqueEnemies.Razorhorn_Stekosaur,
new EnemyIdentificationGroupData("Razorhorn Stekosaur", "SteakosaurBlack (1)", "Unique_DefEd_BlackSteko", "03dSXwJMRUuzGu8s3faATQ", "Reptilian Lair", "Reptilian Lair")
},
{
UniqueEnemies.Royal_Manticore,
new EnemyIdentificationGroupData("The Royal Manticore", "RoyalManticore", "Wildlife_Manticore2", "RM13rq4JTEqbuANnncMCKA", "Enmerkar Forest", "Enmerkar Forest")
},
{
UniqueEnemies.Rusted_Enforcer,
new EnemyIdentificationGroupData("Rusted Enforcer", "GolemRusted (1)", "Unique_DefEd_RustyGolem", "Ed2bzrgz5k-cRx3bUYTfmg", "Ghost Pass", "Ghost Pass")
},
{
UniqueEnemies.Sandrose_Horror,
new EnemyIdentificationGroupData("Sandrose Horror", "ShelledHorrorBurning", "Unique_DefEd_SandroseHorror", "H7HoCKhBl0mC1j9UOECDrQ", "Sand Rose Cave", "Sand Rose Cave")
},
{
UniqueEnemies.She_Who_Speaks,
new EnemyIdentificationGroupData("She Who Speaks", "AncientDwellerSpeak", "Unique_DefEd_BossDweller", "MBooN38mU0GPjQJGRuJ95g", "The Vault of Stone", "The Vault of Stone")
},
{
UniqueEnemies.That_Annoying_Troglodyte,
new EnemyIdentificationGroupData("That Annoying Troglodyte", "TroglodyteAnnoying", "Unique_DefEd_AnnoyingTrog", "no-Z4ibpcEWbNntm_wRwZA", "Jade Quarry", "Jade Quarry")
},
{
UniqueEnemies.The_Crusher,
new EnemyIdentificationGroupData("The Crusher", "HumanCrusher (1)", "Unique_DefEd_DesertCrusher", "AZL-EjXmhkOYB1obj0VkTw", "Ancestor's Resting Place", "Ancestor's Resting Place")
},
{
UniqueEnemies.The_First_Cannibal,
new EnemyIdentificationGroupData("The First Cannibal", "WendigoCanibal", "Wildlife_Wendigo2", "wrYHXXh8J0KMhwoV8AC59w", "Face of the Ancients", "Face of the Ancients")
},
{
UniqueEnemies.The_Last_Acolyte,
new EnemyIdentificationGroupData("The Last Acolyte", "HumanAcolyte (1)", "Unique_DefEd_LastAcolyte", "YeYzQP-gYUmSivlk5JCJew", "Stone Titan Caves", "Stone Titan Caves")
},
{
UniqueEnemies.Thunderbolt_Golem,
new EnemyIdentificationGroupData("Thunderbolt Golem", "ForgeGolemLight (1)", "Unique_DefEd_ProtypeForgeGolem", "4qCAJzcAfEKNcl_c2k0r9g", "Electric Lab", "Electric Lab")
},
{
UniqueEnemies.Titanic_Guardian_Mk_7,
new EnemyIdentificationGroupData(new EnemyIdentificationData("Jade Lich", "TitanGolemHalberd", "Undead_LichJade", "65aI6XT89kmHa1bwJz5PGQ", "Ruined Warehouse", "Ruined Warehouse"), new EnemyIdentificationData("Jade Lich", "TitanGolemHammer", "Undead_LichJade", "G_Q0oH1ttkWAZXCMuaAHjA", "Ruined Warehouse", "Ruined Warehouse"), new EnemyIdentificationData("Jade Lich", "TitanGolemSword", "Undead_LichJade", "wj3frikyIkqwVv7myrc5gw", "Ruined Warehouse", "Ruined Warehouse"))
},
{
UniqueEnemies.Troglodyte_Archmage,
new EnemyIdentificationGroupData("Troglodyte Archmage", "TroglodyteArcMageDefEd (1)", "Unique_DefEd_TrogMage", "syKWNGT3QUO3nXxPt1WEcQ", "Blister Burrow", "Blister Burrow")
},
{
UniqueEnemies.Tyrant_of_the_Hive,
new EnemyIdentificationGroupData("Tyrant of the Hive", "HiveLord1AID+", "Undead_Hivelord2", "yOo-iKN3-0mAtZ2pG16pyw", "Forest Hives", "Forest Hives")
},
{
UniqueEnemies.Vile_Illuminator,
new EnemyIdentificationGroupData("Vile Illuminator", "IlluminatorHorrorVile", "Unique_DefEd_VileIlluminator", "l5ignQfsE0Cv4imB9DZJ5w", "Cabal of Wind Temple", "Cabal of Wind Temple")
},
{
UniqueEnemies.Virulent_Hiveman,
new EnemyIdentificationGroupData("Virulent Hiveman", "HiveManVirulent", "Unique_DefEd_VirulentHiveman", "v1PnLFpcxEmm_IrZaP-eyg", "Ancient Hive", "Ancient Hive")
},
{
UniqueEnemies.Volcanic_Gastrocin,
new EnemyIdentificationGroupData("Volcanic Gastrocin", "SlughellVolcanic", "Unique_DefEd_VolcanicSlug", "fEFTRdXp1kOWX-Z9OMAkBg", "The Eldest Brother", "The Eldest Brother")
}
};
}
}
namespace OutwardEnemiesBalancer.Utility.Data
{
public class EnemyIdentificationData
{
public string DisplayName;
public string InternalName;
public string LocKey;
public string ID;
public string WikiLocation;
public string GameLocation;
public string SceneName;
public EnemyIdentificationData(string Name, string m_name, string m_nameLoc, string id, string wikiLocation, string gameLocation, string sceneName = "")
{
DisplayName = Name;
InternalName = m_name;
LocKey = m_nameLoc;
ID = id;
WikiLocation = wikiLocation;
GameLocation = gameLocation;
SceneName = sceneName;
}
public bool Matches(Character character, params Func<EnemyIdentificationData, Character, bool>[] comparers)
{
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (comparers == null || comparers.Length == 0)
{
string iD = ID;
UID uID = character.UID;
return string.Equals(iD, ((UID)(ref uID)).Value, StringComparison.Ordinal);
}
return comparers.Any((Func<EnemyIdentificationData, Character, bool> c) => c(this, character));
}
}
public class EnemyIdentificationGroupData
{
public List<EnemyIdentificationData> Enemies { get; }
public EnemyIdentificationGroupData(string displayName, string internalName, string locKey, string uid, string wikiLocation, string gameLocation, string sceneName = "")
{
Enemies = new List<EnemyIdentificationData>
{
new EnemyIdentificationData(displayName, internalName, locKey, uid, wikiLocation, gameLocation, sceneName)
};
}
public EnemyIdentificationGroupData(params EnemyIdentificationData[] entries)
{
Enemies = new List<EnemyIdentificationData>();
Enemies.AddRange(entries);
}
public bool Matches(Character character, params Func<EnemyIdentificationData, Character, bool>[] comparers)
{
return Enemies.Any((EnemyIdentificationData e) => e.Matches(character, comparers));
}
public EnemyIdentificationData GetMatching(Character character)
{
return Enemies.FirstOrDefault((EnemyIdentificationData e) => e.Matches(character));
}
}
}
namespace OutwardEnemiesBalancer.Managers
{
public class BalancingRuleRegistryManager
{
private static BalancingRuleRegistryManager _instance;
public List<BalancingRule> balancingRules = new List<BalancingRule>();
public static BalancingRuleRegistryManager Instance
{
get
{
if (_instance == null)
{
_instance = new BalancingRuleRegistryManager();
}
return _instance;
}
}
private BalancingRuleRegistryManager()
{
}
public void AppendBalancingRules(List<BalancingRule> rules)
{
foreach (BalancingRule rule in rules)
{
AppendBalancingRule(rule);
}
}
public void AppendBalancingRule(BalancingRule rule)
{
BalancingRule balancingRule = balancingRules.FirstOrDefault((BalancingRule r) => r.id == rule.id);
if (balancingRule != null)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Rule '" + rule.id + "' already exists. Merging stat modifications:");
stringBuilder.AppendLine(" Old mods: " + CountStats(balancingRule.statModifications));
stringBuilder.AppendLine(" New mods: " + CountStats(rule.statModifications));
foreach (KeyValuePair<string, float?> statModification in rule.statModifications)
{
float? value;
string arg = ((!balancingRule.statModifications.TryGetValue(statModification.Key, out value)) ? "added" : "updated");
balancingRule.statModifications[statModification.Key] = statModification.Value;
stringBuilder.AppendLine($" {arg}: {statModification.Key} = {statModification.Value}");
}
OutwardEnemiesBalancer.LogSL(stringBuilder.ToString());
}
else
{
balancingRules.Add(rule);
EventBusPublisher.SendAppendBalancingRule(rule.id);
}
}
private string CountStats(Dictionary<string, float?> stats)
{
if (stats == null)
{
return "0";
}
return stats.Count.ToString();
}
public void RemoveBalancingRuleById(string id)
{
foreach (BalancingRule item in balancingRules.Where((BalancingRule rule) => rule.id == id).ToList())
{
RemoveBalancingRule(item);
}
}
public void RemoveBalancingRule(BalancingRule rule)
{
balancingRules.Remove(rule);
EventBusPublisher.SendRemoveBalancingRule(rule.id);
}
public List<BalancingRule> GetMatchingRules(Character character)
{
List<BalancingRule> list = new List<BalancingRule>();
foreach (BalancingRule balancingRule in balancingRules)
{
if (balancingRule.Matches(character))
{
list.Add(balancingRule);
}
}
return list;
}
}
public class BalancingRulesSerializer
{
private static BalancingRulesSerializer _instance;
public static BalancingRulesSerializer Instance
{
get
{
if (_instance == null)
{
_instance = new BalancingRulesSerializer();
}
return _instance;
}
}
private BalancingRulesSerializer()
{
}
public BalancingRulesFile Load(string path)
{
try
{
if (!File.Exists(path))
{
OutwardEnemiesBalancer.LogSL("BalancingRules file not found at: " + path);
return null;
}
XmlSerializer xmlSerializer = new XmlSerializer(typeof(BalancingRulesFile));
using FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
return xmlSerializer.Deserialize(stream) as BalancingRulesFile;
}
catch (Exception ex)
{
OutwardEnemiesBalancer.LogSL("Failed to load BalancingRules file at '" + path + "': " + ex.Message);
return null;
}
}
public void LoadPlayerBalanceRules()
{
if (File.Exists(PathsManager.DefaultBalanceRulesPath))
{
LoadBalanceRules(PathsManager.DefaultBalanceRulesPath);
}
}
public void LoadBalanceRules(string path)
{
try
{
if (!File.Exists(path))
{
OutwardEnemiesBalancer.LogSL("BalancingRulesSerializer@LoadBalanceRules file not found at: " + path);
return;
}
BalancingRulesFile balancingRulesFile = Load(path);
if (balancingRulesFile != null)
{
List<BalancingRule> balancingRules = GetBalancingRules(balancingRulesFile);
BalancingRuleRegistryManager.Instance.AppendBalancingRules(balancingRules);
}
}
catch (Exception ex)
{
OutwardEnemiesBalancer.LogSL("BalancingRulesSerializer@LoadBalanceRules failed loading '" + path + "': " + ex.Message);
}
}
public void SaveBalanceRulesToXml(string filePath, List<BalancingRule> rules)
{
try
{
string directoryName = Path.GetDirectoryName(filePath);
if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
BalancingRulesFile o = BuildBalancingRulesFile(rules);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(BalancingRulesFile));
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
NewLineOnAttributes = false
};
using XmlWriter xmlWriter = XmlWriter.Create(filePath, settings);
xmlSerializer.Serialize(xmlWriter, o);
}
catch (Exception ex)
{
OutwardEnemiesBalancer.LogSL("BalancingRulesSerializer@SaveBalanceRulesToXml failed saving '" + filePath + "': " + ex.Message);
}
}
public List<BalancingRule> LoadBalanceRulesFromXmlSync(string path)
{
try
{
if (!File.Exists(path))
{
OutwardEnemiesBalancer.LogSL("LoadBalanceRulesFromXmlSync file not found at: " + path);
return null;
}
BalancingRulesFile balancingRulesFile = Load(path);
if (balancingRulesFile == null)
{
return null;
}
return GetBalancingRules(balancingRulesFile);
}
catch (Exception ex)
{
OutwardEnemiesBalancer.LogSL("LoadBalanceRulesFromXmlSync failed loading '" + path + "': " + ex.Message);
return null;
}
}
public List<BalancingRule> GetBalancingRules(BalancingRulesFile file)
{
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
List<BalancingRule> list = new List<BalancingRule>();
foreach (BalancingRuleSerializable rule in file.Rules)
{
BalancingRule balancingRule = new BalancingRule(rule.Id);
balancingRule.enemyID = (string.IsNullOrEmpty(rule.EnemyID) ? null : rule.EnemyID);
balancingRule.enemyName = rule.EnemyName ?? "";
balancingRule.areaFamily = AreaFamiliesHelpers.GetAreaFamilyByName(rule.AreaFamilyName);
balancingRule.area = AreaHelpers.GetAreaEnumFromAreaName(rule.AreaName);
if (!string.IsNullOrEmpty(rule.FactionName) && Enum.TryParse<Factions>(rule.FactionName, out Factions result))
{
balancingRule.faction = result;
}
balancingRule.exceptIds = rule.ExceptIds;
balancingRule.exceptNames = rule.ExceptNames;
balancingRule.isBoss = rule.IsBoss;
balancingRule.isBossPawn = rule.IsBossPawn;
balancingRule.isStoryBoss = rule.IsStoryBoss;
balancingRule.isUniqueArenaBoss = rule.IsUniqueArenaBoss;
balancingRule.isUniqueEnemy = rule.IsUniqueEnemy;
if (rule.StatModifications != null)
{
foreach (StatModificationSerializable statModification in rule.StatModifications)
{
balancingRule.statModifications[statModification.StatName] = statModification.Value;
}
}
list.Add(balancingRule);
}
return list;
}
public BalancingRulesFile BuildBalancingRulesFile(List<BalancingRule> rules)
{
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
BalancingRulesFile balancingRulesFile = new BalancingRulesFile
{
Rules = new List<BalancingRuleSerializable>()
};
foreach (BalancingRule rule in rules)
{
BalancingRuleSerializable obj = new BalancingRuleSerializable
{
Id = (rule.id ?? null),
EnemyID = (rule.enemyID ?? null),
EnemyName = (rule.enemyName ?? ""),
AreaFamilyName = (rule.areaFamily?.FamilyName ?? "")
};
ref AreaEnum? area = ref rule.area;
object obj2;
if (!area.HasValue)
{
obj2 = null;
}
else
{
AreaEnum valueOrDefault = area.GetValueOrDefault();
obj2 = ((object)(AreaEnum)(ref valueOrDefault)).ToString();
}
if (obj2 == null)
{
obj2 = "";
}
obj.AreaName = (string)obj2;
ref Factions? faction = ref rule.faction;
object obj3;
if (!faction.HasValue)
{
obj3 = null;
}
else
{
Factions valueOrDefault2 = faction.GetValueOrDefault();
obj3 = ((object)(Factions)(ref valueOrDefault2)).ToString();
}
if (obj3 == null)
{
obj3 = "";
}
obj.FactionName = (string)obj3;
obj.ExceptIds = rule.exceptIds ?? null;
obj.ExceptNames = rule.exceptNames ?? null;
obj.IsBoss = rule.isBoss;
obj.IsBossPawn = rule.isBossPawn;
obj.IsStoryBoss = rule.isStoryBoss;
obj.IsUniqueArenaBoss = rule.isUniqueArenaBoss;
obj.IsUniqueEnemy = rule.isUniqueEnemy;
obj.StatModifications = new List<StatModificationSerializable>();
BalancingRuleSerializable balancingRuleSerializable = obj;
foreach (KeyValuePair<string, float?> statModification in rule.statModifications)
{
if (statModification.Value.HasValue)
{
balancingRuleSerializable.StatModifications.Add(new StatModificationSerializable
{
StatName = statModification.Key,
Value = statModification.Value.Value
});
}
}
balancingRulesFile.Rules.Add(balancingRuleSerializable);
}
return balancingRulesFile;
}
public void LoadFactionRules(string path)
{
try
{
if (!File.Exists(path))
{
OutwardEnemiesBalancer.LogSL("FactionRules file not found at: " + path);
return;
}
BalancingRulesFile balancingRulesFile = Load(path);
if (balancingRulesFile != null && balancingRulesFile.FactionRules != null)
{
List<FactionRule> factionRules = GetFactionRules(balancingRulesFile);
FactionRuleRegistryManager.Instance.AppendFactionRules(factionRules);
}
}
catch (Exception ex)
{
OutwardEnemiesBalancer.LogSL("LoadFactionRules failed loading '" + path + "': " + ex.Message);
}
}
public List<FactionRule> GetFactionRules(BalancingRulesFile file)
{
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
List<FactionRule> list = new List<FactionRule>();
if (file.FactionRules == null)
{
return list;
}
foreach (FactionRuleSerializable factionRule2 in file.FactionRules)
{
FactionRule factionRule = new FactionRule(factionRule2.Id);
factionRule.enemyID = (string.IsNullOrEmpty(factionRule2.EnemyID) ? null : factionRule2.EnemyID);
factionRule.enemyName = factionRule2.EnemyName ?? "";
factionRule.areaFamily = AreaFamiliesHelpers.GetAreaFamilyByName(factionRule2.AreaFamilyName);
factionRule.area = AreaHelpers.GetAreaEnumFromAreaName(factionRule2.AreaName);
if (!string.IsNullOrEmpty(factionRule2.TargetFactionName) && Enum.TryParse<Factions>(factionRule2.TargetFactionName, out Factions result))
{
factionRule.targetFaction = result;
}
if (!string.IsNullOrEmpty(factionRule2.NewFactionName) && Enum.TryParse<Factions>(factionRule2.NewFactionName, out Factions result2))
{
factionRule.newFaction = result2;
}
factionRule.exceptIds = factionRule2.ExceptIds;
factionRule.exceptNames = factionRule2.ExceptNames;
factionRule.isBoss = factionRule2.IsBoss;
factionRule.isBossPawn = factionRule2.IsBossPawn;
factionRule.isStoryBoss = factionRule2.IsStoryBoss;
factionRule.isUniqueArenaBoss = factionRule2.IsUniqueArenaBoss;
factionRule.isUniqueEnemy = factionRule2.IsUniqueEnemy;
list.Add(factionRule);
}
return list;
}
public void SaveFactionRulesToXml(string filePath, List<FactionRule> rules)
{
try
{
BalancingRulesFile o = BuildFactionRulesFile(rules);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(BalancingRulesFile));
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
NewLineOnAttributes = false
};
using XmlWriter xmlWriter = XmlWriter.Create(filePath, settings);
xmlSerializer.Serialize(xmlWriter, o);
}
catch (Exception ex)
{
OutwardEnemiesBalancer.LogSL("SaveFactionRulesToXml failed saving '" + filePath + "': " + ex.Message);
}
}
public BalancingRulesFile BuildFactionRulesFile(List<FactionRule> rules)
{
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
BalancingRulesFile balancingRulesFile = new BalancingRulesFile
{
FactionRules = new List<FactionRuleSerializable>()
};
foreach (FactionRule rule in rules)
{
FactionRuleSerializable obj = new FactionRuleSerializable
{
Id = (rule.id ?? null),
EnemyID = (rule.enemyID ?? null),
EnemyName = (rule.enemyName ?? ""),
AreaFamilyName = (rule.areaFamily?.FamilyName ?? "")
};
ref AreaEnum? area = ref rule.area;
object obj2;
if (!area.HasValue)
{
obj2 = null;
}
else
{
AreaEnum valueOrDefault = area.GetValueOrDefault();
obj2 = ((object)(AreaEnum)(ref valueOrDefault)).ToString();
}
if (obj2 == null)
{
obj2 = "";
}
obj.AreaName = (string)obj2;
ref Factions? targetFaction = ref rule.targetFaction;
object obj3;
if (!targetFaction.HasValue)
{
obj3 = null;
}
else
{
Factions valueOrDefault2 = targetFaction.GetValueOrDefault();
obj3 = ((object)(Factions)(ref valueOrDefault2)).ToString();
}
if (obj3 == null)
{
obj3 = "";
}
obj.TargetFactionName = (string)obj3;
obj.NewFactionName = ((object)(Factions)(ref rule.newFaction)).ToString();
obj.ExceptIds = rule.exceptIds ?? null;
obj.ExceptNames = rule.exceptNames ?? null;
obj.IsBoss = rule.isBoss;
obj.IsBossPawn = rule.isBossPawn;
obj.IsStoryBoss = rule.isStoryBoss;
obj.IsUniqueArenaBoss = rule.isUniqueArenaBoss;
obj.IsUniqueEnemy = rule.isUniqueEnemy;
FactionRuleSerializable item = obj;
balancingRulesFile.FactionRules.Add(item);
}
return balancingRulesFile;
}
}
public class BossRegistryManager
{
private static BossRegistryManager _instance;
private readonly Dictionary<string, BossID> bossLookup = new Dictionary<string, BossID>(StringComparer.Ordinal);
public static BossRegistryManager Instance
{
get
{
if (_instance == null)
{
_instance = new BossRegistryManager();
}
return _instance;
}
}
private BossRegistryManager()
{
RegisterEnum(StoryBossesHelper.Enemies, BossCategories.Story);
RegisterEnum(UniqueArenaBossesHelper.Enemies, BossCategories.Arena);
RegisterEnum(BossPawnsHelper.Enemies, BossCategories.Pawn);
}
private void RegisterEnum<T>(Dictionary<T, EnemyIdentificationGroupData> mapping, BossCategories category) where T : Enum
{
foreach (KeyValuePair<T, EnemyIdentificationGroupData> item in mapping)
{
T key = item.Key;
EnemyIdentificationGroupData value = item.Value;
BossID value2 = new BossID(category, value, key);
foreach (EnemyIdentificationData enemy in value.Enemies)
{
if (!string.IsNullOrWhiteSpace(enemy.ID))
{
bossLookup[GetIdentificatorFromEnemyIdentification(enemy)] = value2;
}
}
}
}
public bool TryGetBoss(string key, out BossID boss)
{
return bossLookup.TryGetValue(key, out boss);
}
public bool IsBoss(Character character)
{
return bossLookup.ContainsKey(GetEnemyBossIdentificator(character));
}
public bool IsBossOfCategory(string key, BossCategories category)
{
if (TryGetBoss(key, out var boss))
{
return boss.Category == category;
}
return false;
}
public bool IsBossOfCategory(Character character, BossCategories category)
{
if (TryGetBoss(GetEnemyBossIdentificator(character), out var boss))
{
return boss.Category == category;
}
return false;
}
public IEnumerable<BossID> GetBossesOfCategory(BossCategories category)
{
return bossLookup.Values.Where((BossID b) => b.Category == category);
}
public static string GetEnemyBossIdentificator(Character character)
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
Area currentArea = AreaManager.Instance.CurrentArea;
string text = ((currentArea != null) ? currentArea.GetName() : null);
UID uID;
if (string.IsNullOrEmpty(text))
{
uID = character.UID;
return ((UID)(ref uID)).Value;
}
uID = character.UID;
return ((UID)(ref uID)).Value + "_" + FixAreaNameForCode(text);
}
public static string GetIdentificatorFromEnemyIdentification(EnemyIdentificationData enemy)
{
return enemy.ID + "_" + FixAreaNameForCode(enemy.GameLocation);
}
public static string FixAreaNameForCode(string name)
{
return name.Trim().Replace(' ', '_');
}
}
public class CharacterBalancerManager
{
private static CharacterBalancerManager _instance;
private static MethodInfo _getStatMethod;
public static CharacterBalancerManager Instance
{
get
{
if (_instance == null)
{
_instance = new CharacterBalancerManager();
}
return _instance;
}
}
private CharacterBalancerManager()
{
}
public void ApplyBalancingRules()
{
CharacterAI[] array = Object.FindObjectsOfType<CharacterAI>();
for (int i = 0; i < array.Length; i++)
{
Character character = ((CharacterControl)array[i]).Character;
if ((Object)(object)character == (Object)null || !character.Alive)
{
continue;
}
List<BalancingRule> matchingRules = BalancingRuleRegistryManager.Instance.GetMatchingRules(character);
if (matchingRules.Count <= 0)
{
continue;
}
foreach (BalancingRule item in matchingRules)
{
ApplyRuleToCharacter(character, item);
}
}
}
public void ApplyBalancingRule(BalancingRule rule)
{
CharacterAI[] array = Object.FindObjectsOfType<CharacterAI>();
for (int i = 0; i < array.Length; i++)
{
Character character = ((CharacterControl)array[i]).Character;
if (!((Object)(object)character == (Object)null) && character.Alive && rule.Matches(character))
{
ApplyRuleToCharacter(character, rule);
}
}
}
private int ApplyRuleToCharacter(Character character, BalancingRule rule)
{
if ((Object)(object)character.Stats == (Object)null || rule.statModifications == null || rule.statModifications.Count == 0)
{
return 0;
}
List<StatModification> list = StatModificationBuilder.BuildFromDictionary(rule.statModifications, rule.modifierType);
int num = 0;
foreach (StatModification item in list)
{
ApplyStatModification(character, item);
num++;
}
return num;
}
private void ApplyStatModification(Character character, StatModification mod)
{
float statValue = GetStatValue(character.Stats, mod.StatType);
float num = ApplyModification(statValue, mod);
if (mod.MinClamp.HasValue)
{
num = Mathf.Max(num, mod.MinClamp.Value);
}
if (mod.MaxClamp.HasValue)
{
num = Mathf.Min(num, mod.MaxClamp.Value);
}
SetStatValue(character, mod.StatType, num);
}
private float ApplyModification(float currentValue, StatModification mod)
{
return mod.ModifierType switch
{
ValueModifierType.Direct => mod.Value,
ValueModifierType.Scale => currentValue * mod.Value,
ValueModifierType.Add => currentValue + mod.Value,
_ => currentValue,
};
}
private StatType? MapToGameStatType(EnemyBalanceStatType statType)
{
return statType switch
{
EnemyBalanceStatType.MaxHealth => (StatType)0,
EnemyBalanceStatType.MaxStamina => (StatType)3,
EnemyBalanceStatType.MaxMana => (StatType)8,
EnemyBalanceStatType.HealthRegen => (StatType)1,
EnemyBalanceStatType.StaminaRegen => (StatType)4,
EnemyBalanceStatType.ManaRegen => (StatType)9,
EnemyBalanceStatType.Impact => (StatType)12,
EnemyBalanceStatType.ImpactResistance => (StatType)43,
EnemyBalanceStatType.MovementSpeed => (StatType)54,
EnemyBalanceStatType.AttackSpeed => (StatType)56,
EnemyBalanceStatType.SkillCooldownModifier => (StatType)68,
EnemyBalanceStatType.DodgeInvulnerabilityModifier => (StatType)57,
EnemyBalanceStatType.GlobalStatusResistance => (StatType)45,
EnemyBalanceStatType.ColdProtection => (StatType)47,
EnemyBalanceStatType.HeatProtection => (StatType)48,
EnemyBalanceStatType.ColdRegenRate => (StatType)49,
EnemyBalanceStatType.HeatRegenRate => (StatType)50,
EnemyBalanceStatType.CorruptionProtection => (StatType)52,
EnemyBalanceStatType.Waterproof => (StatType)51,
_ => null,
};
}
private Stat GetGameStat(CharacterStats stats, EnemyBalanceStatType statType)
{
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Expected O, but got Unknown
StatType? val = MapToGameStatType(statType);
if (!val.HasValue)
{
return null;
}
if (_getStatMethod == null)
{
_getStatMethod = typeof(CharacterStats).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetStat" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(StatType));
}
if (_getStatMethod != null)
{
return (Stat)_getStatMethod.Invoke(stats, new object[1] { val.Value });
}
return null;
}
private float GetStatValue(CharacterStats stats, EnemyBalanceStatType statType)
{
switch (statType)
{
case EnemyBalanceStatType.MaxHealth:
return stats.BaseMaxHealth;
case EnemyBalanceStatType.MaxStamina:
return stats.MaxStamina;
case EnemyBalanceStatType.MaxMana:
return stats.BaseMaxMana;
case EnemyBalanceStatType.HealthRegen:
return stats.HealthRegen;
case EnemyBalanceStatType.StaminaRegen:
return stats.StaminaRegen;
case EnemyBalanceStatType.ManaRegen:
return stats.ManaRegen;
case EnemyBalanceStatType.Impact:
return stats.m_impactModifier.CurrentValue;
case EnemyBalanceStatType.ImpactResistance:
return stats.GetImpactResistance();
case EnemyBalanceStatType.MovementSpeed:
return stats.MovementSpeed;
case EnemyBalanceStatType.PhysicalDamage:
return stats.m_damageTypesModifier[0].CurrentValue;
case EnemyBalanceStatType.EtherealDamage:
return stats.m_damageTypesModifier[1].CurrentValue;
case EnemyBalanceStatType.DecayDamage:
return stats.m_damageTypesModifier[2].CurrentValue;
case EnemyBalanceStatType.ElectricDamage:
return stats.m_damageTypesModifier[3].CurrentValue;
case EnemyBalanceStatType.FrostDamage:
return stats.m_damageTypesModifier[4].CurrentValue;
case EnemyBalanceStatType.FireDamage:
return stats.m_damageTypesModifier[5].CurrentValue;
case EnemyBalanceStatType.PhysicalResistance:
return stats.m_damageResistance[0].CurrentValue;
case EnemyBalanceStatType.EtherealResistance:
return stats.m_damageResistance[1].CurrentValue;
case EnemyBalanceStatType.DecayResistance:
return stats.m_damageResistance[2].CurrentValue;
case EnemyBalanceStatType.ElectricResistance:
return stats.m_damageResistance[3].CurrentValue;
case EnemyBalanceStatType.FrostResistance:
return stats.m_damageResistance[4].CurrentValue;
case EnemyBalanceStatType.FireResistance:
return stats.m_damageResistance[5].CurrentValue;
case EnemyBalanceStatType.PhysicalProtection:
return stats.m_damageProtection[0].CurrentValue;
case EnemyBalanceStatType.EtherealProtection:
return stats.m_damageProtection[1].CurrentValue;
case EnemyBalanceStatType.DecayProtection:
return stats.m_damageProtection[2].CurrentValue;
case EnemyBalanceStatType.ElectricProtection:
return stats.m_damageProtection[3].CurrentValue;
case EnemyBalanceStatType.FrostProtection:
return stats.m_damageProtection[4].CurrentValue;
case EnemyBalanceStatType.FireProtection:
return stats.m_damageProtection[5].CurrentValue;
default:
{
Stat gameStat = GetGameStat(stats, statType);
return (gameStat != null) ? gameStat.CurrentValue : 0f;
}
}
}
private void SetStatValue(Character character, EnemyBalanceStatType statType, float value)
{
CharacterStats stats = character.Stats;
switch (statType)
{
case EnemyBalanceStatType.MaxHealth:
{
float num = Mathf.Max(value, 1f);
float health = character.Health;
float num2 = ((stats.BaseMaxHealth > 0f) ? (health / stats.BaseMaxHealth) : 1f);
num2 = Mathf.Clamp(num2, 0f, 1f);
stats.RefreshVitalMaxStat(false);
stats.m_maxHealthStat.BaseValue = num;
stats.RefreshVitalMaxStat(false);
stats.SetHealth(num2 * num);
break;
}
case EnemyBalanceStatType.MaxStamina:
{
float num5 = Mathf.Max(value, 1f);
float stamina = character.Stamina;
float num6 = Mathf.Clamp((stats.MaxStamina > 0f) ? (stamina / stats.MaxStamina) : 1f, 0f, 1f);
stats.RefreshVitalMaxStat(false);
stats.m_maxStamina.BaseValue = num5;
stats.RefreshVitalMaxStat(false);
float num7 = num6 * num5 - stamina;
if (Mathf.Abs(num7) > 0.01f)
{
stats.AffectStamina(num7);
}
break;
}
case EnemyBalanceStatType.MaxMana:
{
float num3 = Mathf.Max(value, 1f);
float currentMana = stats.CurrentMana;
float num4 = ((stats.BaseMaxMana > 0f) ? (currentMana / stats.BaseMaxMana) : 1f);
num4 = Mathf.Clamp(num4, 0f, 1f);
stats.RefreshVitalMaxStat(false);
stats.m_maxManaStat.BaseValue = num3;
stats.RefreshVitalMaxStat(false);
stats.SetMana(num4 * num3);
break;
}
case EnemyBalanceStatType.HealthRegen:
case EnemyBalanceStatType.StaminaRegen:
case EnemyBalanceStatType.ManaRegen:
case EnemyBalanceStatType.ColdProtection:
case EnemyBalanceStatType.HeatProtection:
case EnemyBalanceStatType.ColdRegenRate:
case EnemyBalanceStatType.HeatRegenRate:
case EnemyBalanceStatType.CorruptionProtection:
case EnemyBalanceStatType.Waterproof:
case EnemyBalanceStatType.Impact:
case EnemyBalanceStatType.ImpactResistance:
case EnemyBalanceStatType.MovementSpeed:
case EnemyBalanceStatType.AttackSpeed:
case EnemyBalanceStatType.SkillCooldownModifier:
case EnemyBalanceStatType.DodgeInvulnerabilityModifier:
case EnemyBalanceStatType.GlobalStatusResistance:
{
Stat gameStat = GetGameStat(stats, statType);
if (gameStat != null)
{
gameStat.BaseValue = value;
gameStat.Update();
}
break;
}
case EnemyBalanceStatType.PhysicalDamage:
stats.m_damageTypesModifier[0].BaseValue = value;
stats.m_damageTypesModifier[0].Update();
break;
case EnemyBalanceStatType.EtherealDamage:
stats.m_damageTypesModifier[1].BaseValue = value;
stats.m_damageTypesModifier[1].Update();
break;
case EnemyBalanceStatType.DecayDamage:
stats.m_damageTypesModifier[2].BaseValue = value;
stats.m_damageTypesModifier[2].Update();
break;
case EnemyBalanceStatType.ElectricDamage:
stats.m_damageTypesModifier[3].BaseValue = value;
stats.m_damageTypesModifier[3].Update();
break;
case EnemyBalanceStatType.FrostDamage:
stats.m_damageTypesModifier[4].BaseValue = value;
stats.m_damageTypesModifier[4].Update();
break;
case EnemyBalanceStatType.FireDamage:
stats.m_damageTypesModifier[5].BaseValue = value;
stats.m_damageTypesModifier[5].Update();
break;
case EnemyBalanceStatType.PhysicalResistance:
stats.m_damageResistance[0].BaseValue = value;
stats.m_damageResistance[0].Update();
break;
case EnemyBalanceStatType.EtherealResistance:
stats.m_damageResistance[1].BaseValue = value;
stats.m_damageResistance[1].Update();
break;
case EnemyBalanceStatType.DecayResistance:
stats.m_damageResistance[2].BaseValue = value;
stats.m_damageResistance[2].Update();
break;
case EnemyBalanceStatType.ElectricResistance:
stats.m_damageResistance[3].BaseValue = value;
stats.m_damageResistance[3].Update();
break;
case EnemyBalanceStatType.FrostResistance:
stats.m_damageResistance[4].BaseValue = value;
stats.m_damageResistance[4].Update();
break;
case EnemyBalanceStatType.FireResistance:
stats.m_damageResistance[5].BaseValue = value;
stats.m_damageResistance[5].Update();
break;
case EnemyBalanceStatType.PhysicalProtection:
stats.m_damageProtection[0].BaseValue = value;
stats.m_damageProtection[0].Update();
break;
case EnemyBalanceStatType.EtherealProtection:
stats.m_damageProtection[1].BaseValue = value;
stats.m_damageProtection[1].Update();
break;
case EnemyBalanceStatType.DecayProtection:
stats.m_damageProtection[2].BaseValue = value;
stats.m_damageProtection[2].Update();
break;
case EnemyBalanceStatType.ElectricProtection:
stats.m_damageProtection[3].BaseValue = value;
stats.m_damageProtection[3].Update();
break;
case EnemyBalanceStatType.FrostProtection:
stats.m_damageProtection[4].BaseValue = value;
stats.m_damageProtection[4].Update();
break;
case EnemyBalanceStatType.FireProtection:
stats.m_damageProtection[5].BaseValue = value;
stats.m_damageProtection[5].Update();
break;
}
if (statType == EnemyBalanceStatType.HealthRegen || statType == EnemyBalanceStatType.StaminaRegen || statType == EnemyBalanceStatType.ManaRegen || statType == EnemyBalanceStatType.ColdProtection || statType == EnemyBalanceStatType.HeatProtection || statType == EnemyBalanceStatType.ColdRegenRate || statType == EnemyBalanceStatType.HeatRegenRate || statType == EnemyBalanceStatType.CorruptionProtection || statType == EnemyBalanceStatType.Waterproof || statType == EnemyBalanceStatType.ImpactResistance)
{
stats.UpdateStats();
}
if (statType == EnemyBalanceStatType.PhysicalResistance || statType == EnemyBalanceStatType.EtherealResistance || statType == EnemyBalanceStatType.DecayResistance || statType == EnemyBalanceStatType.ElectricResistance || statType == EnemyBalanceStatType.FrostResistance || statType == EnemyBalanceStatType.FireResistance || statType == EnemyBalanceStatType.PhysicalProtection || statType == EnemyBalanceStatType.EtherealProtection || statType == EnemyBalanceStatType.DecayProtection || statType == EnemyBalanceStatType.ElectricProtection || statType == EnemyBalanceStatType.FrostProtection || statType == EnemyBalanceStatType.FireProtection)
{
stats.UpdateStats();
}
}
}
public class FactionBalancerManager
{
private static FactionBalancerManager _instance;
public static FactionBalancerManager Instance
{
get
{
if (_instance == null)
{
_instance = new FactionBalancerManager();
}
return _instance;
}
}
private FactionBalancerManager()
{
}
public void ApplyFactionRules()
{
CharacterAI[] array = Object.FindObjectsOfType<CharacterAI>();
for (int i = 0; i < array.Length; i++)
{
Character character = ((CharacterControl)array[i]).Character;
if ((Object)(object)character == (Object)null || !character.Alive)
{
continue;
}
List<FactionRule> matchingRules = FactionRuleRegistryManager.Instance.GetMatchingRules(character);
if (matchingRules.Count <= 0)
{
continue;
}
foreach (FactionRule item in matchingRules)
{
ApplyRuleToCharacter(character, item);
}
}
}
public void ApplyFactionRule(FactionRule rule)
{
CharacterAI[] array = Object.FindObjectsOfType<CharacterAI>();
for (int i = 0; i < array.Length; i++)
{
Character character = ((CharacterControl)array[i]).Character;
if (!((Object)(object)character == (Object)null) && character.Alive && rule.Matches(character))
{
ApplyRuleToCharacter(character, rule);
}
}
}
private void ApplyRuleToCharacter(Character character, FactionRule rule)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)character == (Object)null) && (int)rule.newFaction != 0)
{
_ = character.Faction;
character.ChangeFaction(rule.newFaction, true);
}
}
}
public class FactionRuleRegistryManager
{
private static FactionRuleRegistryManager _instance;
public List<FactionRule> factionRules = new List<FactionRule>();
public static FactionRuleRegistryManager Instance
{
get
{
if (_instance == null)
{
_instance = new FactionRuleRegistryManager();
}
return _instance;
}
}
private FactionRuleRegistryManager()
{
}
public void AppendFactionRules(List<FactionRule> rules)
{
foreach (FactionRule rule in rules)
{
AppendFactionRule(rule);
}
}
public void AppendFactionRule(FactionRule rule)
{
FactionRule factionRule = factionRules.FirstOrDefault((FactionRule r) => r.id == rule.id);
if (factionRule != null)
{
OutwardEnemiesBalancer.LogSL("FactionRule '" + rule.id + "' already exists. Replacing.");
factionRules.Remove(factionRule);
}
factionRules.Add(rule);
EventBusPublisher.SendAppendFactionRule(rule.id);
}
public void RemoveFactionRuleById(string id)
{
foreach (FactionRule item in factionRules.Where((FactionRule rule) => rule.id == id).ToList())
{
RemoveFactionRule(item);
}
}
public void RemoveFactionRule(FactionRule rule)
{
factionRules.Remove(rule);
EventBusPublisher.SendRemoveFactionRule(rule.id);
}
public List<FactionRule> GetMatchingRules(Character character)
{
List<FactionRule> list = new List<FactionRule>();
foreach (FactionRule factionRule in factionRules)
{
if (factionRule.Matches(character))
{
list.Add(factionRule);
}
}
return list;
}
public void Clear()
{
factionRules.Clear();
}
}
public static class PathsManager
{
public const string ConfigDirectoryName = "Enemies_Balancer";
public static readonly string ConfigPath;
public static readonly string DefaultBalanceRulesPath;
public static void Initialize()
{
if (!Directory.Exists(ConfigPath))
{
Directory.CreateDirectory(ConfigPath);
}
}
static PathsManager()
{
ConfigPath = Path.Combine(PathsManager.ConfigPath, "Enemies_Balancer");
DefaultBalanceRulesPath = Path.Combine(ConfigPath, "BalanceRules.xml");
Initialize();
}
}
}
namespace OutwardEnemiesBalancer.Events
{
public static class EventBusPublisher
{
public const string Event_BalanceRuleAppended = "BalanceRuleAppended";
public const string Event_BalanceRuleRemoved = "BalanceRuleRemoved";
public const string Event_FactionRuleAppended = "FactionRuleAppended";
public const string Event_FactionRuleRemoved = "FactionRuleRemoved";
public static void SendAppendBalancingRule(string ruleId)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Expected O, but got Unknown
EventPayload val = new EventPayload();
val.Set("balanceRuleId", (object)ruleId);
EventBus.Publish("gymmed.enemies_balancer", "BalanceRuleAppended", val);
}
public static void SendRemoveBalancingRule(string ruleId)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Expected O, but got Unknown
EventPayload val = new EventPayload();
val.Set("balanceRuleId", (object)ruleId);
EventBus.Publish("gymmed.enemies_balancer", "BalanceRuleRemoved", val);
}
public static void SendAppendFactionRule(string ruleId)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Expected O, but got Unknown
EventPayload val = new EventPayload();
val.Set("factionRuleId", (object)ruleId);
EventBus.Publish("gymmed.enemies_balancer", "FactionRuleAppended", val);
}
public static void SendRemoveFactionRule(string ruleId)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Expected O, but got Unknown
EventPayload val = new EventPayload();
val.Set("factionRuleId", (object)ruleId);
EventBus.Publish("gymmed.enemies_balancer", "FactionRuleRemoved", val);
}
}
public static class EventBusRegister
{
private static readonly (string key, Type type, string description)[] TargetingParams = new(string, Type, string)[5]
{
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.EnemyId),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.EnemyName),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.AreaFamily),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.Faction),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.AreaEnum)
};
private static readonly (string key, Type type, string description)[] UniqueEnemyParams = new(string, Type, string)[5]
{
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.IsForBosses),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.IsForBossesPawns),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.IsForStoryBosses),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.IsForUniqueArenaBosses),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.IsForUniqueEnemies)
};
private static readonly (string key, Type type, string description)[] StatModParams = new(string, Type, string)[4]
{
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.StatModifications),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ModifierType),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.StatType),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.Value)
};
private static readonly (string key, Type type, string description)[] ExceptionsParams = new(string, Type, string)[2]
{
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ExceptIds),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ExceptNames)
};
private static readonly (string key, Type type, string description)[] VitalStatsParams = new(string, Type, string)[7]
{
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.MaxHealth),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.MaxStamina),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.MaxMana),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.HealthRegen),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.StaminaRegen),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ManaRegen),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ModifierType)
};
private static readonly (string key, Type type, string description)[] EnvironmentalStatsParams = new(string, Type, string)[5]
{
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ColdProtection),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.HeatProtection),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.CorruptionResistance),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.Waterproof),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ModifierType)
};
private static readonly (string key, Type type, string description)[] CombatStatsParams = new(string, Type, string)[5]
{
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.Impact),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ImpactResistance),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.MovementSpeed),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.AttackSpeed),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ModifierType)
};
private static readonly (string key, Type type, string description)[] FactionRuleParams = new(string, Type, string)[3]
{
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.NewFaction),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ExceptIds),
EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ExceptNames)
};
public static void RegisterEvents()
{
EventBus.RegisterEvent("gymmed.enemies_balancer", "BalanceRuleAppended", "Published when a balancing rule is appended.", new(string, Type, string)[1] { EnemyBalanceParamsHelper.Get(EnemyBalanceParams.BalanceRuleId) });
EventBus.RegisterEvent("gymmed.enemies_balancer", "BalanceRuleRemoved", "Published when a balancing rule is removed.", new(string, Type, string)[1] { EnemyBalanceParamsHelper.Get(EnemyBalanceParams.BalanceRuleId) });
EventBus.RegisterEvent("gymmed.enemies_balancer_*", "AddBalanceRule", "Add a full balancing rule with all targeting options and stat modifications.", EnemyBalanceParamsHelper.Combine(EnemyBalanceParamsHelper.Get(EnemyBalanceParams.BalanceRuleId), TargetingParams, UniqueEnemyParams, StatModParams, ExceptionsParams));
EventBus.RegisterEvent("gymmed.enemies_balancer_*", "AddBalanceRuleByEnemyName", "Add balancing rule targeting by enemy name.", EnemyBalanceParamsHelper.Combine(EnemyBalanceParamsHelper.Get(EnemyBalanceParams.BalanceRuleId), EnemyBalanceParamsHelper.Get(EnemyBalanceParams.EnemyName), StatModParams, EnemyBalanceParamsHelper.Get(EnemyBalanceParams.ExceptIds)));
EventBus.RegisterEvent("gymmed.enemies_balancer_*", "AddBalanceRuleByEnemyId", "Add balancing rule targeting by enem