Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of WarfareTweaks v1.0.1
WarfareTweaks.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using LocalizationManager; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("WarfareTweaks")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("WarfareTweaks")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("2D0FD9F5-0C44-4B03-B27D-E60BB6C0A7B7")] [assembly: AssemblyFileVersion("1.0.1")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LocalizationManager { internal static class Localizer { private static readonly string[] FileExtensions; private static BaseUnityPlugin? _plugin; private static BaseUnityPlugin Plugin => _plugin ?? throw new InvalidOperationException("Localization was used before the plugin was registered."); internal static void Load(BaseUnityPlugin plugin) { _plugin = plugin ?? throw new ArgumentNullException("plugin"); if (Localization.instance != null) { LoadLocalization(Localization.instance, Localization.instance.GetSelectedLanguage()); } } private static void LoadLocalizationLater() { if (Localization.instance != null) { LoadLocalization(Localization.instance, Localization.instance.GetSelectedLanguage()); } } private static void LoadLocalization(Localization __instance, string language) { Dictionary<string, string> dictionary = FindLocalizationFiles(); byte[] array = LoadTranslationFromAssembly("English"); if (array == null) { Debug.LogWarning((object)("Found no English localizations in mod " + Plugin.Info.Metadata.Name + ". Expected an embedded resource translations/English.json or translations/English.yml.")); return; } Dictionary<string, string> dictionary2 = DeserializeLocalizationText(Encoding.UTF8.GetString(array), "embedded English localization"); if (dictionary2.Count == 0) { Debug.LogWarning((object)("Localization for mod " + Plugin.Info.Metadata.Name + " failed: English localization file was empty or invalid.")); return; } string text = null; if (language != "English") { if (dictionary.TryGetValue(language, out var value)) { text = File.ReadAllText(value); } else { byte[] array2 = LoadTranslationFromAssembly(language); if (array2 != null) { text = Encoding.UTF8.GetString(array2); } } } if (text == null && dictionary.TryGetValue("English", out var value2)) { text = File.ReadAllText(value2); } if (text != null) { foreach (KeyValuePair<string, string> item in DeserializeLocalizationText(text, language + " localization")) { dictionary2[item.Key] = item.Value; } } foreach (KeyValuePair<string, string> item2 in dictionary2) { __instance.AddWord(item2.Key, item2.Value); } } private static Dictionary<string, string> DeserializeLocalizationText(string localizationData, string sourceDescription) { try { return new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(localizationData) ?? new Dictionary<string, string>(); } catch (Exception ex) { Debug.LogError((object)("Failed to parse " + sourceDescription + " for mod " + Plugin.Info.Metadata.Name + ": " + ex.Message)); return new Dictionary<string, string>(); } } private static Dictionary<string, string> FindLocalizationFiles() { Dictionary<string, string> dictionary = new Dictionary<string, string>(); string pluginPath = Paths.PluginPath; if (!Directory.Exists(pluginPath)) { return dictionary; } foreach (string item in from file in Directory.GetFiles(pluginPath, Plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories) where FileExtensions.Contains<string>(Path.GetExtension(file)) select file) { string[] array = Path.GetFileNameWithoutExtension(item).Split('.'); if (array.Length >= 2) { string text = array[1]; if (dictionary.ContainsKey(text)) { Debug.LogWarning((object)("Duplicate key " + text + " found for " + Plugin.Info.Metadata.Name + ". The duplicate file found at " + item + " will be skipped.")); } else { dictionary[text] = item; } } } return dictionary; } static Localizer() { //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) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown FileExtensions = new string[2] { ".json", ".yml" }; Harmony val = new Harmony("org.bepinex.helpers.LocalizationManager"); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalization", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static byte[]? LoadTranslationFromAssembly(string language) { string[] fileExtensions = FileExtensions; foreach (string text in fileExtensions) { byte[] array = ReadEmbeddedFileBytes("translations." + language + text); if (array != null) { return array; } } return null; } private static byte[]? ReadEmbeddedFileBytes(string resourceFileName) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); using MemoryStream memoryStream = new MemoryStream(); string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith(resourceFileName, StringComparison.Ordinal)); if (text != null) { executingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream); } return (memoryStream.Length == 0L) ? null : memoryStream.ToArray(); } } } namespace WarfareTweaks { internal static class ChainLightningDedupSystem { internal readonly struct ChainUpdateScope { internal ChainLightningActivation? PreviousActivation { get; } internal ChainUpdateScope(ChainLightningActivation? previousActivation) { PreviousActivation = previousActivation; } } internal sealed class ChainLightningActivation { public HashSet<int> HitTargetIds { get; } = new HashSet<int>(); } private sealed class ChainLightningAoeState { internal static readonly ChainLightningAoeState NotChainLightning = new ChainLightningAoeState(isChainLightning: false); public bool IsChainLightning { get; } public ChainLightningActivation? Activation { get; set; } public ChainLightningAoeState(bool isChainLightning) { IsChainLightning = isChainLightning; } } private const float VanillaLightningDamage = 75f; private const float VanillaChainChancePerTarget = 0.3f; private static readonly ConditionalWeakTable<Aoe, ChainLightningAoeState> AoeStates = new ConditionalWeakTable<Aoe, ChainLightningAoeState>(); private static bool _loggedVanillaRestore; [ThreadStatic] private static ChainLightningActivation? ActiveActivation; internal static void RestoreVanillaChainLightningBehavior(ZNetScene scene) { GameObject val = (((Object)(object)scene != (Object)null) ? scene.GetPrefab("ChainLightning") : null); Aoe val2 = (((Object)(object)val != (Object)null) ? (val.GetComponent<Aoe>() ?? val.GetComponentInChildren<Aoe>(true)) : null); if (!((Object)(object)val2 == (Object)null)) { bool num = !Mathf.Approximately(val2.m_damage.m_lightning, 75f) || !Mathf.Approximately(val2.m_chainChancePerTarget, 0.3f); val2.m_damage.m_lightning = 75f; val2.m_chainChancePerTarget = 0.3f; if (num && !_loggedVanillaRestore) { _loggedVanillaRestore = true; WarfareTweaksPlugin.ModLogger.LogInfo((object)"Restored vanilla ChainLightning damage and chain target chance after WarfareFireAndIce patch."); } } } internal static void TrackSetup(Aoe aoe) { if (!((Object)(object)aoe == (Object)null) && TryGetChainLightningState(aoe, out ChainLightningAoeState state)) { ChainLightningActivation activation = ActiveActivation ?? new ChainLightningActivation(); state.Activation = activation; } } internal static ChainUpdateScope BeginChainUpdate(Aoe aoe) { ChainLightningActivation? activeActivation = ActiveActivation; if ((Object)(object)aoe != (Object)null && TryGetChainLightningState(aoe, out ChainLightningAoeState state)) { ActiveActivation = EnsureActivation(state); } return new ChainUpdateScope(activeActivation); } internal static void EndChainUpdate(ChainUpdateScope scope) { ActiveActivation = scope.PreviousActivation; } internal static bool ShouldAllowChainLightningHit(Aoe aoe, Collider collider) { if ((Object)(object)aoe == (Object)null || (Object)(object)collider == (Object)null) { return true; } if (!TryGetChainLightningState(aoe, out ChainLightningAoeState state)) { return true; } Character val = TryGetHitCharacter(collider); if ((Object)(object)val == (Object)null) { return true; } int instanceID = ((Object)val).GetInstanceID(); ChainLightningActivation chainLightningActivation = EnsureActivation(state); if (chainLightningActivation.HitTargetIds.Contains(instanceID)) { return false; } chainLightningActivation.HitTargetIds.Add(instanceID); return true; } internal static void FilterChainLightningCandidate(Aoe aoe, Collider collider, ref bool result) { if (!result || (Object)(object)aoe == (Object)null || (Object)(object)collider == (Object)null || !TryGetChainLightningState(aoe, out ChainLightningAoeState state)) { return; } ChainLightningActivation chainLightningActivation = state.Activation ?? ActiveActivation; if (chainLightningActivation != null) { Character val = TryGetHitCharacter(collider); if (!((Object)(object)val == (Object)null) && chainLightningActivation.HitTargetIds.Contains(((Object)val).GetInstanceID())) { result = false; } } } private static ChainLightningActivation EnsureActivation(ChainLightningAoeState state) { ChainLightningActivation chainLightningActivation = state.Activation; if (chainLightningActivation == null) { ChainLightningActivation? obj = ActiveActivation ?? new ChainLightningActivation(); ChainLightningActivation chainLightningActivation2 = obj; state.Activation = obj; chainLightningActivation = chainLightningActivation2; } return chainLightningActivation; } private static Character? TryGetHitCharacter(Collider collider) { GameObject val = Projectile.FindHitObject(collider); if (!((Object)(object)val != (Object)null)) { return null; } return val.GetComponent<Character>(); } private static bool TryGetChainLightningState(Aoe aoe, out ChainLightningAoeState state) { state = ChainLightningAoeState.NotChainLightning; if (!CouldBeChainLightningAoe(aoe)) { return false; } state = AoeStates.GetValue(aoe, CreateAoeState); return state.IsChainLightning; } private static ChainLightningAoeState CreateAoeState(Aoe aoe) { if (!HasChainLightningName(aoe)) { return ChainLightningAoeState.NotChainLightning; } return new ChainLightningAoeState(isChainLightning: true); } private static bool HasChainLightningName(Aoe aoe) { Transform transform = ((Component)aoe).transform; if (!ContainsChainLightningName(((Object)aoe).name) && (!((Object)(object)transform.root != (Object)null) || !ContainsChainLightningName(((Object)transform.root).name))) { if ((Object)(object)transform.parent != (Object)null) { return ContainsChainLightningName(((Object)transform.parent).name); } return false; } return true; } private static bool CouldBeChainLightningAoe(Aoe aoe) { if ((Object)(object)aoe == (Object)null) { return false; } if (ContainsChainLightningName(((Object)aoe).name)) { return true; } if (aoe.m_chainStartChance <= 0f && aoe.m_chainChancePerTarget <= 0f && (Object)(object)aoe.m_chainObj == (Object)null) { return false; } if (ContainsChainLightningName(((Object)(object)aoe.m_chainObj != (Object)null) ? ((Object)aoe.m_chainObj).name : "")) { return true; } Transform transform = ((Component)aoe).transform; if (!((Object)(object)transform.root != (Object)null) || !ContainsChainLightningName(((Object)transform.root).name)) { if ((Object)(object)transform.parent != (Object)null) { return ContainsChainLightningName(((Object)transform.parent).name); } return false; } return true; } private static bool ContainsChainLightningName(string value) { if (!string.IsNullOrWhiteSpace(value)) { return value.IndexOf("ChainLightning", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } } [HarmonyPatch(typeof(Aoe), "OnHit", new Type[] { typeof(Collider), typeof(Vector3) })] internal static class AoeOnHitChainLightningDedupPatch { [HarmonyPriority(800)] private static bool Prefix(Aoe __instance, Collider collider, ref bool __result) { if (ChainLightningDedupSystem.ShouldAllowChainLightningHit(__instance, collider)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Aoe), "ShouldHit", new Type[] { typeof(Collider) })] internal static class AoeShouldHitChainLightningCandidatePatch { private static void Postfix(Aoe __instance, Collider collider, ref bool __result) { ChainLightningDedupSystem.FilterChainLightningCandidate(__instance, collider, ref __result); } } [HarmonyPatch(typeof(Aoe), "Setup")] internal static class AoeSetupChainLightningActivationPatch { private static void Postfix(Aoe __instance) { ChainLightningDedupSystem.TrackSetup(__instance); } } [HarmonyPatch(typeof(Aoe), "CustomFixedUpdate")] internal static class AoeCustomFixedUpdateChainLightningActivationPatch { private static void Prefix(Aoe __instance, out ChainLightningDedupSystem.ChainUpdateScope __state) { __state = ChainLightningDedupSystem.BeginChainUpdate(__instance); } private static void Postfix(ChainLightningDedupSystem.ChainUpdateScope __state) { ChainLightningDedupSystem.EndChainUpdate(__state); } } internal static class DirectWeaponHitContextSystem { internal readonly struct Scope { internal ScopeKind Kind { get; } internal string PreviousWeaponPrefabName { get; } internal Scope(ScopeKind kind, string previousWeaponPrefabName = "") { Kind = kind; PreviousWeaponPrefabName = previousWeaponPrefabName; } } internal enum ScopeKind { None, DirectHit, CharacterDamage, CharacterDamageWithExternalDirectHit } private static int _directHitDepth; private static int _characterDamageDepth; private static string _weaponPrefabName = ""; internal static bool IsDirectWeaponHitActive => _directHitDepth > 0; internal static bool ShouldCountWeaponEffectHit { get { if (_directHitDepth > 0 && _characterDamageDepth == 1) { return !WarfareTweaksBridge.IsExternalGeneratedDamageActive; } return false; } } internal static Scope BeginAttackHit(Attack attack) { if ((Object)(object)attack?.m_character != (Object)(object)Player.m_localPlayer) { return default(Scope); } string weaponPrefabName = _weaponPrefabName; _weaponPrefabName = GetWeaponPrefabName(attack.m_weapon); _directHitDepth++; return new Scope(ScopeKind.DirectHit, weaponPrefabName); } internal static Scope BeginProjectileHit(Projectile projectile) { if ((Object)(object)projectile == (Object)null || (Object)(object)ProjectileAccess.GetOwner(projectile) != (Object)(object)Player.m_localPlayer || WarfareTweaksBridge.ShouldSuppressProjectile(projectile)) { return default(Scope); } string weaponPrefabName = _weaponPrefabName; _weaponPrefabName = GetWeaponPrefabName(ProjectileAccess.GetWeapon(projectile)); _directHitDepth++; return new Scope(ScopeKind.DirectHit, weaponPrefabName); } internal static Scope BeginCharacterDamage() { _characterDamageDepth++; if (_directHitDepth == 0 && WarfareTweaksBridge.TryGetCaptainValheimShieldHitWeaponPrefabName(out string weaponPrefabName)) { string weaponPrefabName2 = _weaponPrefabName; _weaponPrefabName = weaponPrefabName; _directHitDepth++; return new Scope(ScopeKind.CharacterDamageWithExternalDirectHit, weaponPrefabName2); } return new Scope(ScopeKind.CharacterDamage); } internal static bool TryGetCurrentWeaponPrefabName(out string prefabName) { prefabName = _weaponPrefabName; if (_directHitDepth > 0) { return !string.IsNullOrWhiteSpace(prefabName); } return false; } private static string GetWeaponPrefabName(ItemData? weapon) { if (!((Object)(object)weapon?.m_dropPrefab != (Object)null)) { return ""; } return ((Object)weapon.m_dropPrefab).name; } internal static void End(Scope scope) { switch (scope.Kind) { case ScopeKind.DirectHit: if (_directHitDepth > 0) { _directHitDepth--; _weaponPrefabName = scope.PreviousWeaponPrefabName; } break; case ScopeKind.CharacterDamage: if (_characterDamageDepth > 0) { _characterDamageDepth--; } break; case ScopeKind.CharacterDamageWithExternalDirectHit: if (_characterDamageDepth > 0) { _characterDamageDepth--; } if (_directHitDepth > 0) { _directHitDepth--; _weaponPrefabName = scope.PreviousWeaponPrefabName; } break; } } } [HarmonyPatch(typeof(Attack), "DoMeleeAttack")] internal static class AttackDoMeleeAttackDirectWeaponHitPatch { [HarmonyPriority(800)] private static void Prefix(Attack __instance, out DirectWeaponHitContextSystem.Scope __state) { __state = DirectWeaponHitContextSystem.BeginAttackHit(__instance); } [HarmonyPriority(800)] private static void Postfix(DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(__state); } } [HarmonyPatch(typeof(Attack), "DoAreaAttack")] internal static class AttackDoAreaAttackDirectWeaponHitPatch { [HarmonyPriority(800)] private static void Prefix(Attack __instance, out DirectWeaponHitContextSystem.Scope __state) { __state = DirectWeaponHitContextSystem.BeginAttackHit(__instance); } [HarmonyPriority(800)] private static void Postfix(DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(__state); } } [HarmonyPatch(typeof(Character), "Damage")] internal static class CharacterDamageDirectWeaponHitDepthPatch { [HarmonyPriority(800)] private static void Prefix(out DirectWeaponHitContextSystem.Scope __state) { __state = DirectWeaponHitContextSystem.BeginCharacterDamage(); } [HarmonyPriority(0)] private static void Postfix(DirectWeaponHitContextSystem.Scope __state) { DirectWeaponHitContextSystem.End(__state); } } internal static class ProjectileAccess { private static readonly FieldRef<Projectile, ItemData>? WeaponRef = CreateFieldRef<ItemData>("m_weapon"); private static readonly FieldRef<Projectile, Character>? OwnerRef = CreateFieldRef<Character>("m_owner"); private static readonly FieldRef<Projectile, HitData>? OriginalHitDataRef = CreateFieldRef<HitData>("m_originalHitData"); private static readonly FieldInfo? WeaponField = AccessTools.Field(typeof(Projectile), "m_weapon"); private static readonly FieldInfo? OwnerField = AccessTools.Field(typeof(Projectile), "m_owner"); private static readonly FieldInfo? OriginalHitDataField = AccessTools.Field(typeof(Projectile), "m_originalHitData"); internal static ItemData? GetWeapon(Projectile projectile) { if (WeaponRef == null) { object? obj = WeaponField?.GetValue(projectile); return (ItemData?)((obj is ItemData) ? obj : null); } return WeaponRef.Invoke(projectile); } internal static Character? GetOwner(Projectile projectile) { if (OwnerRef == null) { object? obj = OwnerField?.GetValue(projectile); return (Character?)((obj is Character) ? obj : null); } return OwnerRef.Invoke(projectile); } internal static HitData? GetOriginalHitData(Projectile projectile) { if (OriginalHitDataRef == null) { object? obj = OriginalHitDataField?.GetValue(projectile); return (HitData?)((obj is HitData) ? obj : null); } return OriginalHitDataRef.Invoke(projectile); } private static FieldRef<Projectile, T>? CreateFieldRef<T>(string fieldName) { try { return AccessTools.FieldRefAccess<Projectile, T>(fieldName); } catch { return null; } } } internal static class WarfareTweaksLocalization { internal const string EffectAdrenaline = "$wt_effect_adrenaline"; internal const string EffectBash = "$wt_effect_bash"; internal const string EffectBleeding = "$wt_effect_bleeding"; internal const string EffectBleedingSecondary = "$wt_effect_bleeding_secondary"; internal const string EffectBurningSecondary = "$wt_effect_burning_secondary"; internal const string EffectDecapitator = "$wt_effect_decapitator"; internal const string EffectExecutioner = "$wt_effect_executioner"; internal const string EffectHackAndSlash = "$wt_effect_hack_and_slash"; internal const string EffectHaste = "$wt_effect_haste"; internal const string EffectImpale = "$wt_effect_impale"; internal const string EffectPiercer = "$wt_effect_piercer"; internal const string EffectLightningBurst = "$wt_effect_lightning_burst"; internal const string EffectPinning = "$wt_effect_pinning"; internal const string EffectPiercingGreatbow = "$wt_effect_piercing_greatbow"; internal const string EffectSmasher = "$wt_effect_smasher"; internal const string EffectSmashAndBash = "$wt_effect_smash_and_bash"; internal const string EffectBludgeoner = "$wt_effect_bludgeoner"; internal const string EffectVampirism = "$wt_effect_vampirism"; internal const string EffectFallback = "$wt_effect_fallback"; internal const string DamageBlunt = "$wt_damage_blunt"; internal const string DamageFire = "$wt_damage_fire"; internal const string DamageFrost = "$wt_damage_frost"; internal const string DamageLightning = "$wt_damage_lightning"; internal const string DamagePierce = "$wt_damage_pierce"; internal const string DamageSlash = "$wt_damage_slash"; internal const string TooltipAdrenalineValue = "$wt_tooltip_adrenaline_value"; internal const string TooltipAdrenaline = "$wt_tooltip_adrenaline"; internal const string TooltipHaste = "$wt_tooltip_haste"; internal const string TooltipVampirismValue = "$wt_tooltip_vampirism_value"; internal const string TooltipVampirism = "$wt_tooltip_vampirism"; internal const string TooltipDotValue = "$wt_tooltip_dot_value"; internal const string TooltipDot = "$wt_tooltip_dot"; internal const string TooltipBashValue = "$wt_tooltip_bash_value"; internal const string TooltipBash = "$wt_tooltip_bash"; internal const string TooltipExecutionerValue = "$wt_tooltip_executioner_value"; internal const string TooltipExecutioner = "$wt_tooltip_executioner"; internal const string TooltipResistanceWeakness = "$wt_tooltip_resistance_weakness"; internal const string TooltipFixedExtraDamageValue = "$wt_tooltip_fixed_extra_damage_value"; internal const string TooltipFixedExtraDamage = "$wt_tooltip_fixed_extra_damage"; internal const string TooltipPinningValue = "$wt_tooltip_pinning_value"; internal const string TooltipPinning = "$wt_tooltip_pinning"; internal const string TooltipFallbackValue = "$wt_tooltip_fallback_value"; internal const string TooltipFallback = "$wt_tooltip_fallback"; internal static void Load(WarfareTweaksPlugin plugin) { Localizer.Load((BaseUnityPlugin)(object)plugin); } internal static string Localize(string token, string fallback) { if (Localization.instance == null) { return fallback; } string text = Localization.instance.Localize(token); if (!string.IsNullOrWhiteSpace(text) && !string.Equals(text, token, StringComparison.Ordinal)) { return text; } return fallback; } internal static string Format(string token, string fallback, params object[] args) { string format = Localize(token, fallback); try { return string.Format(CultureInfo.InvariantCulture, format, args); } catch (FormatException) { return string.Format(CultureInfo.InvariantCulture, fallback, args); } } } internal static class WarfareTweaksBridge { private delegate bool ShouldSuppressProjectileDelegate(Projectile projectile); private delegate bool IsGeneratedDamageActiveDelegate(); private delegate bool TryGetWeaponPrefabNameDelegate(out string weaponPrefabName); private const string SecondaryAttacksBridgeTypeName = "SecondaryAttacks.WarfareTweaksBridge, SecondaryAttacks"; private const string CaptainValheimBridgeTypeName = "CaptainValheim.WarfareTweaksBridge, CaptainValheim"; private static ShouldSuppressProjectileDelegate? _shouldSuppressProjectile; private static IsGeneratedDamageActiveDelegate? _isGeneratedDamageActive; private static TryGetWeaponPrefabNameDelegate? _tryGetCaptainValheimShieldHitWeaponPrefabName; private static bool _resolved; internal static bool IsExternalGeneratedDamageActive { get { EnsureResolved(); return _isGeneratedDamageActive?.Invoke() ?? false; } } internal static bool ShouldSuppressProjectile(Projectile projectile) { EnsureResolved(); return _shouldSuppressProjectile?.Invoke(projectile) ?? false; } internal static bool TryGetCaptainValheimShieldHitWeaponPrefabName(out string weaponPrefabName) { weaponPrefabName = ""; EnsureResolved(); if (_tryGetCaptainValheimShieldHitWeaponPrefabName == null) { return false; } if (!_tryGetCaptainValheimShieldHitWeaponPrefabName(out var weaponPrefabName2) || string.IsNullOrWhiteSpace(weaponPrefabName2)) { return false; } weaponPrefabName = weaponPrefabName2; return true; } private static void EnsureResolved() { if (!_resolved) { _resolved = true; Type type = Type.GetType("SecondaryAttacks.WarfareTweaksBridge, SecondaryAttacks", throwOnError: false); Type type2 = Type.GetType("CaptainValheim.WarfareTweaksBridge, CaptainValheim", throwOnError: false); if (type != null) { _shouldSuppressProjectile = CreateStaticDelegate<ShouldSuppressProjectileDelegate>(type.GetMethod("ShouldSuppressProjectile", BindingFlags.Static | BindingFlags.Public)); _isGeneratedDamageActive = CreateStaticDelegate<IsGeneratedDamageActiveDelegate>(type.GetProperty("IsGeneratedDamageActive", BindingFlags.Static | BindingFlags.Public)?.GetGetMethod()); } if (type2 != null) { _tryGetCaptainValheimShieldHitWeaponPrefabName = CreateStaticDelegate<TryGetWeaponPrefabNameDelegate>(type2.GetMethod("TryGetShieldHitWeaponPrefabName", BindingFlags.Static | BindingFlags.Public)); } } } private static TDelegate? CreateStaticDelegate<TDelegate>(MethodInfo? method) where TDelegate : class { if (method == null) { return null; } try { return Delegate.CreateDelegate(typeof(TDelegate), method, throwOnBindFailure: false) as TDelegate; } catch { return null; } } } internal static class WarfareTweaksCompat { internal const string WarfareGuid = "Therzie.Warfare"; internal const string WarfareFireAndIceGuid = "Therzie.WarfareFireAndIce"; internal const string JewelcraftingGuid = "org.bepinex.plugins.jewelcrafting"; } internal static class WarfareTweaksConfigLoader { private static readonly HashSet<string> IgnoredLegacyEffectFields = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "trigger", "damageType", "modifier", "damage", "heal", "healthThresholdPercent", "damageMultiplier", "consumeOnModify" }; private static readonly HashSet<string> IgnoredLegacyPrefabOverrideFields = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "type", "prefab", "trigger", "lightningDamage", "radius", "ttl", "hitInterval", "damageType", "modifier", "damage", "heal", "healthThresholdPercent", "damageMultiplier", "consumeOnModify" }; private static readonly HashSet<string> IgnoredLegacyScalarValueFields = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "mode" }; private static readonly Dictionary<string, string> PrefabScalarFields = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["adrenaline"] = "staminaRestore.value", ["bash"] = "value", ["bleeding"] = "damageFactor", ["bleedingSecondary"] = "damageFactor", ["bleeding_secondary"] = "damageFactor", ["burningSecondary"] = "damageFactor", ["burning_secondary"] = "damageFactor", ["executioner"] = "value", ["haste"] = "moveSpeedMultiplier", ["impale"] = "damageFactor", ["lightningBurst"] = "value", ["pinning"] = "value", ["pierceGreatbow"] = "value", ["pierceGreatbowFireAndIce"] = "value", ["piercingGreatbowFireAndIce"] = "value", ["piercingGreatbowMistlands"] = "value", ["piercingGreatbowModer"] = "value", ["piercingGreatbowPlains"] = "value", ["vampirism"] = "value" }; private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); internal static void EnsureLocalFileExists() { Directory.CreateDirectory(WarfareTweaksPlugin.ConfigDirectoryPath); if (!File.Exists(WarfareTweaksPlugin.WarfareYamlFilePath)) { File.WriteAllText(WarfareTweaksPlugin.WarfareYamlFilePath, WarfareTweaksDefaultYamlResources.Load("WarfareTweaks.yml")); } } internal static bool TryParse(string yamlText, out Dictionary<string, EffectBehaviorConfig> parsed) { parsed = new Dictionary<string, EffectBehaviorConfig>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(yamlText)) { WarfareTweaksPlugin.ModLogger.LogWarning((object)"Ignoring empty WarfareTweaks.yml; the last valid configuration remains active. Use '{}' to explicitly clear all effect assignments."); return false; } try { YamlStream yamlStream = new YamlStream(); yamlStream.Load(new StringReader(yamlText)); if (yamlStream.Documents.Count == 0 || !(yamlStream.Documents[0].RootNode is YamlMappingNode yamlMappingNode)) { throw new InvalidDataException("The YAML root must be a mapping."); } foreach (KeyValuePair<YamlNode, YamlNode> child in yamlMappingNode.Children) { string text = (child.Key as YamlScalarNode)?.Value?.Trim() ?? ""; if (!string.IsNullOrWhiteSpace(text)) { try { parsed[text] = DeserializeEffectBehaviorConfig(text, child.Value); } catch (Exception ex) { throw new InvalidDataException("Block '" + text + "' is invalid: " + ex.Message, ex); } } } return true; } catch (Exception ex2) { WarfareTweaksPlugin.ModLogger.LogError((object)("Failed to parse WarfareTweaks.yml; the last valid configuration remains active: " + ex2.Message)); parsed.Clear(); return false; } } private static EffectBehaviorConfig DeserializeEffectBehaviorConfig(string rootKey, YamlNode node) { if (!(node is YamlMappingNode yamlMappingNode)) { return DeserializeYamlNode<EffectBehaviorConfig>(node) ?? new EffectBehaviorConfig(); } NormalizePrefabScalarOverrides(rootKey, yamlMappingNode); RemoveIgnoredLegacyFields(rootKey, yamlMappingNode); return DeserializeYamlNode<EffectBehaviorConfig>(yamlMappingNode) ?? new EffectBehaviorConfig(); } private static void RemoveIgnoredLegacyFields(string rootKey, YamlMappingNode mapping) { RemoveIgnoredMappingFields(mapping, IgnoredLegacyEffectFields, "block '" + rootKey + "'"); RemoveIgnoredScalarValueMode(mapping, "staminaRestore", "block '" + rootKey + "'"); if (!TryGetMappingChild(mapping, "prefabs", out YamlMappingNode child) || child == null) { return; } foreach (KeyValuePair<YamlNode, YamlNode> child2 in child.Children) { if (child2.Value is YamlMappingNode yamlMappingNode) { string text = (child2.Key as YamlScalarNode)?.Value?.Trim() ?? ""; string location = "prefab '" + text + "' in block '" + rootKey + "'"; RemoveIgnoredMappingFields(yamlMappingNode, IgnoredLegacyPrefabOverrideFields, location); RemoveIgnoredScalarValueMode(yamlMappingNode, "staminaRestore", location); } } } private static void RemoveIgnoredScalarValueMode(YamlMappingNode owner, string fieldName, string location) { if (TryGetMappingChild(owner, fieldName, out YamlMappingNode child) && child != null) { RemoveIgnoredMappingFields(child, IgnoredLegacyScalarValueFields, "field '" + fieldName + "' in " + location); } } private static void RemoveIgnoredMappingFields(YamlMappingNode mapping, HashSet<string> ignoredLegacyFields, string location) { foreach (KeyValuePair<YamlNode, YamlNode> item in mapping.Children.ToList()) { string text = (item.Key as YamlScalarNode)?.Value?.Trim() ?? ""; if (ignoredLegacyFields.Contains(text)) { mapping.Children.Remove(item.Key); if (WarfareTweaksWarningLog.TryMarkReported("ignored_yaml_field_" + text)) { WarfareTweaksPlugin.ModLogger.LogWarning((object)("Ignoring unsupported WarfareTweaks.yml field '" + text + "' in " + location + "; it has no WarfareTweaks behavior.")); } } } } private static void NormalizePrefabScalarOverrides(string rootKey, YamlMappingNode mapping) { string text = ResolvePrefabScalarField(rootKey, mapping); if (string.IsNullOrWhiteSpace(text) || !TryGetMappingChild(mapping, "prefabs", out YamlMappingNode child) || child == null) { return; } foreach (KeyValuePair<YamlNode, YamlNode> item in child.Children.ToList()) { if (!(item.Value is YamlScalarNode yamlScalarNode)) { continue; } string text2 = yamlScalarNode.Value?.Trim() ?? ""; if (string.IsNullOrWhiteSpace(text2)) { child.Children[item.Key] = new YamlMappingNode(); continue; } if (!float.TryParse(text2, NumberStyles.Float, CultureInfo.InvariantCulture, out var _)) { throw new InvalidDataException($"Prefab '{item.Key}' must use a numeric {text}, got '{text2}'."); } if (string.Equals(text, "value", StringComparison.OrdinalIgnoreCase) && !int.TryParse(text2, NumberStyles.Integer, CultureInfo.InvariantCulture, out var _)) { throw new InvalidDataException($"Prefab '{item.Key}' must use an integer value, got '{text2}'."); } child.Children[item.Key] = BuildScalarOverrideNode(text, text2); } } private static YamlMappingNode BuildScalarOverrideNode(string scalarField, string value) { string[] array = (from part in scalarField.Split(new char[1] { '.' }, StringSplitOptions.RemoveEmptyEntries) select part.Trim() into part where !string.IsNullOrWhiteSpace(part) select part).ToArray(); if (array.Length == 0) { return new YamlMappingNode(); } YamlMappingNode yamlMappingNode = new YamlMappingNode(); YamlMappingNode yamlMappingNode2 = yamlMappingNode; for (int num = 0; num < array.Length - 1; num++) { YamlMappingNode yamlMappingNode3 = new YamlMappingNode(); yamlMappingNode2.Children[new YamlScalarNode(array[num])] = yamlMappingNode3; yamlMappingNode2 = yamlMappingNode3; } yamlMappingNode2.Children[new YamlScalarNode(array[^1])] = new YamlScalarNode(value); return yamlMappingNode; } private static string? ResolvePrefabScalarField(string rootKey, YamlMappingNode mapping) { string value; string text = ((TryGetScalarChild(mapping, "type", out value) && !string.IsNullOrWhiteSpace(value)) ? value : rootKey).Trim(); if (text.IndexOf("ChainLightning", StringComparison.OrdinalIgnoreCase) >= 0 || string.Equals(text, "chainLightning", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "chainLightningMistlands", StringComparison.OrdinalIgnoreCase)) { return "procChance"; } if (!PrefabScalarFields.TryGetValue(text, out string value2)) { return null; } return value2; } private static bool TryGetMappingChild(YamlMappingNode mapping, string key, out YamlMappingNode? child) { child = null; foreach (KeyValuePair<YamlNode, YamlNode> child2 in mapping.Children) { if (child2.Key is YamlScalarNode yamlScalarNode && string.Equals(yamlScalarNode.Value?.Trim(), key, StringComparison.OrdinalIgnoreCase) && child2.Value is YamlMappingNode yamlMappingNode) { child = yamlMappingNode; return true; } } return false; } private static bool TryGetScalarChild(YamlMappingNode mapping, string key, out string? value) { value = null; foreach (KeyValuePair<YamlNode, YamlNode> child in mapping.Children) { if (child.Key is YamlScalarNode yamlScalarNode && string.Equals(yamlScalarNode.Value?.Trim(), key, StringComparison.OrdinalIgnoreCase) && child.Value is YamlScalarNode yamlScalarNode2) { value = yamlScalarNode2.Value; return true; } } return false; } private static T? DeserializeYamlNode<T>(YamlNode node) { using StringWriter stringWriter = new StringWriter(); new YamlStream(new YamlDocument(node)).Save(stringWriter, assignAnchors: false); return Deserializer.Deserialize<T>(stringWriter.ToString()); } } internal sealed class EffectBehaviorConfig { public string Type { get; set; } = ""; public int? Value { get; set; } public string Prefab { get; set; } = ""; public int? StacksRequired { get; set; } public float StackWindow { get; set; } public float? Duration { get; set; } public float? TickInterval { get; set; } public float? DamageFactor { get; set; } public float? LightningDamage { get; set; } public float? Radius { get; set; } public float? Ttl { get; set; } public float? HitInterval { get; set; } public float ProcChance { get; set; } = 100f; public ScalarValueConfig StaminaRestore { get; set; } = new ScalarValueConfig(); public float MoveSpeedMultiplier { get; set; } = 1f; public Dictionary<string, EffectBehaviorOverrideConfig>? Prefabs { get; set; } } internal sealed class EffectBehaviorOverrideConfig { public int? Value { get; set; } public int? StacksRequired { get; set; } public float? StackWindow { get; set; } public float? Duration { get; set; } public float? TickInterval { get; set; } public float? DamageFactor { get; set; } public float? ProcChance { get; set; } public ScalarValueOverrideConfig? StaminaRestore { get; set; } public float? MoveSpeedMultiplier { get; set; } } internal sealed class ScalarValueConfig { public float Value { get; set; } } internal sealed class ScalarValueOverrideConfig { public float? Value { get; set; } } internal static class WarfareTweaksDefaultYamlResources { private const string ResourcePrefix = "WarfareTweaks.Resources.Defaults."; internal static string Load(string fileName) { string text = "WarfareTweaks.Resources.Defaults." + fileName; using Stream stream = typeof(WarfareTweaksDefaultYamlResources).Assembly.GetManifestResourceStream(text); if (stream == null) { throw new InvalidOperationException("Embedded default YAML resource '" + text + "' was not found."); } using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); return streamReader.ReadToEnd(); } } [HarmonyPatch(typeof(FejdStartup), "Awake")] internal static class FejdStartupAwakeWarfareItemManagerSyncCompatPatch { [HarmonyPriority(0)] private static void Postfix() { WarfareItemManagerSyncCompat.RegisterMissingRecipeConfigs(); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDbAwakeWarfareTweaksPatch { private static void Postfix(ObjectDB __instance) { WarfareTweaksPlugin.ApplyToObjectDb(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class ObjectDbCopyOtherDbWarfareTweaksPatch { [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { WarfareTweaksPlugin.ApplyToObjectDb(__instance); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] [HarmonyAfter(new string[] { "Therzie.WarfareFireAndIce" })] internal static class ZNetSceneAwakeWarfareTweaksPatch { [HarmonyPriority(0)] private static void Postfix(ZNetScene __instance) { WarfareTweaksPlugin.ApplyToZNetScene(__instance); } } [HarmonyPatch(typeof(SEMan), "ApplyStatusEffectSpeedMods")] internal static class SeManApplyStatusEffectSpeedModsWarfareHastePatch { [HarmonyPriority(0)] private static void Postfix(SEMan __instance, ref float speed) { WarfareCompat.ApplyWarfareHasteSpeedModifier(__instance, ref speed); } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int) })] internal static class ItemDataGetTooltipWarfareTweaksFallbackTooltipPatch { [HarmonyPriority(0)] private static void Postfix(ItemData item, ref string __result) { WarfareCompat.AppendMissingConfiguredEffectTooltips(item, ref __result); } } [HarmonyPatch(typeof(Projectile), "Setup")] internal static class ProjectileSetupWarfareThrowablePatch { [HarmonyPriority(0)] private static void Postfix(Projectile __instance) { WarfareThrowableCompat.PrepareProjectileIfNeeded(__instance); } } [HarmonyPatch(typeof(Projectile), "OnHit")] internal static class ProjectileOnHitWarfareTweaksPatch { private readonly struct ProjectileHitContextScope { public WarfareTweaksProjectileHitContext.Scope ProjectileScope { get; } public DirectWeaponHitContextSystem.Scope DirectHitScope { get; } public ProjectileHitContextScope(WarfareTweaksProjectileHitContext.Scope projectileScope, DirectWeaponHitContextSystem.Scope directHitScope) { ProjectileScope = projectileScope; DirectHitScope = directHitScope; } } [HarmonyPriority(800)] private static void Prefix(Projectile __instance, out ProjectileHitContextScope __state) { __state = new ProjectileHitContextScope(WarfareTweaksProjectileHitContext.Begin(__instance), DirectWeaponHitContextSystem.BeginProjectileHit(__instance)); WarfareThrowableCompat.PrepareProjectileIfNeeded(__instance); } [HarmonyPriority(0)] private static void Postfix(ProjectileHitContextScope __state) { DirectWeaponHitContextSystem.End(__state.DirectHitScope); WarfareTweaksProjectileHitContext.End(__state.ProjectileScope); } } [HarmonyPatch(typeof(Destructible), "Damage")] internal static class DestructibleDamageWarfareThrowablePatch { private static void Prefix(HitData hit) { WarfareThrowableCompat.ApplyProjectileToolTierIfNeeded(hit); } } [HarmonyPatch(typeof(MineRock), "Damage")] internal static class MineRockDamageWarfareThrowablePatch { private static void Prefix(HitData hit) { WarfareThrowableCompat.ApplyProjectileToolTierIfNeeded(hit); } } [HarmonyPatch(typeof(MineRock5), "Damage")] internal static class MineRock5DamageWarfareThrowablePatch { private static void Prefix(HitData hit) { WarfareThrowableCompat.ApplyProjectileToolTierIfNeeded(hit); } } [HarmonyPatch(typeof(TreeBase), "Damage")] internal static class TreeBaseDamageWarfareThrowablePatch { private static void Prefix(HitData hit) { WarfareThrowableCompat.ApplyProjectileToolTierIfNeeded(hit); WarfareThrowableCompat.ApplyProjectileWoodCuttingSkillIfNeeded(hit); } } [HarmonyPatch(typeof(TreeLog), "Damage")] internal static class TreeLogDamageWarfareThrowablePatch { private static void Prefix(HitData hit) { WarfareThrowableCompat.ApplyProjectileToolTierIfNeeded(hit); WarfareThrowableCompat.ApplyProjectileWoodCuttingSkillIfNeeded(hit); } } [HarmonyPatch(typeof(WearNTear), "Damage")] internal static class WearNTearDamageWarfareThrowablePatch { private static void Prefix(HitData hit) { WarfareThrowableCompat.ApplyProjectileToolTierIfNeeded(hit); } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList", new Type[] { typeof(List<Recipe>) })] internal static class InventoryGuiUpdateRecipeListWarfareThrowableUpgradePatch { [HarmonyPriority(800)] private static void Prefix(InventoryGui __instance, List<Recipe> recipes) { WarfareThrowableCompat.PrepareUpgradeRecipeList(recipes, !__instance.InCraftTab()); } } [HarmonyPatch(typeof(Humanoid), "StartAttack")] internal static class HumanoidStartAttackWarfareThrowablePatch { [HarmonyPriority(800)] private static void Prefix(Humanoid __instance) { if (__instance is Player) { WarfareThrowableCompat.PrepareWeaponForUse(__instance.GetCurrentWeapon()); } } } [HarmonyPatch(typeof(Attack), "OnAttackTrigger")] internal static class AttackOnAttackTriggerWarfareThrowablePatch { [HarmonyPriority(800)] private static void Prefix(Attack __instance, out bool __state) { __state = WarfareThrowableCompat.BeginInventoryRemovalPreservation(__instance); } [HarmonyPriority(0)] private static void Postfix(bool __state) { WarfareThrowableCompat.EndInventoryRemovalPreservation(__state); } } [HarmonyPatch(typeof(Attack), "ConsumeItem")] internal static class AttackConsumeItemWarfareThrowablePatch { [HarmonyPriority(800)] private static bool Prefix(Attack __instance) { return !WarfareThrowableCompat.ShouldPreserveWeaponOnConsume(__instance); } } [HarmonyPatch(typeof(Attack), "UseAmmo")] internal static class AttackUseAmmoWarfareThrowablePatch { [HarmonyPriority(800)] private static bool Prefix(Attack __instance, ref bool __result, ref ItemData ammoItem) { if (!WarfareThrowableCompat.ShouldSkipAmmoConsumption(__instance)) { return true; } ammoItem = null; __result = true; return false; } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(ItemData) })] internal static class InventoryRemoveItemWarfareThrowablePatch { [HarmonyPriority(800)] private static bool Prefix(ItemData item, ref bool __result) { if (!WarfareThrowableCompat.ShouldBlockInventoryRemoval(item)) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(ItemData), typeof(int) })] internal static class InventoryRemoveItemAmountWarfareThrowablePatch { [HarmonyPriority(800)] private static bool Prefix(ItemData item, ref bool __result) { if (!WarfareThrowableCompat.ShouldBlockInventoryRemoval(item)) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(Inventory), "RemoveOneItem", new Type[] { typeof(ItemData) })] internal static class InventoryRemoveOneItemWarfareThrowablePatch { [HarmonyPriority(800)] private static bool Prefix(ItemData item, ref bool __result) { if (!WarfareThrowableCompat.ShouldBlockInventoryRemoval(item)) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(Humanoid), "UnequipItem", new Type[] { typeof(ItemData), typeof(bool) })] internal static class HumanoidUnequipItemWarfareThrowableAttackPatch { [HarmonyPriority(800)] private static bool Prefix(ItemData item) { return !WarfareThrowableCompat.ShouldBlockInventoryRemoval(item, allowBrokenUnequip: true); } } [HarmonyPatch(typeof(Humanoid), "ConsumeItem", new Type[] { typeof(Inventory), typeof(ItemData), typeof(bool) })] internal static class HumanoidConsumeItemWarfareThrowablePatch { [HarmonyPriority(800)] private static bool Prefix(ItemData item, ref bool __result) { if (!WarfareThrowableCompat.ShouldBlockInventoryRemoval(item)) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(bool) })] internal static class InventoryRemoveNamedItemWarfareThrowablePatch { [HarmonyPriority(800)] private static bool Prefix(string name) { return !WarfareThrowableCompat.ShouldBlockNamedInventoryRemoval(name); } } [HarmonyPatch(typeof(Attack), "ProjectileAttackTriggered")] internal static class AttackProjectileAttackTriggeredWarfareThrowableDurabilityPatch { private static void Prefix(Attack __instance, out WarfareThrowableCompat.ProjectileDurabilityDrainState __state) { __state = WarfareThrowableCompat.CaptureProjectileDurabilityDrain(__instance); } private static void Postfix(WarfareThrowableCompat.ProjectileDurabilityDrainState __state) { WarfareThrowableCompat.ApplyMissingProjectileDurabilityDrain(__state); } } [BepInPlugin("sighsorry.WarfareTweaks", "WarfareTweaks", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class WarfareTweaksPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } internal const string ModName = "WarfareTweaks"; internal const string ModVersion = "1.0.1"; internal const string Author = "sighsorry"; internal const string ModGUID = "sighsorry.WarfareTweaks"; internal const string WarfareYamlFileName = "WarfareTweaks.yml"; internal static readonly ManualLogSource ModLogger = Logger.CreateLogSource("WarfareTweaks"); internal static readonly ConfigSync ConfigSync = new ConfigSync("sighsorry.WarfareTweaks") { DisplayName = "WarfareTweaks", CurrentVersion = "1.0.1", MinimumRequiredVersion = "1.0.1" }; private static Dictionary<string, EffectBehaviorConfig> _currentEffects = new Dictionary<string, EffectBehaviorConfig>(StringComparer.OrdinalIgnoreCase); private static CustomSyncedValue<string>? _syncedWarfareYaml; private static bool _suppressSyncedYamlChanged; private readonly Harmony _harmony = new Harmony("sighsorry.WarfareTweaks"); private FileSystemWatcher? _watcher; private readonly object _reloadLock = new object(); private DateTime _lastConfigReloadTime; private const long ReloadDelayTicks = 10000000L; internal static string ConfigDirectoryPath => Paths.ConfigPath; internal static string WarfareYamlFilePath => Path.Combine(ConfigDirectoryPath, "WarfareTweaks.yml"); public void Awake() { ConfigSync.AddLockingConfigEntry<Toggle>(((BaseUnityPlugin)this).Config.Bind<Toggle>("1 - General", "Lock Configuration", Toggle.On, "If on, the server configuration is enforced for clients.")); _syncedWarfareYaml = new CustomSyncedValue<string>(ConfigSync, "warfare_tweaks_warfare_yaml", ""); _syncedWarfareYaml.ValueChanged += OnSyncedWarfareYamlChanged; WarfareTweaksLocalization.Load(this); if (!ReloadLocalConfigFromDisk(applyToWorld: false)) { ApplyEmbeddedDefaultConfig(); } Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); WarfareCompat.TryInstallHooks(); WarfareSkillCompat.TryInstallHooks(); JewelcraftingThrowableCompat.TryInstallHooks(); SetupWatcher(); } private void OnDestroy() { if (_syncedWarfareYaml != null) { _syncedWarfareYaml.ValueChanged -= OnSyncedWarfareYamlChanged; } _watcher?.Dispose(); _harmony.UnpatchSelf(); } internal static void ApplyToObjectDb(ObjectDB objectDb, bool logMissingPrefabWarnings = false) { if (!((Object)(object)objectDb == (Object)null)) { WarfareCompat.ApplyConfiguredEffects(objectDb, _currentEffects, logMissingPrefabWarnings); WarfareThrowableCompat.ApplyToObjectDb(objectDb); WarfareSkillCompat.ApplyToObjectDb(objectDb); } } internal static void ApplyToZNetScene(ZNetScene scene) { if (!((Object)(object)scene == (Object)null)) { if ((Object)(object)ObjectDB.instance != (Object)null) { WarfareCompat.ApplyConfiguredEffects(ObjectDB.instance, _currentEffects, logMissingPrefabWarnings: true); } WarfareThrowableCompat.ApplyToZNetScene(scene); ChainLightningDedupSystem.RestoreVanillaChainLightningBehavior(scene); } } private void SetupWatcher() { _watcher = new FileSystemWatcher(ConfigDirectoryPath, "WarfareTweaks.yml"); _watcher.Changed += ReadConfigValues; _watcher.Created += ReadConfigValues; _watcher.Renamed += ReadConfigValues; _watcher.IncludeSubdirectories = false; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { if (DateTime.Now.Ticks - _lastConfigReloadTime.Ticks >= 10000000) { bool flag; lock (_reloadLock) { flag = ReloadLocalConfigFromDisk(applyToWorld: true); } if (flag) { _lastConfigReloadTime = DateTime.Now; } } } private static bool ReloadLocalConfigFromDisk(bool applyToWorld) { string yamlText; try { WarfareTweaksConfigLoader.EnsureLocalFileExists(); yamlText = File.ReadAllText(WarfareYamlFilePath); } catch (Exception ex) { ModLogger.LogError((object)("Failed to read WarfareTweaks.yml; the last valid configuration remains active: " + ex.Message)); return false; } if (!TryApplyYamlText(yamlText, applyToWorld)) { return false; } PublishSyncedYaml(yamlText); return true; } private static void PublishSyncedYaml(string yamlText) { if (_syncedWarfareYaml != null) { _suppressSyncedYamlChanged = true; try { _syncedWarfareYaml.AssignLocalValue(yamlText); } finally { _suppressSyncedYamlChanged = false; } } } private static void OnSyncedWarfareYamlChanged() { if (!_suppressSyncedYamlChanged && _syncedWarfareYaml != null && !string.IsNullOrWhiteSpace(_syncedWarfareYaml.Value)) { TryApplyYamlText(_syncedWarfareYaml.Value, applyToWorld: true); } } private static bool TryApplyYamlText(string yamlText, bool applyToWorld) { if (!WarfareTweaksConfigLoader.TryParse(yamlText, out Dictionary<string, EffectBehaviorConfig> parsed)) { return false; } _currentEffects = parsed; WarfareCompat.RebuildBuiltInEffects(_currentEffects); if (!applyToWorld) { return true; } if ((Object)(object)ObjectDB.instance != (Object)null) { ApplyToObjectDb(ObjectDB.instance, (Object)(object)ZNetScene.instance != (Object)null); } if ((Object)(object)ZNetScene.instance != (Object)null) { ApplyToZNetScene(ZNetScene.instance); } ModLogger.LogInfo((object)"WarfareTweaks YAML reload complete."); return true; } private static void ApplyEmbeddedDefaultConfig() { string yamlText = WarfareTweaksDefaultYamlResources.Load("WarfareTweaks.yml"); if (!TryApplyYamlText(yamlText, applyToWorld: false)) { throw new InvalidDataException("Embedded default WarfareTweaks.yml is invalid."); } PublishSyncedYaml(yamlText); ModLogger.LogWarning((object)"Using embedded default WarfareTweaks.yml because the local file could not be loaded."); } } internal static class WarfareTweaksProjectileHitContext { internal readonly struct Scope { internal Projectile? Previous { get; } internal Scope(Projectile? previous) { Previous = previous; } } [ThreadStatic] private static Projectile? _currentProjectile; internal static Scope Begin(Projectile projectile) { if ((Object)(object)projectile == (Object)null) { return default(Scope); } Projectile? currentProjectile = _currentProjectile; _currentProjectile = projectile; return new Scope(currentProjectile); } internal static void End(Scope scope) { _currentProjectile = scope.Previous; } internal static bool TryPeek(out ProjectileHitContext context) { Projectile currentProjectile = _currentProjectile; context = (((Object)(object)currentProjectile != (Object)null) ? new ProjectileHitContext(currentProjectile) : default(ProjectileHitContext)); return (Object)(object)currentProjectile != (Object)null; } } internal readonly struct ProjectileHitContext { public Projectile? Projectile { get; } public ProjectileHitContext(Projectile projectile) { Projectile = projectile; } } internal static class WarfareTweaksWarningLog { private static readonly HashSet<string> Reported = new HashSet<string>(StringComparer.OrdinalIgnoreCase); internal static bool TryMarkReported(string key) { if (!string.IsNullOrWhiteSpace(key)) { return Reported.Add(key); } return false; } } internal static class JewelcraftingThrowableCompat { private const string UtilsTypeName = "Jewelcrafting.Utils"; private const string JewelcraftingTypeName = "Jewelcrafting.Jewelcrafting"; private static bool _hooksInstalled; private static bool _reportedFailure; private static FieldInfo? _socketBlacklistField; private static FieldInfo? _prefabBlacklistField; internal static void TryInstallHooks() { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Expected O, but got Unknown if (_hooksInstalled || !TryGetJewelcraftingAssembly(out Assembly jewelcraftingAssembly) || jewelcraftingAssembly == null) { return; } MethodInfo methodInfo = jewelcraftingAssembly.GetType("Jewelcrafting.Utils", throwOnError: false)?.GetMethod("IsSocketableItem", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(ItemData) }, null); MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(JewelcraftingThrowableCompat), "IsSocketableItemPostfix", (Type[])null, (Type[])null); MethodInfo methodInfo3 = AccessTools.DeclaredMethod(typeof(InventoryGui), "UpdateRecipeList", (Type[])null, (Type[])null); MethodInfo methodInfo4 = AccessTools.DeclaredMethod(typeof(JewelcraftingThrowableCompat), "PrepareInventoryBeforeRecipeListPrefix", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null || methodInfo4 == null) { ReportFailure("Jewelcrafting.Utils.IsSocketableItem(ItemData) was not found."); return; } CacheBlacklistFields(jewelcraftingAssembly); Harmony val = new Harmony("sighsorry.WarfareTweaks.JewelcraftingThrowableCompat"); val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); if (methodInfo3 != null) { val.Patch((MethodBase)methodInfo3, new HarmonyMethod(methodInfo4) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { ReportFailure("InventoryGui.UpdateRecipeList was not found; socket tab pre-normalization is unavailable."); } _hooksInstalled = true; WarfareTweaksPlugin.ModLogger.LogInfo((object)"Installed Jewelcrafting compatibility hooks for Warfare throwing axe socketing."); } private static void PrepareInventoryBeforeRecipeListPrefix() { Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val == null) { return; } foreach (ItemData item in val.m_inventory) { if (WarfareThrowableCompat.TryPrepareJewelcraftingSocketableWeapon(item)) { RemovePrefabBlacklistEntry(item); } } } private static void IsSocketableItemPostfix(ItemData item, ref bool __result) { if (!__result && item != null && !IsUserSocketBlacklisted(item) && WarfareThrowableCompat.TryPrepareJewelcraftingSocketableWeapon(item)) { RemovePrefabBlacklistEntry(item); __result = true; } } private static bool IsUserSocketBlacklisted(ItemData item) { string itemPrefabName = WarfareThrowableCompat.GetItemPrefabName(item); if (string.IsNullOrWhiteSpace(itemPrefabName)) { return false; } if (_socketBlacklistField?.GetValue(null) is ConfigEntry<string> val) { string[] array = val.Value.Replace(" ", "").Split(','); for (int i = 0; i < array.Length; i++) { if (string.Equals(array[i], itemPrefabName, StringComparison.OrdinalIgnoreCase)) { return true; } } } return false; } private static void RemovePrefabBlacklistEntry(ItemData item) { string itemPrefabName = WarfareThrowableCompat.GetItemPrefabName(item); if (!string.IsNullOrWhiteSpace(itemPrefabName) && _prefabBlacklistField?.GetValue(null) is ICollection<string> collection && collection.Contains(itemPrefabName)) { collection.Remove(itemPrefabName); } } private static void CacheBlacklistFields(Assembly jewelcraftingAssembly) { Type type = jewelcraftingAssembly.GetType("Jewelcrafting.Jewelcrafting", throwOnError: false); _socketBlacklistField = ((type != null) ? AccessTools.Field(type, "socketBlacklist") : null); _prefabBlacklistField = ((type != null) ? AccessTools.Field(type, "PrefabBlacklist") : null); } private static bool TryGetJewelcraftingAssembly(out Assembly? jewelcraftingAssembly) { jewelcraftingAssembly = null; if (Chainloader.PluginInfos.TryGetValue("org.bepinex.plugins.jewelcrafting", out var value)) { jewelcraftingAssembly = ((object)value.Instance)?.GetType().Assembly; if (jewelcraftingAssembly != null) { return true; } } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetType("Jewelcrafting.Utils", throwOnError: false) != null) { jewelcraftingAssembly = assembly; return true; } } return false; } private static void ReportFailure(string message) { if (!_reportedFailure) { _reportedFailure = true; WarfareTweaksPlugin.ModLogger.LogWarning((object)("Jewelcrafting Warfare throwable compatibility skipped: " + message)); } } } internal static class WarfareCompat { private sealed class ConfiguredWarfareEffectLookup { public WarfareBuiltInEffectRegistration Registration { get; } public EffectBehaviorConfig EffectConfig { get; } public EffectBehaviorOverrideConfig? PrefabOverride { get; } public ConfiguredWarfareEffectLookup(WarfareBuiltInEffectRegistration registration, EffectBehaviorConfig effectConfig, EffectBehaviorOverrideConfig? prefabOverride) { Registration = registration; EffectConfig = effectConfig; PrefabOverride = prefabOverride; } } private sealed class WarfareTargetAccessors { public FieldInfo? TargetsField { get; } public MethodInfo[] AddToItemMethods { get; } public WarfareTargetAccessors(Type effectType) { TargetsField = effectType.GetField("Targets", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); AddToItemMethods = (from method in effectType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where method.Name == "AddToItem" select method).ToArray(); } } private sealed class WarfareTargetSetAccessors { public MethodInfo? ContainsMethod { get; } public MethodInfo? AddMethod { get; } public MethodInfo? RemoveMethod { get; } public WarfareTargetSetAccessors(Type targetType) { ContainsMethod = targetType.GetMethod("Contains", new Type[1] { typeof(string) }); AddMethod = targetType.GetMethod("Add", new Type[1] { typeof(string) }); RemoveMethod = targetType.GetMethod("Remove", new Type[1] { typeof(string) }); } } private sealed class WarfareBuiltInEffectRegistration { private readonly Dictionary<string, WarfareDefaultAssignment> _defaultAssignmentsByPrefabName; public string Id { get; } public WarfareEffectTypeSpec[] EffectTypeSpecs { get; } public string[] EffectTypeNames => EffectTypeSpecs.Select((WarfareEffectTypeSpec spec) => spec.EffectTypeName).ToArray(); public string[] PrefabNames { get; } public string[] EffectIds { get; } public WarfareBuiltInEffectRegistration(string id, string patchTypeName, string[] prefabSpecs, params string[] aliases) : this(id, new WarfareEffectTypeSpec[1] { new WarfareEffectTypeSpec("Warfare_StatusEffects.StatusEffects", patchTypeName, prefabSpecs) }, aliases) { } public WarfareBuiltInEffectRegistration(string id, WarfareEffectTypeSpec[] effectTypeSpecs, params string[] aliases) { Id = id; EffectTypeSpecs = (effectTypeSpecs ?? Array.Empty<WarfareEffectTypeSpec>()).Where((WarfareEffectTypeSpec spec) => spec != null && !string.IsNullOrWhiteSpace(spec.EffectTypeName)).ToArray(); if (EffectTypeSpecs.Length == 0) { EffectTypeSpecs = new WarfareEffectTypeSpec[1] { new WarfareEffectTypeSpec("Warfare_StatusEffects.StatusEffects", id, Array.Empty<string>()) }; } _defaultAssignmentsByPrefabName = ParsePrefabSpecs(EffectTypeSpecs); PrefabNames = _defaultAssignmentsByPrefabName.Keys.ToArray(); EffectIds = new string[1] { id }.Concat(aliases).ToArray(); } public int? GetDefaultValue(string prefabName) { if (!_defaultAssignmentsByPrefabName.TryGetValue(prefabName, out WarfareDefaultAssignment value)) { return null; } return value.Value; } public string? GetDefaultEffectTypeName(string prefabName) { if (!_defaultAssignmentsByPrefabName.TryGetValue(prefabName, out WarfareDefaultAssignment value)) { return null; } return value.EffectTypeName; } private static Dictionary<string, WarfareDefaultAssignment> ParsePrefabSpecs(IEnumerable<WarfareEffectTypeSpec> effectTypeSpecs) { Dictionary<string, WarfareDefaultAssignment> dictionary = new Dictionary<string, WarfareDefaultAssignment>(StringComparer.OrdinalIgnoreCase); foreach (WarfareEffectTypeSpec effectTypeSpec in effectTypeSpecs) { string[] prefabSpecs = effectTypeSpec.PrefabSpecs; foreach (string text in prefabSpecs) { if (string.IsNullOrWhiteSpace(text)) { continue; } string[] array = text.Split(new char[1] { ':' }, 2); string text2 = array[0].Trim(); if (!string.IsNullOrWhiteSpace(text2)) { int? value = null; if (array.Length == 2 && int.TryParse(array[1].Trim(), out var result)) { value = result; } dictionary[text2] = new WarfareDefaultAssignment(effectTypeSpec.EffectTypeName, value); } } } return dictionary; } } private sealed class WarfareEffectTypeSpec { public string StatusEffectsNamespace { get; } public string PatchTypeName { get; } public string EffectTypeName { get; } public string CharacterDamagePatchTypeName => StatusEffectsNamespace + "." + PatchTypeName + "+Character_Damage_Patch"; public string[] PrefabSpecs { get; } public WarfareEffectTypeSpec(string statusEffectsNamespace, string patchTypeName, string[] prefabSpecs) { StatusEffectsNamespace = statusEffectsNamespace; PatchTypeName = patchTypeName; PrefabSpecs = prefabSpecs ?? Array.Empty<string>(); EffectTypeName = StatusEffectsNamespace + "." + PatchTypeName; } } private sealed class WarfareDefaultAssignment { public string EffectTypeName { get; } public int? Value { get; } public WarfareDefaultAssignment(string effectTypeName, int? value) { EffectTypeName = effectTypeName; Value = value; } } private sealed class WarfareAppliedAssignment { public string EffectTypeName { get; } public string PrefabName { get; } public bool UsesValueTarget { get; } public bool HadOriginalTarget { get; } public int? OriginalValue { get; } public int? AppliedValue { get; } public WarfareAppliedAssignment(string effectTypeName, string prefabName, bool usesValueTarget, bool hadOriginalTarget, int? originalValue, int? appliedValue) { EffectTypeName = effectTypeName; PrefabName = prefabName; UsesValueTarget = usesValueTarget; HadOriginalTarget = hadOriginalTarget; OriginalValue = originalValue; AppliedValue = appliedValue; } } private sealed class WarfareAttackSpawnOverrideState { public Attack Attack { get; } public GameObject? OriginalSpawnOnHit { get; } public float OriginalSpawnOnHitChance { get; } public WarfareAttackSpawnOverrideState(Attack attack, GameObject? originalSpawnOnHit, float originalSpawnOnHitChance) { Attack = attack; OriginalSpawnOnHit = originalSpawnOnHit; OriginalSpawnOnHitChance = originalSpawnOnHitChance; } } private sealed class WarfareAoeOverrideState { public Aoe Aoe { get; } public DamageTypes OriginalDamage { get; } public float OriginalRadius { get; } public float OriginalTtl { get; } public float OriginalHitInterval { get; } public WarfareAoeOverrideState(Aoe aoe, DamageTypes originalDamage, float originalRadius, float originalTtl, float originalHitInterval) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) Aoe = aoe; OriginalDamage = originalDamage; OriginalRadius = originalRadius; OriginalTtl = originalTtl; OriginalHitInterval = originalHitInterval; } } private sealed class WarfareAttackStatusEffectOverrideState { public ItemDrop ItemDrop { get; } public StatusEffect OriginalAttackStatusEffect { get; } public WarfareAttackStatusEffectOverrideState(ItemDrop itemDrop, StatusEffect originalAttackStatusEffect) { ItemDrop = itemDrop; OriginalAttackStatusEffect = originalAttackStatusEffect; } } private sealed class WarfareBleedTuning { public string PrefabName { get; set; } = ""; public int? StacksRequired { get; set; } public float? StackWindow { get; set; } public float? Duration { get; set; } public float? TickInterval { get; set; } public float? DamageFactor { get; set; } public int? NativeValue { get; set; } public float? SourceDamage { get; private set; } public bool HasAnyValue { get { if (!StacksRequired.HasValue && !StackWindow.HasValue && !Duration.HasValue && !TickInterval.HasValue) { return DamageFactor.HasValue; } return true; } } public bool UseSourceDamageDot => DamageFactor.HasValue; public void SetSourceDamage(float sourceDamage) { SourceDamage = Mathf.Max(0f, sourceDamage); } } private sealed class WarfareHasteTuningState { public float MoveSpeedMultiplier { get; } public WarfareHasteTuningState(float moveSpeedMultiplier) { MoveSpeedMultiplier = moveSpeedMultiplier; } } private sealed class WarfareDotSourceDamageContext { private readonly List<WarfareBleedTuning> _tunings = new List<WarfareBleedTuning>(); public Character Target { get; } public string WeaponPrefabName { get; } public float SourceDamage { get; } public float HealthBefore { get; } public WarfareDotSourceDamageContext(Character target, string weaponPrefabName, float sourceDamage, float healthBefore) { Target = target; WeaponPrefabName = weaponPrefabName; SourceDamage = sourceDamage; HealthBefore = healthBefore; } public void RegisterTuning(WarfareBleedTuning tuning) { if (!_tunings.Contains(tuning)) { _tunings.Add(tuning); } } public void ApplyActualDamage(float actualDamage) { foreach (WarfareBleedTuning tuning in _tunings) { tuning.SetSourceDamage(actualDamage); } } } private sealed class ItemPrefabNameCacheEntry { public string PrefabName { get; } public ItemPrefabNameCacheEntry(string prefabName) { PrefabName = prefabName; } } private const string WarfareStatusEffectsNamespace = "Warfare_StatusEffects.StatusEffects"; private const string WarfareFireAndIceStatusEffectsNamespace = "WarfareFireAndIce_StatusEffects.StatusEffects"; private const string WarfareUtilsTypeName = "Warfare_StatusEffects.StatusEffects.WarfareUtils"; private static readonly Dictionary<string, HashSet<string>> SuppressedEffectsByPrefabName = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<MethodBase, string> PatchedPrefixMethods = new Dictionary<MethodBase, string>(); private static readonly Dictionary<MethodBase, string> PatchedAddToItemMethods = new Dictionary<MethodBase, string>(); private static readonly Dictionary<string, WarfareAppliedAssignment> AppliedConfiguredAssignments = new Dictionary<string, WarfareAppliedAssignment>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, WarfareAttackSpawnOverrideState> AppliedAttackSpawnOverrides = new Dictionary<string, WarfareAttackSpawnOverrideState>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, WarfareAoeOverrideState> AppliedAoeOverrides = new Dictionary<string, WarfareAoeOverrideState>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, WarfareAttackStatusEffectOverrideState> AppliedAttackStatusEffectOverrides = new Dictionary<string, WarfareAttackStatusEffectOverrideState>(StringComparer.OrdinalIgnoreCase); private static readonly ConditionalWeakTable<StatusEffect, WarfareBleedTuning> BleedTuningsByStatus = new ConditionalWeakTable<StatusEffect, WarfareBleedTuning>(); private static readonly ConditionalWeakTable<StatusEffect, WarfareHasteTuningState> HasteTuningsByStatus = new ConditionalWeakTable<StatusEffect, WarfareHasteTuningState>(); private static readonly List<WarfareDotSourceDamageContext> ActiveDotSourceDamageContexts = new List<WarfareDotSourceDamageContext>(); private static readonly Dictionary<string, ConfiguredWarfareEffectLookup> ConfiguredEffectsByPrefabAndEffectId = new Dictionary<string, ConfiguredWarfareEffectLookup>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Type> LoadedTypesByName = new Dictionary<string, Type>(StringComparer.Ordinal); private static readonly Dictionary<Type, WarfareTargetAccessors> TargetAccessorsByEffectType = new Dictionary<Type, WarfareTargetAccessors>(); private static readonly Dictionary<Type, WarfareTargetSetAccessors> TargetSetAccessorsByType = new Dictionary<Type, WarfareTargetSetAccessors>(); private static readonly int WarfareHasteStackingHash = StringExtensionMethods.GetStableHashCode("Warfare_Haste_Stacking"); private static readonly int WarfareFireAndIceHasteStackingHash = StringExtensionMethods.GetStableHashCode("WarfareFireAndIce_Haste_Stacking"); private const float WarfareSourceDamageDotDuration = 10f; private const float WarfareSourceDamageDotTickInterval = 1f; private static float? _pendingHasteMoveSpeedMultiplier; private static bool _allowWarfareAddToItem; private static bool _hooksInstalled; private static readonly string[] ChainLightningMistlandsDefaultPrefabs = new string[11] { "GreatbowDvergr_TW", "BastardDvergr_TW", "AxeDvergr_TW", "BattleaxeDvergr_TW", "BattlehammerDvergr_TW", "ClaymoreDvergr_TW", "FistDvergr_TW", "LanceDvergr_TW", "MaceDvergr_TW", "ThrowAxeDvergr_TW", "WarpikeDvergr_TW" }; private static readonly WarfareBuiltInEffectRegistration[] BuiltInRegistrations = new WarfareBuiltInEffectRegistration[22] { RegisterWarfareAndFireAndIce("adrenaline", "Adrenaline", new string[3] { "KnifeViper_TW:10", "FistBlackmetal_TW:14", "FistDvergr_TW:16" }, new string[5] { "DualHammerRageHatred_TW:20", "DualKnifeNjord_TW:20", "DualKnifeSurtr_TW:20", "KnifeNjord_TW:20", "KnifeSurtr_TW:20" }), RegisterWarfareAndFireAndIce("bash", "Bash", new string[5] { "SledgeBonemass_TW:3", "SledgeSilver_TW:2", "SledgeBlackmetal_TW:3", "SledgeDemolisher_TW:4", "SledgeFlametal_TW:5" }, new string[2] { "SledgeNjord_TW:6", "SledgeSurtr_TW:6" }), new WarfareBuiltInEffectRegistration("bleeding", "Bleeding", new string[1] { "FistSilver_TW:1" }), RegisterWarfareAndFireAndIce("bleedingSecondary", "BleedingSecondaryAttack", new string[2] { "FistQueen_TW:2", "FistChitin_TW:1" }, new string[2] { "DualSwordSkadi_TW:1", "DualSpearSvigaFrekk_TW:1" }, "bleeding_secondary"), RegisterWarfareAndFireAndIce("decapitator4", "Decapitator4", new string[5] { "BastardDvergr_TW:100", "BastardFlametal_TW:100", "BattleaxeDvergr_TW:100", "BattleaxeFlametal_TW:100", "DualSwordScimitar_TW:100" }, new string[5] { "BastardSurtr_TW:100", "BastardNjord_TW:100", "BattleaxeSurtr_TW:100", "BattleaxeNjord_TW:100", "DualSwordSkadi_TW:100" }), RegisterWarfareAndFireAndIce("decapitator5", "Decapitator5", new string[2] { "FistFlametal_TW:100", "ThrowAxeDvergr_TW:100" }, new string[3] { "ThrowAxeSurtr_TW:100", "ThrowAxeNjord_TW:100", "DualAxeKrom_TW:100" }), RegisterWarfareAndFireAndIce("executioner", "Executioner", new string[1] { "ClaymoreDvergr_TW:15" }, new string[6] { "ClaymoreNjord_TW:20", "ClaymoreSurtr_TW:20", "BattleaxeDragon_TW:25", "VolcanicBlade_TW:25", "GlacierBlade_TW:25", "LightningBlade_TW:25" }), new WarfareBuiltInEffectRegistration("hackAndSlash", "HacknSlash", new string[1] { "FistQueen_TW:100" }, "hacknslash", "hacknSlash"), new WarfareBuiltInEffectRegistration("haste", "Haste", new string[2] { "KnifeViper_TW", "FistBlackmetal_TW" }), RegisterWarfareAndFireAndIce("impale", "Impale", new string[2] { "LanceBlackmetal_TW:1", "LanceDvergr_TW:2" }, new string[3] { "LanceNjord_TW:1", "LanceSurtr_TW:1", "ClaymoreJotunn_TW:1" }), RegisterWarfareAndFireAndIce("juggernaut", "Juggernaut", new string[7] { "WarpikeBone_TW:100", "WarpikeElder_TW:100", "WarpikeChitin_TW:100", "WarpikeObsidian_TW:100", "WarpikeBlackmetal_TW:100", "WarpikeDvergr_TW:100", "WarpikeFlametal_TW:100" }, new string[3] { "WarpikeNjord_TW:100", "WarpikeSurtr_TW:100", "DualSpearSvigaFrekk_TW:100" }), new WarfareBuiltInEffectRegistration("pinning", "Pinned", new string[1] { "GreatbowModer_TW:4" }, "pinned"), new WarfareBuiltInEffectRegistration("piercingGreatbowMistlands", "PiercingGreatbowMistlands", new string[1] { "GreatbowDvergr_TW:100" }, "piercing_greatbow_mistlands"), new WarfareBuiltInEffectRegistration("piercingGreatbowModer", "PiercingGreatbowModer", new string[1] { "GreatbowModer_TW:100" }, "piercing_greatbow_moder"), new WarfareBuiltInEffectRegistration("piercingGreatbowPlains", "PiercingGreatbowPlains", new string[1] { "GreatbowBlackmetal_TW:100" }, "piercing_greatbow_plains"), RegisterWarfareAndFireAndIce("smasher", "Smasher", new string[2] { "BattlehammerDvergr_TW:100", "BattlehammerElder_TW:100" }, new string[2] { "BattlehammerNjord_TW:100", "BattlehammerSurtr_TW:100" }), new WarfareBuiltInEffectRegistration("vampirism", "Vampirism", new string[1] { "ScytheVampiric_TW:6" }), new WarfareBuiltInEffectRegistration("bludgeoner", new WarfareEffectTypeSpec[1] { new WarfareEffectTypeSpec("WarfareFireAndIce_StatusEffects.StatusEffects", "Bludgeoner", new string[1] { "DualHammerRageHatred_TW:100" }) }), new WarfareBuiltInEffectRegistration("burningSecondary", new WarfareEffectTypeSpec[1] { new WarfareEffectTypeSpec("WarfareFireAndIce_StatusEffects.StatusEffects", "BurningSecondaryAttack", new string[1] { "FistSurtr_TW:1" }) }, "burning_secondary"), new WarfareBuiltInEffectRegistration("lightningBurst", new WarfareEffectTypeSpec[1] { new WarfareEffectTypeSpec("WarfareFireAndIce_StatusEffects.StatusEffects", "LightningBurst", new string[2] { "DualHammerStormstrike_TW:1", "FistFenrir_TW:1" }) }), new WarfareBuiltInEffectRegistration("pierceGreatbowFireAndIce", new WarfareEffectTypeSpec[1] { new WarfareEffectTypeSpec("WarfareFireAndIce_StatusEffects.StatusEffects", "PierceGreatbow", new string[2] { "GreatbowNjord_TW:100", "GreatbowSurtr_TW:100" }) }, "pierceGreatbow", "piercingGreatbowFireAndIce"), new WarfareBuiltInEffectRegistration("smashAndBash", new WarfareEffectTypeSpec[1] { new WarfareEffectTypeSpec("WarfareFireAndIce_StatusEffects.StatusEffects", "SmashnBash", new string[1] { "FistNjord_TW:100" }) }, "smashnBash") }; private static readonly Dictionary<string, WarfareBuiltInEffectRegistration> RegistrationsByEffectId = BuildRegistrationLookup(); private static readonly ConditionalWeakTable<SharedData, ItemPrefabNameCacheEntry> ItemPrefabNamesBySharedData = new ConditionalWeakTable<SharedData, ItemPrefabNameCacheEntry>(); private static readonly Dictionary<string, List<ConfiguredWarfareEffectLookup>> ConfiguredTooltipEffectsByPrefabName = new Dictionary<string, List<ConfiguredWarfareEffectLookup>>(StringComparer.OrdinalIgnoreCase); private static WarfareBuiltInEffectRegistration RegisterWarfareAndFireAndIce(string id, string patchTypeName, string[] warfarePrefabSpecs, string[] fireAndIcePrefabSpecs, params string[] aliases) { return new WarfareBuiltInEffectRegistration(id, new WarfareEffectTypeSpec[2] { new WarfareEffectTypeSpec("Warfare_StatusEffects.StatusEffects", patchTypeName, warfarePrefabSpecs), new WarfareEffectTypeSpec("WarfareFireAndIce_StatusEffects.StatusEffects", patchTypeName, fireAndIcePrefabSpecs) }, aliases); } private static Dictionary<string, WarfareBuiltInEffectRegistration> BuildRegistrationLookup() { Dictionary<string, WarfareBuiltInEffectRegistration> dictionary = new Dictionary<string, WarfareBuiltInEffectRegistration>(StringComparer.OrdinalIgnoreCase); WarfareBuiltInEffectRegistration[] builtInRegistrations = BuiltInRegistrations; foreach (WarfareBuiltInEffectRegistration warfareBuiltInEffectRegistration in builtInRegistrations) { string[] effectIds = warfareBuiltInEffectRegistration.EffectIds; for (int j = 0; j < effectIds.Length; j++) { string text = effectIds[j]?.Trim() ?? ""; if (!string.IsNullOrWhiteSpace(text) && !dictionary.ContainsKey(text)) { dictionary.Add(text, warfareBuiltInEffectRegistration); } } } return dictionary; } internal static void TryInstallHooks() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown if (!_hooksInstalled) { Harmony harmony = new Harmony("sighsorry.WarfareTweaks"); int num = TryInstallAttackWeaponContextHook(harmony); num += TryInstallBuiltInDirectHitGateHooks(harmony); num += TryInstallAddToItemGateHooks(harmony); num += TryInstallBleedingTuningHooks(harmony); num += TryInstallFixedDamageTuningHooks(harmony); num += TryInstallHasteTuningHooks(harmony); num += TryInstallAdrenalineTuningHooks(harmony); if (num > 0) { _hooksInstalled = true; WarfareTweaksPlugin.ModLogger.LogInfo((object)$"Installed {num} Warfare built-in effect hook(s)."); } } } internal static void RebuildBuiltInEffects(IReadOnlyDictionary<string, EffectBehaviorConfig> effectConfigs) { SuppressedEffectsByPrefabName.Clear(); RebuildConfiguredEffectLookup(effectConfigs); WarfareBuiltInEffectRegistration[] builtInRegistrations = BuiltInRegistrations; foreach (WarfareBuiltInEffectRegistration registration in builtInRegistrations) { if (!TryFindConfiguredEffect(effectConfigs, registration, out EffectBehaviorConfig effectConfig) || effectConfig == null) { AddSuppressedDefaultAssignments(registration); } else { AddSuppressedMissingDefaultAssignments(registration, effectConfig); } } } internal static void ApplyConfiguredEffects(ObjectDB objectDb, IReadOnlyDictionary<string, EffectBehaviorConfig> effectConfigs, bool logMissingPrefabWarnings = false) { List<(string, EffectBehaviorConfig)> list = (from entry in effectConfigs where entry.Value != null select (entry.Key.Trim(), Value: entry.Value) into entry where !string.IsNullOrWhiteSpace(entry.Item1) select entry).ToList(); ResetAppliedConfiguredAssignments(); ResetAppliedPrefabOverrides(); if (!IsWarfareLoaded()) { if (list.Count > 0 && WarfareTweaksWarningLog.TryMarkReported("warfare_effects_missing_warfare")) { WarfareTweaksPlugin.ModLogger.LogWarning((object)"Skipping WarfareTweaks.yml warfare effect assignments: Warfare is not installed."); } return; } EnsureWarfareGreatbowStatusEffects(objectDb); int num = RemoveBuiltInTargetAssignments(effectConfigs); if (num > 0) { WarfareTweaksPlugin.ModLogger.LogInfo((object)string.Format("Removed {0} Warfare built-in effect target assignment(s) before applying {1}.", num, "WarfareTweaks.yml")); } int num2 = SuppressNativeAttackStatusEffects(objectDb, effectConfigs); if (num2 > 0) { WarfareTweaksPlugin.ModLogger.LogInfo((object)string.Format("Removed {0} Warfare native attack status effect assignment(s) before applying {1}.", num2, "WarfareTweaks.yml")); } int appliedCount = 0; if (!TryFindConfiguredChainLightningEffect(effectConfigs, out string effectId, out EffectBehaviorConfig _)) { SuppressDefaultChainLightningAssignments(objectDb, ref appliedCount); } if (list.Count == 0) { if (appliedCount > 0) { WarfareTweaksPlugin.ModLogger.LogInfo((object)$"Applied {appliedCount} configured Warfare effect assignment(s)."); } return; } foreach (var (text, effectBehaviorConfig) in list) { if (TryApplyChainLightningConfig(objectDb, text, effectBehaviorConfig, logMissingPrefabWarnings, out var appliedCount2)) { appliedCount += appliedCount2; continue; } if (!TryFindRegistration(text, out WarfareBuiltInEffectRegistration registration)) { if (WarfareTweaksWarningLog.TryMarkReported("warfare_effect_unknown_" + text)) { WarfareTweaksPlugin.ModLogger.LogWarning((object)("Skipping warfare effect '" + text + "': no matching Warfare built-in effect is known.")); } continue; } WarfareBuiltInEffectRegistration warfareBuiltInEffectRegistration = registration; if (!TryResolveAnyLoadedEffectTypeName(warfareBuiltInEffectRegistration, out string effectTypeName)) { if (!ShouldSilentlySkipMissingOptionalEffect(warfareBuiltInEffectRegistration, effectBehaviorConfig) && WarfareTweaksWarningLog.TryMarkReported("warfare_effect_type_missing_" + warfareBuiltInEffectRegistration.Id)) { WarfareTweaksPlugin.ModLogger.LogWarning((object)("Skipping warfare effect '" + warfareBuiltInEffectRegistration.Id + "': none of its known Warfare type(s) were found.")); } continue; } foreach (KeyValuePair<string, EffectBehaviorOverrideConfig> item in effectBehaviorConfig.Prefabs ?? new Dictionary<string, EffectBehaviorOverrideConfig>()) { item.Deconstruct(out effectId, out var value); string text2 = effectId; EffectBehaviorOverrideConfig prefabOverride = value; string text3 = text2?.Trim() ?? ""; if (string.IsNullOrWhiteSpace(text3)) { continue; } if (!ContainsObjectDbItem(objectDb, text3)) { if (!ShouldSilentlySkipMissingOptionalDefaultPrefab(warfareBuiltInEffectRegistration, text3) && logMissingPrefabWarnings && WarfareTweaksWarningLog.TryMarkReported("warfare_effect_prefab_missing_" + warfareBuiltInEffectRegistration.Id + "_" + text3)) { WarfareTweaksPlugin.ModLogger.LogWarning((object)("Skipping warfare effect '" + warfareBuiltInEffectRegistration.Id + "' on '" + text3 + "': prefab was not found in ObjectDB.")); } continue; } string text4 = ResolveEffectTypeNameForPrefab(warfareBuiltInEffectRegistration, text3) ?? effectTypeName; Type type = FindLoadedType(text4); if (type == null) { if (WarfareTweaksWarningLog.TryMarkReported("warfare_effect_type_missing_" + warfareBuiltInEffectRegistration.Id + "_" + text3)) { WarfareTweaksPlugin.ModLogger.LogWarning((object)("Skipping warfare effect '" + warfareBuiltInEffectRegistration.Id + "' on '" + text3 + "': Warfare type '" + text4 + "' was not found.")); } continue; } int? value2 = ResolveConfiguredWarfareValue(warfareBuiltInEffectRegistration, effectBehaviorConfig, prefabOverride, text3); if (!TryApplyTargetAssignment(type, text3, value2, out WarfareAppliedAssignment assignment, out string reason)) { if (WarfareTweaksWarningLog.TryMarkReported("warfare_effect_add_failed_" + warfareBuiltInEffectRegistration.Id + "_" + text3)) { WarfareTweaksPlugin.ModLogger.LogWarning((object)("Skipping warfare effect '" + warfareBuiltInEffectRegistration.Id + "' on '" + text3 + "': " + reason)); } } else { AppliedConfiguredAssignments[warfareBuiltInEffectRegistration.Id + ":" + text3] = assignment; appliedCount++; } } } if (appliedCount > 0) { WarfareTweaksPlugin.ModLogger.LogInfo((object)$"Applied {appliedCount} configured Warfare effect assignment(s)."); } } internal static bool ShouldSuppressBuiltIn(string weaponPrefabName, string effectId) { if (SuppressedEffectsByPrefabName.TryGetValue(weaponPrefabName, out HashSet<string> value)) { return value.Contains(effectId); } return false; } private static bool TryFindConfiguredEffect(IReadOnlyDictionary<string, EffectBehaviorConfig> effectConfigs, WarfareBuiltInEffectRegistration registration, out EffectBehaviorConfig? effectConfig) { effectConfig = null; foreach (KeyValuePair<string, EffectBehaviorConfig> effectConfig2 in effectConfigs) { var (configuredEffectId, effectBehaviorConfig2) = (KeyValuePair<string, EffectBehaviorConfig>)(ref effectConfig2); if (effectBehaviorConfig2 != null && registration.EffectIds.Any((string effectId) => string.Equals(effectId, configuredEffectId?.Trim(), StringComparison.OrdinalIgnoreCase))) { effectConfig = effectBehaviorConfig2; return true; } } return false; } private static void RebuildConfiguredEffectLookup(IReadOnlyDictionary<string, EffectBehaviorConfig> effectConfigs) { ConfiguredEffectsByPrefabAndEffectId.Clear(); ConfiguredTooltipEffectsByPrefabName.Clear(); foreach (KeyValuePair<string, EffectBehaviorConfig> effectConfig in effectConfigs) { effectConfig.Deconstruct(out var key, out var value); string text = key; EffectBehaviorConfig effectBehaviorConfig = value; string effectId = text?.Trim() ?? ""; if (effectBehaviorConfig == null || !TryFindRegistration(effectId, out WarfareBuiltInEffectRegistration registration) || registration == null) { continue; } foreach (string configuredPrefabName in GetConfiguredPrefabNames(effectBehaviorConfig)) { ConfiguredWarfareEffectLookup configuredWarfareEffectLookup = new ConfiguredWarfareEffectLookup(registration, effectBehaviorConfig, TryGetPrefabOverride(effectBehaviorConfig, configuredPrefabName)); ConfiguredEffectsByPrefabAndEffectId[ConfiguredEffectLookupKey(configuredPrefabName, registration.Id)] = configuredWarfareEffectLookup; AddConfiguredTooltipEffect(configuredPrefabName, configuredWarfareEffectLookup); } } } private static bool TryGetConfiguredEffectForCurrentWeapon(string canonicalEffectId, out string prefabName, out ConfiguredWarfareEffectLookup? lookup) { lookup = null; if (!TryGetCurrentAttackWeaponPrefabName(out prefabName)) { return false; } return TryGetConfiguredEffect(prefabName, canonicalEffectId, out lookup); } private static bool TryGetConfiguredEffect(string prefabName, string canonicalEffectId, out ConfiguredWarfareEffectLookup? lookup) { lookup = null; if (string.IsNullOrWhiteSpace(prefabName) || string.IsNullOrWhiteSpace(canonicalEffectId)) { return false; } return ConfiguredEffectsByPrefabAndEffectId.TryGetValue(ConfiguredEffectLookupKey(prefabName, canonicalEffectId), out lookup); } private static string ConfiguredEffectLookupKey(string prefabName, string canonicalEffectId) { return prefabName.Trim() + "\n" + canonicalEffectId.Trim(); } private static IEnumerable<string> GetConfiguredPrefabNames(EffectBehaviorConfig effectConfig) { if (effectConfig.Prefabs == null) { yield break; } foreach (string key in effectConfig.Prefabs.Keys) { string text = key?.Trim() ?? ""; if (!string.IsNullOrWhiteSpace(text)) { yield return text; } } } private static bool HasPrefabAssignment(EffectBehaviorConfig effectConfig, string prefabName) { if (effectConfig.Prefabs != null && !string.IsNullOrWhiteSpace(prefabName)) { return effectConfig.Prefabs.Keys.Any((string configuredPrefabName) => string.Equals(configuredPrefabName?.Trim(), prefabName, StringComparison.OrdinalIgnoreCase)); } return false; } private static bool TryFindConfiguredChainLightningEffect(IReadOnlyDictionary<string, EffectBehaviorConfig> effectConfigs, out string effectId, out EffectBehaviorConfig? effectConfig) { effectId = ""; effectConfig = null; foreach (KeyValuePair<string, EffectBehaviorConfig> effectConfig2 in effectConfigs) { effectConfig2.Deconstruct(out var key, out var value); string text = key; EffectBehaviorConfig effectBehaviorConfig = value; string text2 = text?.Trim() ?? ""; if (effectBehaviorConfig != null && IsChainLightningConfig(text2, effectBehaviorConfig)) { effectId = text2; effectConfig = effectBehaviorConfig; return true; } } return false; } private static void AddSuppressedDefaultAssignments(WarfareBuiltInEffectRegistration registration) { string[] prefabNames = registration.PrefabNames; for (int i = 0; i < prefabNames.Length; i++) { AddSuppressedEffect(prefabNames[i], registration.Id); } } private static void AddSuppressedMissingDefaultAssignments(WarfareBuiltInEffectRegistration registration, EffectBehaviorConfig effectConfig) { string[] prefabNames = registration.PrefabNames; foreach (string prefabName in prefabNames) { if (!HasPrefabAssignment(effectConfig, prefabName)) { AddSuppressedEffect(prefabName, registration.Id); } } } private static void AddSuppressedEffect(string prefabName, string effectId) { if (!SuppressedEffectsByPrefabName.TryGetValue(prefabName, out HashSet<string> value)) { value = new HashSet<string>(StringComparer.OrdinalIgnoreCase); SuppressedEffectsByPrefabName[prefabName] = value; } value.Add(effectId); } private static bool SuppressBuiltInPrefix(MethodBase __originalMethod) { if (!(__originalMethod == null) && PatchedPrefixMethods.TryGetValue(__originalMethod, out string value) && DirectWeaponHitContextSystem.TryGetCurrentWeaponPrefabName(out string prefabName)) { return !ShouldSuppressBuiltIn(prefabName, value); } return true; } private static int TryInstallAttackWeaponContextHook(Harmony harmony) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown Type type = FindLoadedType("Warfare_StatusEffects.StatusEffects.WarfareUtils"); MethodInfo methodInfo = ((type != null) ? AccessTools.DeclaredMethod(type, "TryGetAttackWeapon", (Type[])null, (Type[])null) : null); if (methodInfo == null) { return 0; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(WarfareCompat), "WarfareTryGetAttackWeaponPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return 1; } private static bool WarfareTryGetAttackWeaponPrefix(ref string prefab, ref bool __result) { if (!DirectWeaponHitContextSystem.TryGetCurrentWeaponPrefabName(out string prefabName)) { return true; } prefab = prefabName; __result = true; return false; } private static int TryInstallBuiltInDirectHitGateHooks(Harmony harmony) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown int num = 0; HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); WarfareBuiltInEffectRegistration[] builtInRegistrations = BuiltInRegistrations; foreach (WarfareBuiltInEffectRegistration warfareBuiltInEffectRegistration in builtInRegistrations) { WarfareEffectTypeSpec[] effectTypeSpecs = warfareBuiltInEffectRegistration.EffectTypeSpecs; for (int j = 0; j < effectTypeSpecs.Length; j++) { string characterDamagePatchTypeName = effectTypeSpecs[j].CharacterDamagePatchTypeName; if (!hashSet.Add(characterDamagePatchTypeName)) { continue; } Type type = FindLoadedType(characterDamagePatchTypeName); if (!(type == null)) { MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "Prefix", (Type[])null, (Type[])null); if (!(methodInfo == null)) { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(WarfareCompat), "WarfareBuiltInDirectHitGatePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); PatchedPrefixMethods[methodInfo] = warfareBuiltInEffectRegistration.Id; num++; } } } } return num; } private static bool WarfareBuiltInDirectHitGatePrefix(MethodBase __originalMethod) { int num; if (DirectWeaponHitContextSystem.ShouldCountWeaponEffectHit) { num = (SuppressBuiltInPrefix(__originalMethod) ? 1 : 0); if (num != 0 && __originalMethod != null && PatchedPrefixMeth