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.0
WarfareTweaks.dll
Decompiled 2 days 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.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.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 { public class Localizer { private static readonly Dictionary<string, Dictionary<string, string>> LoadedTexts; private static readonly ConditionalWeakTable<Localization, string> LocalizationLanguage; private static readonly List<WeakReference<Localization>> LocalizationObjects; private static readonly string[] FileExtensions; private static BaseUnityPlugin? _plugin; private static BaseUnityPlugin Plugin { get { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown if ((Object)(object)_plugin == (Object)null) { IEnumerable<TypeInfo> source; try { source = Assembly.GetExecutingAssembly().DefinedTypes.ToList(); } catch (ReflectionTypeLoadException ex) { source = from type in ex.Types where type != null select type.GetTypeInfo(); } _plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First((TypeInfo type) => type.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(type))); } return _plugin; } } public static void Load() { _ = Plugin; if (Localization.instance != null) { LoadLocalization(Localization.instance, Localization.instance.GetSelectedLanguage()); } } public static void LoadLocalizationLater() { if (Localization.instance != null) { LoadLocalization(Localization.instance, Localization.instance.GetSelectedLanguage()); } } private static void LoadLocalization(Localization __instance, string language) { if (!LocalizationLanguage.Remove(__instance)) { LocalizationObjects.Add(new WeakReference<Localization>(__instance)); } LocalizationLanguage.Add(__instance, 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; } } LoadedTexts[language] = dictionary2; foreach (string key in dictionary2.Keys) { UpdateText(__instance, key); } } 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; } private static void UpdateText(Localization localization, string key) { LocalizationLanguage.TryGetValue(localization, out string value); if (LoadedTexts.TryGetValue(value, out Dictionary<string, string> value2) && value2.TryGetValue(key, out var value3)) { localization.AddWord(key, value3); } } static Localizer() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown LoadedTexts = new Dictionary<string, Dictionary<string, string>>(); LocalizationLanguage = new ConditionalWeakTable<Localization, string>(); LocalizationObjects = new List<WeakReference<Localization>>(); 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(); } } public static class LocalizationManagerVersion { public const string Version = "1.4.0"; } } 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 int Id { get; } public float CreatedAt { get; } public int CreatedFrame { get; } public int SetupCount { get; set; } public HashSet<int> HitTargetIds { get; } = new HashSet<int>(); public ChainLightningActivation(int id, float createdAt, int createdFrame) { Id = id; CreatedAt = createdAt; CreatedFrame = createdFrame; } } private sealed class ChainLightningAoeState { internal static readonly ChainLightningAoeState NotChainLightning = new ChainLightningAoeState(isChainLightning: false, 0); public bool IsChainLightning { get; } public int SourceId { get; } public ChainLightningActivation? Activation { get; set; } public ChainLightningAoeState(bool isChainLightning, int sourceId) { IsChainLightning = isChainLightning; SourceId = sourceId; } } 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; private static int _nextActivationId; [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, Character owner, ItemData item) { if (!((Object)(object)aoe == (Object)null) && TryGetChainLightningState(aoe, out ChainLightningAoeState state)) { ChainLightningActivation? obj = ActiveActivation ?? new ChainLightningActivation(++_nextActivationId, Time.time, Time.frameCount); (state.Activation = obj).SetupCount++; } } 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(++_nextActivationId, Time.time, Time.frameCount); 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 (!TryResolveChainLightningSourceName(aoe, out string sourceName)) { return ChainLightningAoeState.NotChainLightning; } return new ChainLightningAoeState(isChainLightning: true, StringExtensionMethods.GetStableHashCode(NormalizeSourceName(sourceName))); } private static bool TryResolveChainLightningSourceName(Aoe aoe, out string sourceName) { string name = ((Object)aoe).name; Transform transform = ((Component)aoe).transform; bool flag = ContainsChainLightningName(name); bool flag2 = (Object)(object)transform.root != (Object)null && ContainsChainLightningName(((Object)transform.root).name); bool flag3 = (Object)(object)transform.parent != (Object)null && ContainsChainLightningName(((Object)transform.parent).name); if (!flag && !flag2 && !flag3) { sourceName = ""; return false; } sourceName = ((!flag && (Object)(object)transform.root != (Object)null) ? ((Object)transform.root).name : name); 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; } private static string NormalizeSourceName(string name) { return name.Replace("(Clone)", "", StringComparison.OrdinalIgnoreCase).Trim(); } } [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, Character owner, ItemData item) { ChainLightningDedupSystem.TrackSetup(__instance, owner, item); } } [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 Scope(ScopeKind kind) { Kind = kind; } } 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 !WeaponEffectManager.IsApplyingGeneratedEffectDamage; } return false; } } internal static Scope BeginAttackHit(Attack attack) { if ((Object)(object)attack?.m_character != (Object)(object)Player.m_localPlayer) { return default(Scope); } _weaponPrefabName = GetWeaponPrefabName(attack.m_weapon); _directHitDepth++; return new Scope(ScopeKind.DirectHit); } 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); } _weaponPrefabName = GetWeaponPrefabName(ProjectileAccess.GetWeapon(projectile)); _directHitDepth++; return new Scope(ScopeKind.DirectHit); } internal static Scope BeginCharacterDamage() { _characterDamageDepth++; if (_directHitDepth == 0 && WarfareTweaksBridge.TryGetCaptainValheimShieldHitWeaponPrefabName(out string weaponPrefabName)) { _weaponPrefabName = weaponPrefabName; _directHitDepth++; return new Scope(ScopeKind.CharacterDamageWithExternalDirectHit); } return new Scope(ScopeKind.CharacterDamage); } internal static bool TryGetCurrentProjectileWeaponPrefabName(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--; if (_directHitDepth == 0) { _weaponPrefabName = ""; } } break; case ScopeKind.CharacterDamage: if (_characterDamageDepth > 0) { _characterDamageDepth--; } break; case ScopeKind.CharacterDamageWithExternalDirectHit: if (_characterDamageDepth > 0) { _characterDamageDepth--; } if (_directHitDepth > 0) { _directHitDepth--; if (_directHitDepth == 0) { _weaponPrefabName = ""; } } 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, ItemData>? AmmoRef = CreateFieldRef<ItemData>("m_ammo"); 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 FieldRef<Projectile, int>? StatusEffectHashRef = CreateFieldRef<int>("m_statusEffectHash"); private static readonly FieldRef<Projectile, Vector3>? VelocityRef = CreateFieldRef<Vector3>("m_vel"); private static readonly FieldRef<Projectile, bool>? DidHitRef = CreateFieldRef<bool>("m_didHit"); private static readonly FieldInfo? WeaponField = AccessTools.Field(typeof(Projectile), "m_weapon"); private static readonly FieldInfo? AmmoField = AccessTools.Field(typeof(Projectile), "m_ammo"); private static readonly FieldInfo? OwnerField = AccessTools.Field(typeof(Projectile), "m_owner"); private static readonly FieldInfo? OriginalHitDataField = AccessTools.Field(typeof(Projectile), "m_originalHitData"); private static readonly FieldInfo? StatusEffectHashField = AccessTools.Field(typeof(Projectile), "m_statusEffectHash"); private static readonly FieldInfo? VelocityField = AccessTools.Field(typeof(Projectile), "m_vel"); private static readonly FieldInfo? DidHitField = AccessTools.Field(typeof(Projectile), "m_didHit"); 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 ItemData? GetAmmo(Projectile projectile) { if (AmmoRef == null) { object? obj = AmmoField?.GetValue(projectile); return (ItemData?)((obj is ItemData) ? obj : null); } return AmmoRef.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); } internal static int GetStatusEffectHash(Projectile projectile) { if (StatusEffectHashRef == null) { object obj = StatusEffectHashField?.GetValue(projectile); if (obj is int) { return (int)obj; } return 0; } return StatusEffectHashRef.Invoke(projectile); } internal static Vector3 GetVelocity(Projectile projectile) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (VelocityRef == null) { object obj = VelocityField?.GetValue(projectile); if (obj is Vector3) { return (Vector3)obj; } return Vector3.zero; } return VelocityRef.Invoke(projectile); } internal static void SetVelocity(Projectile projectile, Vector3 velocity) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (VelocityRef != null) { VelocityRef.Invoke(projectile) = velocity; } else { VelocityField?.SetValue(projectile, velocity); } } internal static void SetDidHit(Projectile projectile, bool didHit) { if (DidHitRef != null) { DidHitRef.Invoke(projectile) = didHit; } else { DidHitField?.SetValue(projectile, didHit); } } private static FieldRef<Projectile, T>? CreateFieldRef<T>(string fieldName) { try { return AccessTools.FieldRefAccess<Projectile, T>(fieldName); } catch { return null; } } } internal static class WarfareEffectConfigHelpers { internal static bool IsWarfareEffectConfig(EffectBehaviorConfig effectConfig) { string text = effectConfig.Type?.Trim() ?? ""; if (!string.IsNullOrWhiteSpace(text) && !string.Equals(text, "warfare", StringComparison.OrdinalIgnoreCase) && !string.Equals(text, "chainLightning", StringComparison.OrdinalIgnoreCase)) { return string.Equals(text, "chainLightningMistlands", StringComparison.OrdinalIgnoreCase); } return true; } internal static bool HasPrefabAssignment(EffectBehaviorConfig effectConfig, string prefabName) { if (effectConfig.Prefabs == null || string.IsNullOrWhiteSpace(prefabName)) { return false; } return effectConfig.Prefabs.Keys.Any((string configuredPrefabName) => string.Equals(configuredPrefabName?.Trim(), prefabName, StringComparison.OrdinalIgnoreCase)); } } 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() { Localizer.Load(); } 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 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 Dictionary<string, EffectBehaviorConfig> LoadLocalFile() { EnsureLocalFileExists(); return Parse(File.ReadAllText(WarfareTweaksPlugin.WarfareYamlFilePath)); } internal static Dictionary<string, EffectBehaviorConfig> Parse(string yamlText) { Dictionary<string, EffectBehaviorConfig> dictionary = new Dictionary<string, EffectBehaviorConfig>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(yamlText)) { return dictionary; } try { YamlStream yamlStream = new YamlStream(); yamlStream.Load(new StringReader(yamlText)); if (yamlStream.Documents.Count == 0 || !(yamlStream.Documents[0].RootNode is YamlMappingNode yamlMappingNode)) { return dictionary; } foreach (KeyValuePair<YamlNode, YamlNode> child in yamlMappingNode.Children) { string text = (child.Key as YamlScalarNode)?.Value?.Trim() ?? ""; if (!string.IsNullOrWhiteSpace(text)) { try { dictionary[text] = DeserializeEffectBehaviorConfig(text, child.Value); } catch (Exception ex) { WarfareTweaksPlugin.ModLogger.LogWarning((object)("Skipping WarfareTweaks.yml block '" + text + "': " + ex.Message)); } } } } catch (Exception ex2) { WarfareTweaksPlugin.ModLogger.LogError((object)("Failed to parse WarfareTweaks.yml: " + ex2.Message)); } return dictionary; } private static EffectBehaviorConfig DeserializeEffectBehaviorConfig(string rootKey, YamlNode node) { if (!(node is YamlMappingNode yamlMappingNode)) { return DeserializeYamlNode<EffectBehaviorConfig>(node) ?? new EffectBehaviorConfig(); } NormalizePrefabScalarOverrides(rootKey, yamlMappingNode); return DeserializeYamlNode<EffectBehaviorConfig>(yamlMappingNode) ?? new EffectBehaviorConfig(); } 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) { string text2 = yamlScalarNode.Value?.Trim() ?? ""; float result; int result2; if (string.IsNullOrWhiteSpace(text2)) { child.Children[item.Key] = new YamlMappingNode(); } else if (!float.TryParse(text2, NumberStyles.Float, CultureInfo.InvariantCulture, out result)) { WarfareTweaksPlugin.ModLogger.LogWarning((object)("Skipping scalar prefab override in WarfareTweaks.yml block '" + rootKey + "': expected a numeric " + text + ", got '" + text2 + "'.")); } else if (string.Equals(text, "value", StringComparison.OrdinalIgnoreCase) && !int.TryParse(text2, NumberStyles.Integer, CultureInfo.InvariantCulture, out result2)) { WarfareTweaksPlugin.ModLogger.LogWarning((object)("Skipping scalar prefab override in WarfareTweaks.yml block '" + rootKey + "': expected an integer value, got '" + text2 + "'.")); } else { 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 string Trigger { get; set; } = "anyHit"; 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 string DamageType { get; set; } = ""; public string Modifier { get; set; } = "normal"; public ScalarValueConfig Damage { get; set; } = new ScalarValueConfig(); public ScalarValueConfig Heal { get; set; } = new ScalarValueConfig(); public ScalarValueConfig StaminaRestore { get; set; } = new ScalarValueConfig(); public float MoveSpeedMultiplier { get; set; } = 1f; public float HealthThresholdPercent { get; set; } = 25f; public float DamageMultiplier { get; set; } = 1f; public bool ConsumeOnModify { get; set; } public Dictionary<string, EffectBehaviorOverrideConfig>? Prefabs { get; set; } } internal sealed class EffectBehaviorOverrideConfig { public string? Type { get; set; } public int? Value { get; set; } public string? Prefab { get; set; } public string? Trigger { 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; } public string? DamageType { get; set; } public string? Modifier { get; set; } public ScalarValueOverrideConfig? Damage { get; set; } public ScalarValueOverrideConfig? Heal { get; set; } public ScalarValueOverrideConfig? StaminaRestore { get; set; } public float? MoveSpeedMultiplier { get; set; } public float? HealthThresholdPercent { get; set; } public float? DamageMultiplier { get; set; } public bool? ConsumeOnModify { get; set; } } internal sealed class ScalarValueConfig { public string Mode { get; set; } = "fixed"; public float Value { get; set; } } internal sealed class ScalarValueOverrideConfig { public string? Mode { get; set; } public float? Value { get; set; } } internal static class WarfareTweaksConfigExtensions { internal static bool IsOn(this WarfareTweaksPlugin.Toggle value) { return value == WarfareTweaksPlugin.Toggle.On; } } 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, "Destructible.Damage"); } } [HarmonyPatch(typeof(MineRock), "Damage")] internal static class MineRockDamageWarfareThrowablePatch { private static void Prefix(HitData hit) { WarfareThrowableCompat.ApplyProjectileToolTierIfNeeded(hit, "MineRock.Damage"); } } [HarmonyPatch(typeof(MineRock5), "Damage")] internal static class MineRock5DamageWarfareThrowablePatch { private static void Prefix(HitData hit) { WarfareThrowableCompat.ApplyProjectileToolTierIfNeeded(hit, "MineRock5.Damage"); } } [HarmonyPatch(typeof(TreeBase), "Damage")] internal static class TreeBaseDamageWarfareThrowablePatch { private static void Prefix(HitData hit) { WarfareThrowableCompat.ApplyProjectileToolTierIfNeeded(hit, "TreeBase.Damage"); WarfareThrowableCompat.ApplyProjectileWoodCuttingSkillIfNeeded(hit, "TreeBase.Damage"); } } [HarmonyPatch(typeof(TreeLog), "Damage")] internal static class TreeLogDamageWarfareThrowablePatch { private static void Prefix(HitData hit) { WarfareThrowableCompat.ApplyProjectileToolTierIfNeeded(hit, "TreeLog.Damage"); WarfareThrowableCompat.ApplyProjectileWoodCuttingSkillIfNeeded(hit, "TreeLog.Damage"); } } [HarmonyPatch(typeof(WearNTear), "Damage")] internal static class WearNTearDamageWarfareThrowablePatch { private static void Prefix(HitData hit) { WarfareThrowableCompat.ApplyProjectileToolTierIfNeeded(hit, "WearNTear.Damage"); } } [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, "Inventory.RemoveItem(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, int amount, ref bool __result) { if (!WarfareThrowableCompat.ShouldBlockInventoryRemoval(item, "Inventory.RemoveItem(item, amount)", amount)) { 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, "Inventory.RemoveOneItem(item)", 1)) { 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, "Humanoid.UnequipItem(item)"); } } [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, "Humanoid.ConsumeItem(item)", 1)) { 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, int amount) { return !WarfareThrowableCompat.ShouldBlockNamedInventoryRemoval("Inventory.RemoveItem(name, amount, quality, worldLevel)", name, amount); } } [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.0")] [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.0"; 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.0", MinimumRequiredVersion = "1.0.0" }; 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"); internal static IReadOnlyDictionary<string, EffectBehaviorConfig> CurrentEffects => _currentEffects; 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(); WarfareTweaksConfigLoader.EnsureLocalFileExists(); ReloadLocalConfigFromDisk(applyToWorld: false); 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) { DateTime now = DateTime.Now; if (now.Ticks - _lastConfigReloadTime.Ticks >= 10000000) { lock (_reloadLock) { ReloadLocalConfigFromDisk(applyToWorld: true); } _lastConfigReloadTime = now; } } private static void ReloadLocalConfigFromDisk(bool applyToWorld) { WarfareTweaksConfigLoader.EnsureLocalFileExists(); string text = File.ReadAllText(WarfareYamlFilePath); if (_syncedWarfareYaml != null) { _suppressSyncedYamlChanged = true; try { _syncedWarfareYaml.AssignLocalValue(text); } finally { _suppressSyncedYamlChanged = false; } } ApplyYamlText(text, applyToWorld); } private static void OnSyncedWarfareYamlChanged() { if (!_suppressSyncedYamlChanged && _syncedWarfareYaml != null && !string.IsNullOrWhiteSpace(_syncedWarfareYaml.Value)) { ApplyYamlText(_syncedWarfareYaml.Value, applyToWorld: true); } } private static void ApplyYamlText(string yamlText, bool applyToWorld) { _currentEffects = WarfareTweaksConfigLoader.Parse(yamlText); WarfareCompat.RebuildBuiltInEffects(_currentEffects); if (applyToWorld) { 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."); } } } 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 WarfareTweaksRuntimeContext { internal static bool TryPeekProjectileHitContext(out ProjectileHitContext context) { return WarfareTweaksProjectileHitContext.TryPeek(out context); } } internal static class WarfareTweaksRuntimeFacade { internal static bool TryGetProjectileHitAttackContext(out string weaponPrefabName, out bool secondaryAttack, out object? definition, out bool disableCurrentAttackFallback) { weaponPrefabName = ""; secondaryAttack = false; definition = null; disableCurrentAttackFallback = false; return DirectWeaponHitContextSystem.TryGetCurrentProjectileWeaponPrefabName(out weaponPrefabName); } } internal static class WeaponEffectManager { internal static bool IsApplyingGeneratedEffectDamage => WarfareTweaksBridge.IsExternalGeneratedDamageActive; internal static bool ShouldSuppressWarfareBuiltIn(string effectId) { if (DirectWeaponHitContextSystem.TryGetCurrentProjectileWeaponPrefabName(out string prefabName)) { return WarfareCompat.ShouldSuppressBuiltIn(prefabName, effectId); } return false; } } 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); if (WarfareThrowableCompat.DebugLoggingEnabled) { WarfareThrowableCompat.LogDebug("Jewelcrafting pre-normalized Warfare throwable prefab=" + GetItemPrefabName(item) + " shared=" + item.m_shared?.m_name); } } } } private static void IsSocketableItemPostfix(ItemData item, ref bool __result) { if (!__result && item != null && !IsUserSocketBlacklisted(item) && WarfareThrowableCompat.TryPrepareJewelcraftingSocketableWeapon(item)) { RemovePrefabBlacklistEntry(item); __result = true; if (WarfareThrowableCompat.DebugLoggingEnabled) { WarfareThrowableCompat.LogDebug("Jewelcrafting socketability allowed for Warfare throwable prefab=" + GetItemPrefabName(item) + " shared=" + item.m_shared?.m_name); } } } private static bool IsUserSocketBlacklisted(ItemData item) { string itemPrefabName = 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 = 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)); } } private static string GetItemPrefabName(ItemData item) { if ((Object)(object)item.m_dropPrefab == (Object)null) { return string.Empty; } string name = ((Object)item.m_dropPrefab).name; if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length); } } 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 StatusEffectsNamespace => EffectTypeSpecs[0].StatusEffectsNamespace; public string PatchTypeName => EffectTypeSpecs[0].PatchTypeName; public string CharacterDamagePatchTypeName => StatusEffectsNamespace + "." + PatchTypeName + "+Character_Damage_Patch"; public string EffectTypeName => EffectTypeSpecs[0].EffectTypeName; public string[] EffectTypeNames => EffectTypeSpecs.Select((WarfareEffectTypeSpec spec) => spec.EffectTypeName).ToArray(); public string[] PrefabNames { get; } public string[] EffectIds { get; } public bool RequiresValue => _defaultAssignmentsByPrefabName.Values.Any((WarfareDefaultAssignment assignment) => assignment.Value.HasValue); 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; } public bool IsDefaultAssignment(string prefabName, int? value) { if (_defaultAssignmentsByPrefabName.TryGetValue(prefabName, out WarfareDefaultAssignment value2)) { return value2.Value == value; } return false; } 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; set; } public bool HasAnyValue { get { if (!StacksRequired.HasValue && !StackWindow.HasValue && !Duration.HasValue && !TickInterval.HasValue) { return DamageFactor.HasValue; } return true; } } } private sealed class WarfareBleedTuningState { public float? Duration { get; } public float? TickInterval { get; } public float? DamageFactor { get; } public int? NativeValue { get; } public float? SourceDamage { get; private set; } public bool UseSourceDamageDot => DamageFactor.HasValue; public WarfareBleedTuningState(WarfareBleedTuning tuning) { Duration = tuning.Duration; TickInterval = tuning.TickInterval; DamageFactor = tuning.DamageFactor; NativeValue = tuning.NativeValue; SourceDamage = tuning.SourceDamage; } 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 ItemPrefabNameCacheEntry { public string PrefabName { get; } public ItemPrefabNameCacheEntry(string prefabName) { PrefabName = prefabName; } } private sealed class ObjectDbItemNameCache { public int ItemCount { get; } public HashSet<string> ItemNames { get; } public ObjectDbItemNameCache(int itemCount, HashSet<string> itemNames) { ItemCount = itemCount; ItemNames = itemNames; } } private sealed class WarfareDotSourceDamageContext { private readonly List<WarfareBleedTuningState> _states = new List<WarfareBleedTuningState>(); 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 RegisterState(WarfareBleedTuningState state) { if (!_states.Contains(state)) { _states.Add(state); } } public void ApplyActualDamage(float actualDamage) { foreach (WarfareBleedTuningState state in _states) { state.SetSourceDamage(actualDamage); } } } internal const string WarfareGuid = "Therzie.Warfare"; 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<string, HashSet<string>> ManagedEffectsByPrefabName = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> ManagedChainLightningPrefabs = new 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, WarfareBleedTuningState> BleedTuningsByStatus = new ConditionalWeakTable<StatusEffect, WarfareBleedTuningState>(); private static readonly ConditionalWeakTable<StatusEffect, WarfareHasteTuningState> HasteTuningsByStatus = new ConditionalWeakTable<StatusEffect, WarfareHasteTuningState>(); private static readonly ConditionalWeakTable<SharedData, ItemPrefabNameCacheEntry> ItemPrefabNamesBySharedData = new ConditionalWeakTable<SharedData, ItemPrefabNameCacheEntry>(); private static readonly List<WarfareDotSourceDamageContext> ActiveDotSourceDamageContexts = new List<WarfareDotSourceDamageContext>(); private static readonly Dictionary<ObjectDB, ObjectDbItemNameCache> ObjectDbItemNameCaches = new Dictionary<ObjectDB, ObjectDbItemNameCache>(); private static readonly Dictionary<string, ConfiguredWarfareEffectLookup> ConfiguredEffectsByPrefabAndEffectId = new Dictionary<string, ConfiguredWarfareEffectLookup>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, List<ConfiguredWarfareEffectLookup>> ConfiguredTooltipEffectsByPrefabName = new Dictionary<string, List<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 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(); ManagedEffectsByPrefabName.Clear(); ManagedChainLightningPrefabs.Clear(); RebuildConfiguredEffectLookup(effectConfigs); WarfareBuiltInEffectRegistration[] builtInRegistrations = BuiltInRegistrations; string[] prefabNames; foreach (WarfareBuiltInEffectRegistration warfareBuiltInEffectRegistration in builtInRegistrations) { prefabNames = warfareBuiltInEffectRegistration.PrefabNames; for (int j = 0; j < prefabNames.Length; j++) { AddManagedEffect(prefabNames[j], warfareBuiltInEffectRegistration.Id); } if (!TryFindConfiguredEffect(effectConfigs, warfareBuiltInEffectRegistration, out EffectBehaviorConfig effectConfig) || effectConfig == null) { AddSuppressedDefaultAssignments(warfareBuiltInEffectRegistration); continue; } foreach (string configuredPrefabName in GetConfiguredPrefabNames(effectConfig)) { AddManagedEffect(configuredPrefabName, warfareBuiltInEffectRegistration.Id); } AddSuppressedMissingDefaultAssignments(warfareBuiltInEffectRegistration, effectConfig); } prefabNames = ChainLightningMistlandsDefaultPrefabs; foreach (string item in prefabNames) { ManagedChainLightningPrefabs.Add(item); } if (!TryFindConfiguredChainLightningEffect(effectConfigs, out string _, out EffectBehaviorConfig effectConfig2) || effectConfig2 == null) { return; } foreach (string configuredPrefabName2 in GetConfiguredPrefabNames(effectConfig2)) { ManagedChainLightningPrefabs.Add(configuredPrefabName2); } } 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, te