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 Ward Cores v1.0.0
WardCores.dll
Decompiled 12 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ConditionalConfigSync; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using WardCores.Core; using WardCores.Net; using WardCores.Patches; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("WardCores")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("WardCores")] [assembly: AssemblyTitle("WardCores")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 WardCores { internal sealed class ConfigurationManagerAttributes { public Action<ConfigEntryBase> CustomDrawer; } internal static class CoreListDrawer { private sealed class Row { internal string Prefab; internal string Max; } private const float PrefabFieldWidth = 150f; private const float MaxFieldWidth = 60f; private const float RemoveButtonWidth = 24f; private const string ListedTwice = "listed twice"; private const string NotFound = "not found in the game"; private static readonly Color ProblemColor = new Color(1f, 0.5f, 0.5f); private static readonly Color UnknownColor = new Color(0.5f, 0.85f, 1f); private static readonly List<Row> Draft = new List<Row>(); private static string _source; private static string _loadedText; internal static void Draw(ConfigEntryBase entry) { string text = (entry.BoxedValue as string) ?? ""; if (text != _source) { Load(text); } int num = 0; int num2 = -1; GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); for (int i = 0; i < Draft.Count; i++) { Row row = Draft[i]; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); row.Prefab = GUILayout.TextField(row.Prefab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) }); row.Max = GUILayout.TextField(row.Max, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }); string text2 = Problem(i); if (text2 != null) { num++; } Note(text2, row.Prefab.Trim()); if (GUILayout.Button("x", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) })) { num2 = i; } GUILayout.EndHorizontal(); } if (num2 >= 0) { Draft.RemoveAt(num2); } Buttons(entry, text, num); GUILayout.EndVertical(); } private static void Buttons(ConfigEntryBase entry, string value, int problems) { bool flag = DraftText() != _loadedText; bool enabled = GUI.enabled; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("+ Add core", Array.Empty<GUILayoutOption>())) { Draft.Add(new Row { Prefab = "", Max = "" }); } GUI.enabled = enabled && flag && problems == 0; if (GUILayout.Button("Apply", Array.Empty<GUILayoutOption>())) { entry.BoxedValue = CoreTable.Join(Entries()); Load((entry.BoxedValue as string) ?? ""); } GUI.enabled = enabled && flag; if (GUILayout.Button("Revert", Array.Empty<GUILayoutOption>())) { Load(value); } GUI.enabled = enabled; GUILayout.EndHorizontal(); } private static void Load(string value) { Draft.Clear(); foreach (CorePart item in CoreTable.Split(value)) { Draft.Add(new Row { Prefab = item.Prefab, Max = (item.Number ?? "") }); } _source = value; _loadedText = DraftText(); } private static string Problem(int index) { Row row = Draft[index]; string text = row.Prefab.Trim(); float max; string text2 = CoreTable.Check(text, row.Max.Trim(), out max); if (text2 != null) { return text2; } if (text == "Thunderstone") { return "has its own [Thunderstone] section"; } for (int i = 0; i < index; i++) { if (Draft[i].Prefab.Trim() == text) { return "listed twice"; } } return null; } private static void Note(string problem, string prefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Color contentColor = GUI.contentColor; string text = ""; if (problem != null) { GUI.contentColor = ProblemColor; text = problem; } else if (Items.CanLookUp && !Items.IsKnown(prefab)) { GUI.contentColor = UnknownColor; text = "not found in the game"; } else if (Items.CanLookUp) { text = Items.DisplayName(prefab); } GUILayout.Label(text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.contentColor = contentColor; } private static IEnumerable<CoreEntry> Entries() { foreach (Row item in Draft) { CoreTable.Check(item.Prefab.Trim(), item.Max.Trim(), out var max); yield return new CoreEntry(item.Prefab.Trim(), max); } } private static string DraftText() { List<string> list = new List<string>(); foreach (Row item in Draft) { list.Add(item.Prefab.Trim() + ":" + item.Max.Trim()); } return string.Join(", ", list); } } internal static class Lang { internal static string NotConfigured => Status(T("Not configured - no shield", "Nicht eingetragen - kein Schild")); internal static string InstallHint => "[<color=yellow><b>1–8</b></color>] " + T("Use a core to install", "Kern einsetzen"); internal static string NoAccess => T("No access to this ward", "Kein Zugriff"); internal static string Core(string core) { return T("Core: ", "Kern: ") + core; } internal static string Charge(float energy, float max) { return T("Charge: ", "Ladung: ") + Whole(energy) + " / " + Whole(max) + " - " + Percent(energy, max) + "%"; } internal static string Ready(float holds) { return Status(T("Ready", "Bereit") + Protection(holds)); } internal static string Charging(double seconds, float holds) { string text = Whole((float)Math.Ceiling(seconds)) + "s"; return Status(T("Charging - " + text + " until ready", "Lädt - bereit in " + text) + Protection(holds)); } internal static string NeedsFuel(string fuel, float holds) { return Status(T("Charging - needs " + fuel, "Lädt - braucht " + fuel) + Protection(holds)); } internal static string NotCharging(float holds) { return Status(T("Not charging", "Lädt nicht") + Protection(holds)); } internal static string Fuel(string fuel, int items, int capacity, double seconds) { return fuel + ": " + items + " / " + capacity + ((items > 0) ? (" - " + Duration(seconds)) : ""); } internal static string FeedHint(string fuel) { string text = ((ZInput.IsNonClassicFunctionality() && ZInput.IsGamepadActive()) ? "$KEY_AltKeys + $KEY_Use" : "$KEY_AltPlace + $KEY_Use"); return Localization.instance.Localize("[<color=yellow><b>" + text + "</b></color>] ") + T("Add " + fuel, fuel + " hinzufügen"); } internal static string Adding(string fuelToken) { return Localization.instance.Localize("$msg_fireadding", new string[1] { fuelToken }); } internal static string CantAddMore(string fuelToken) { return Localization.instance.Localize("$msg_cantaddmore", new string[1] { fuelToken }); } internal static string OutOf(string fuelToken) { return "$msg_outof " + fuelToken; } internal static string WardFuel(double seconds, float percentPerSecond) { return T(WardName() + " Fuel", WardName() + "-Brennstoff") + ": <color=orange>" + Duration(seconds) + "</color> " + T("at", "mit") + " +" + percentPerSecond.ToString("0.##", CultureInfo.InvariantCulture) + " %/s"; } internal static string WardPower(float max) { return T(WardName() + " Power", WardName() + "-Energie") + ": <color=orange>" + Whole(max) + "</color>"; } private static string WardName() { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab("guard_stone") : null); Piece val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null); if (!((Object)(object)val2 != (Object)null) || Localization.instance == null) { return "Ward"; } return Localization.instance.Localize(val2.m_name); } internal static string AlreadyInstalled(string core) { return core + T(" is already installed", " ist bereits eingesetzt"); } internal static string Installed(string core) { return core + T(" installed — charging", " eingesetzt — lädt auf"); } private static string T(string en, string de) { if (Localization.instance == null || !(Localization.instance.GetSelectedLanguage() == "German")) { return en; } return de; } private static string Status(string text) { return "Status: " + text; } private static string Protection(float holds) { return " - " + T("Protection ", "Schutz ") + Whole((float)Math.Floor((double)holds * 100.0)) + "%"; } private static string Whole(float value) { return value.ToString("0", CultureInfo.InvariantCulture); } private static string Duration(double seconds) { long num = (long)Math.Ceiling(Math.Max(0.0, seconds)); if (num < 60) { return num + "s"; } if (num < 3600) { return num / 60 + "m " + num % 60 + "s"; } return num / 3600 + "h " + num % 3600 / 60 + "m"; } private static string Percent(float energy, float max) { if (!(max > 0f)) { return "0"; } return Whole((float)Math.Floor((double)energy * 100.0 / (double)max)); } } [BepInPlugin("valheim.wardcores", "Ward Cores", "1.0.0")] [BepInDependency("_shudnal.ConditionalConfigSync", "1.0.10")] [BepInProcess("valheim.exe")] [BepInProcess("valheim_server.exe")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "valheim.wardcores"; public const string Name = "Ward Cores"; public const string Version = "1.0.0"; private const string SyncGuid = "_shudnal.ConditionalConfigSync"; private const string SyncMinimumVersion = "1.0.10"; internal static ManualLogSource Log; internal static string Stamp() { return "[frame " + Time.frameCount + ", " + Time.time.ToString("0.000", CultureInfo.InvariantCulture) + " s] "; } private void Awake() { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Settings.Bind(((BaseUnityPlugin)this).Config); List<string> list = WardAccess.Resolve(); if (list.Count > 0) { foreach (string item in list) { Log.LogError((object)("Not found in this Valheim version: " + item)); } Log.LogError((object)"Ward Cores stays off, nothing is patched."); return; } Harmony val = new Harmony("valheim.wardcores"); try { WardPatches.Apply(val); DamagePatch.Apply(val); TooltipPatch.Apply(val); NumbersPatch.Apply(val); } catch (Exception ex) { val.UnpatchSelf(); Log.LogError((object)("Ward Cores stays off, patching failed: " + ex)); return; } Log.LogInfo((object)"Ward Cores 1.0.0 ready"); } private void LateUpdate() { DamagePatch.FlushSplashes(); } } internal static class Settings { internal static ConfigEntry<string> CoreList; internal static ConfigEntry<string> BuiltIn; internal static ConfigEntry<float> RechargePercentPerSecond; internal static ConfigEntry<float> RechargeCooldownSeconds; internal static ConfigEntry<float> CooldownRechargeFactor; internal static ConfigEntry<float> ReactivateAtPercent; internal static ConfigEntry<float> BleedBelowPercent; internal static ConfigEntry<float> BleedMaxPercent; internal static ConfigEntry<float> DamageToEnergyFactor; internal static ConfigEntry<float> ToolDamageFactor; internal static ConfigEntry<string> FuelItem; internal static ConfigEntry<float> FuelRechargePercentPerSecond; internal static ConfigEntry<float> FuelSecondsPerItem; internal static ConfigEntry<int> FuelCapacity; internal static ConfigEntry<bool> ThunderstoneEnabled; internal static ConfigEntry<float> ThunderstoneMaxEnergy; internal static ConfigEntry<float> RetaliationDamageFactor; internal static ConfigEntry<bool> StaggerAttackers; internal static ConfigEntry<bool> LogAbsorb; private static ConfigSync _sync; internal static CoreTable Cores { get; private set; } = CoreTable.Parse(""); internal static string Fuel => FuelItem.Value.Trim(); internal static bool FuelEnabled => Fuel.Length > 0; internal static RechargeRule Recharge => new RechargeRule(RechargePercentPerSecond.Value, FuelEnabled ? FuelRechargePercentPerSecond.Value : 0f, CooldownRechargeFactor.Value); internal static float FuelCapacitySeconds => (float)FuelCapacity.Value * FuelSecondsPerItem.Value; internal static void Bind(ConfigFile config) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Expected O, but got Unknown //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Expected O, but got Unknown //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Expected O, but got Unknown //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Expected O, but got Unknown //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Expected O, but got Unknown //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Expected O, but got Unknown //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Expected O, but got Unknown //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Expected O, but got Unknown //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Expected O, but got Unknown //IL_03b9: Unknown result type (might be due to invalid IL or missing references) //IL_03be: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) //IL_03d4: Unknown result type (might be due to invalid IL or missing references) //IL_03df: Unknown result type (might be due to invalid IL or missing references) //IL_03eb: Expected O, but got Unknown CoreList = config.Bind<string>("Cores", "List", "SurtlingCore:600, Upgrader0Armor:800, Upgrader1Armor:1000, Upgrader2Armor:1200, FrostCore:1400, DragonTear:1600, Upgrader3Armor:1800, YagluthDrop:2000, Upgrader4Armor:2200, Wisp:2400, BlackCore:2600, Upgrader5Armor:2800, MoltenCore:3000, FaderEmber:3200, Upgrader6Armor:3400, FrozenFuel:3600, OrbFrostFire:3800, OrbThunderBlood:3800, Upgrader7Armor:4000", new ConfigDescription("Prefab:MaxEnergy, comma separated, e.g. DragonTear:1600. Names are case-sensitive, decimals use a dot; broken entries are skipped with a warning in the log. A ward whose core is removed from the list has no shield until a listed core is installed. Items from other mods work by their prefab name.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { CustomDrawer = CoreListDrawer.Draw } })); BuiltIn = config.Bind<string>("Cores", "BuiltIn", "SurtlingCore", "Core every ward carries until another is installed. It must also be in List, or those wards have no shield. Never returned by a swap: it comes back through the ward's recipe."); RechargePercentPerSecond = config.Bind<float>("Energy", "RechargePercentPerSecond", 1f, new ConfigDescription("Percent of max energy a ward regains per second of world time, also while nobody is near and while players sleep. A change applies at once, also to recharge already under way. 0 = wards recharge only while fuel burns.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); RechargeCooldownSeconds = config.Bind<float>("Energy", "RechargeCooldownSeconds", 5f, new ConfigDescription("After absorbing a hit, a ward recharges only at CooldownRechargeFactor of its rate for this long; every further hit restarts it. 0 = no cooldown.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>())); CooldownRechargeFactor = config.Bind<float>("Energy", "CooldownRechargeFactor", 0.25f, new ConfigDescription("Share of the normal recharge rate a ward keeps during the cooldown after a hit: 0 = it pauses, 0.5 = half speed, 1 = no slowdown.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); ReactivateAtPercent = config.Bind<float>("Energy", "ReactivateAtPercent", 30f, new ConfigDescription("A drained ward switches back on at this percent of max energy. The same point decides when the hover says Ready, and when a ward switched on with E below it comes on.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>())); BleedBelowPercent = config.Bind<float>("Energy", "BleedBelowPercent", 30f, new ConfigDescription("Below this charge a ward lets part of its share of a hit through: none at this value, more the emptier it is. Wards at or above it take over what bleeds through. What no ward takes over reaches the piece. 0 turns bleeding off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); BleedMaxPercent = config.Bind<float>("Energy", "BleedMaxPercent", 50f, new ConfigDescription("The most of its share a weakened ward lets through, reached when it is empty (the bleed grows linearly up to it), so it still pays something for every hit and runs dry instead of fading forever. 0 = no bleed, 100 = no cap (a ward then never quite runs dry).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); DamageToEnergyFactor = config.Bind<float>("Energy", "DamageToEnergyFactor", 1f, new ConfigDescription("Energy a ward pays per point of damage it absorbs, counted on the reshaped hit (resistances apply, weaknesses don't, chop and pickaxe scaled by ToolDamageFactor). 1 = 1:1, 0.5 = half, 2 = double.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 100f), Array.Empty<object>())); ToolDamageFactor = config.Bind<float>("Energy", "ToolDamageFactor", 1f, new ConfigDescription("Share of chop and pickaxe damage that counts in a hit on a protected piece: its wards pay for it, and only what bleeds through reaches the piece. 0 = none, 1 = full. Weaknesses never apply while protected.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); FuelItem = config.Bind<string>("Fuel", "FuelItem", "Sap", "Item fed to wards with Shift+E to speed up their recharge (prefab ID). Empty turns feeding off."); FuelRechargePercentPerSecond = config.Bind<float>("Fuel", "FuelRechargePercentPerSecond", 1f, new ConfigDescription("Percent of max energy per second a ward recharges on top of RechargePercentPerSecond while fuel burns. The cooldown after a hit slows it like the rest of the rate.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); FuelSecondsPerItem = config.Bind<float>("Fuel", "FuelSecondsPerItem", 60f, new ConfigDescription("Seconds of recharging one fuel item lasts; it burns only while the ward recharges.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3600f), Array.Empty<object>())); FuelCapacity = config.Bind<int>("Fuel", "FuelCapacity", 50, new ConfigDescription("Fuel items a ward can hold.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>())); ThunderstoneEnabled = config.Bind<bool>("Thunderstone", "Enabled", true, "Whether the Thunder Stone works as a ward core. Off: it cannot be installed, and wards that run it have no shield until another core is installed."); ThunderstoneMaxEnergy = config.Bind<float>("Thunderstone", "MaxEnergy", 1000f, new ConfigDescription("Max energy of a ward running the Thunder Stone. It stays out of the core list.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100000f), Array.Empty<object>())); RetaliationDamageFactor = config.Bind<float>("Thunderstone", "RetaliationDamageFactor", 0.5f, new ConfigDescription("Share of the damage a Thunder Stone ward absorbed from an attack that its lightning strike returns to the attacker: 0 = stagger only, 1 = all of it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); StaggerAttackers = config.Bind<bool>("Thunderstone", "StaggerAttackers", true, "Whether the strike staggers the attacker. Creatures the game never staggers, like bosses, stay unstaggered."); LogAbsorb = config.Bind<bool>("Debug", "LogAbsorb", false, "Log every absorbed hit (piece, vanilla and reshaped damage, wards, shares, what reaches the piece), every splash charge and every feed, with frame and time."); _sync = new ConfigSync("valheim.wardcores") { DisplayName = "Ward Cores", CurrentVersion = "1.0.0", MinimumRequiredVersion = "1.0.0", ModRequired = true }; Server<string>(CoreList); Server<string>(BuiltIn); Server<float>(RechargePercentPerSecond); Server<float>(RechargeCooldownSeconds); Server<float>(CooldownRechargeFactor); Server<float>(ReactivateAtPercent); Server<float>(BleedBelowPercent); Server<float>(BleedMaxPercent); Server<float>(DamageToEnergyFactor); Server<float>(ToolDamageFactor); Server<string>(FuelItem); Server<float>(FuelRechargePercentPerSecond); Server<float>(FuelSecondsPerItem); Server<int>(FuelCapacity); Server<bool>(ThunderstoneEnabled); Server<float>(ThunderstoneMaxEnergy); Server<float>(RetaliationDamageFactor); Server<bool>(StaggerAttackers); CoreList.SettingChanged += delegate { Rebuild(); }; Rebuild(); } private static void Server<T>(ConfigEntry<T> entry) { ((ConditionalConfigSync)_sync).AddConfigEntry<T>(entry, (ConfigSyncMode)0); } internal static bool TryGetMax(string prefab, out float max) { return TryGetCoreMax(string.IsNullOrEmpty(prefab) ? BuiltIn.Value : prefab, out max); } internal static bool TryGetCoreMax(string prefab, out float max) { if (prefab == "Thunderstone") { max = (ThunderstoneEnabled.Value ? ThunderstoneMaxEnergy.Value : 0f); return ThunderstoneEnabled.Value; } return Cores.TryGetMax(prefab, out max); } private static void Rebuild() { CoreTable coreTable = CoreTable.Parse(CoreList.Value); foreach (string problem in coreTable.Problems) { Plugin.Log.LogWarning((object)("[Cores] List: " + problem + " — skipped")); } List<CoreEntry> list = new List<CoreEntry>(); foreach (CoreEntry entry in coreTable.Entries) { if (entry.Prefab == "Thunderstone") { Plugin.Log.LogWarning((object)"[Cores] List: Thunderstone has its own [Thunderstone] section — skipped"); } else { list.Add(entry); } } Cores = ((list.Count == coreTable.Entries.Count) ? coreTable : CoreTable.Parse(CoreTable.Join(list))); Plugin.Log.LogInfo((object)(Cores.Entries.Count + " cores configured")); } } } namespace WardCores.Patches { internal static class DamagePatch { private static readonly List<PrivateArea> Paying = new List<PrivateArea>(); private static readonly List<float> Available = new List<float>(); private static readonly List<float> Charge = new List<float>(); private static readonly SplashBatch<ZDOID, PrivateArea> Batch = new SplashBatch<ZDOID, PrivateArea>(); private static readonly List<SplashBatch<ZDOID, PrivateArea>.Splash> Closed = new List<SplashBatch<ZDOID, PrivateArea>.Splash>(); private const float SlightlyWeakFactor = 1.25f; private const float WeakFactor = 1.5f; private const float VeryWeakFactor = 2f; internal static void Apply(Harmony harmony) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown harmony.Patch((MethodBase)WardAccess.ApplyDamage, new HarmonyMethod(typeof(DamagePatch), "ApplyDamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static bool ApplyDamagePrefix(WearNTear __instance, ref float damage, HitData hitData, ref bool __result) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) if (hitData == null || damage <= 0f || !Protects(__instance, hitData)) { return true; } float vanilla = damage; damage = Reshape(hitData, __instance.m_damages, damage); if (damage <= 0.001f) { NumbersPatch.ShowPiece(hitData, 0f); __result = true; return false; } float value = Settings.DamageToEnergyFactor.Value; float[] array = new float[Paying.Count]; float num = WardMath.SplitWithBleed(damage * value, Available, Charge, Settings.BleedBelowPercent.Value, Settings.BleedMaxPercent.Value, array) / value; bool flag = !((ZDOID)(ref hitData.m_attacker)).IsNone(); if (flag) { Batch.Add(hitData.m_attacker, Time.frameCount, Paying, array); } for (int i = 0; i < Paying.Count; i++) { if (array[i] > 0f) { if (!flag) { Drain(Paying[i], array[i]); } WardAccess.Flash(Paying[i]); } } NumbersPatch.ShowPiece(hitData, num); if (Settings.LogAbsorb.Value) { Plugin.Log.LogInfo((object)Describe(__instance, vanilla, damage, num, array, flag)); } if (num <= 0.001f) { __result = true; return false; } damage = num; return true; } internal static bool Protects(WearNTear piece, HitData hit) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) Paying.Clear(); Available.Clear(); Charge.Clear(); Vector3 position = ((Component)piece).transform.position; Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); long num = (((Object)(object)val != (Object)null) ? val.GetPlayerID() : 0); long now = WardStore.Now(); foreach (PrivateArea allArea in WardAccess.AllAreas) { if ((Object)(object)allArea == (Object)null || !WardStore.IsPlayerWard(allArea) || !WardAccess.IsEnabled(allArea) || !WardAccess.IsInside(allArea, position)) { continue; } ZDO val2 = WardStore.Zdo(allArea); if (val2 == null) { continue; } if (num != 0L && WardStore.HasAccess(allArea, num)) { Paying.Clear(); return false; } if (!WardStore.IsDepleted(val2) && WardStore.TryGetMax(val2, out var max)) { float num2 = WardStore.Energy(val2, max, now) - WardRpc.PendingFor(val2.m_uid); if (num2 > 0.001f) { Paying.Add(allArea); Available.Add(num2); Charge.Add(WardStore.ChargePercent(num2, max)); } } } return Paying.Count > 0; } private static float Reshape(HitData hit, DamageModifiers modifiers, float damage) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) float value = Settings.ToolDamageFactor.Value; damage += Adjust(ref hit.m_damage.m_blunt, modifiers.m_blunt, 1f); damage += Adjust(ref hit.m_damage.m_slash, modifiers.m_slash, 1f); damage += Adjust(ref hit.m_damage.m_pierce, modifiers.m_pierce, 1f); damage += Adjust(ref hit.m_damage.m_chop, modifiers.m_chop, value); damage += Adjust(ref hit.m_damage.m_pickaxe, modifiers.m_pickaxe, value); damage += Adjust(ref hit.m_damage.m_fire, modifiers.m_fire, 1f); damage += Adjust(ref hit.m_damage.m_frost, modifiers.m_frost, 1f); damage += Adjust(ref hit.m_damage.m_lightning, modifiers.m_lightning, 1f); damage += Adjust(ref hit.m_damage.m_poison, modifiers.m_poison, 1f); damage += Adjust(ref hit.m_damage.m_spirit, modifiers.m_spirit, 1f); damage += Adjust(ref hit.m_damage.m_nonPlayer, modifiers.m_nonPlayer, 1f); return Mathf.Max(0f, damage); } private static float Adjust(ref float dealt, DamageModifier modifier, float scale) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) float num = WardMath.Unweakened(dealt, WeaknessFactor(modifier)) * scale; float result = num - dealt; dealt = num; return result; } private static float WeaknessFactor(DamageModifier modifier) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if ((int)modifier != 2) { if ((int)modifier != 6) { if ((int)modifier == 8) { return 1.25f; } return 1f; } return 2f; } return 1.5f; } internal static void FlushSplashes() { //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (Batch.Open == 0) { return; } Batch.TakeClosed(Time.frameCount, Closed); foreach (SplashBatch<ZDOID, PrivateArea>.Splash item in Closed) { foreach (KeyValuePair<PrivateArea, float> item2 in item.Charges()) { if ((Object)(object)item2.Key != (Object)null && item2.Value > 0f) { Drain(item2.Key, item2.Value); if (WardStore.StrikesBack(item2.Key)) { WardStrike.Strike(item.Attacker, Thunderstone.StrikeDamage(item2.Value, Settings.DamageToEnergyFactor.Value, Settings.RetaliationDamageFactor.Value)); } } } if (Settings.LogAbsorb.Value) { Plugin.Log.LogInfo((object)(Plugin.Stamp() + "Splash of " + item.Hits + " hit(s): charged " + Number(item.Sum / (float)item.Hits) + " (" + Number(item.Sum) + " ÷ " + item.Hits + ")")); } } } private static void Drain(PrivateArea ward, float amount) { WardRpc.SendDrain(ward, amount); NumbersPatch.ShowDrain(ward, amount); } private static string Describe(WearNTear piece, float vanilla, float damage, float left, float[] shares, bool splash) { StringBuilder stringBuilder = new StringBuilder(Plugin.Stamp()); stringBuilder.Append("Absorbed ").Append(Number(damage - left)).Append(" of ") .Append(Number(damage)); if (Mathf.Abs(vanilla - damage) > 0.001f) { stringBuilder.Append(" (vanilla ").Append(Number(vanilla)).Append(')'); } stringBuilder.Append(" on ").Append(Utils.GetPrefabName(((Component)piece).gameObject)).Append(", wards:"); for (int i = 0; i < Paying.Count; i++) { stringBuilder.Append(' ').Append(Utils.GetPrefabName(((Component)Paying[i]).gameObject)).Append('@') .Append(Number(Available[i])) .Append(" (") .Append(Number(Charge[i])) .Append("%) ") .Append(splash ? "share " : "pays ") .Append(Number(shares[i])); } return stringBuilder.Append(", reaches piece: ").Append(Number(left)).ToString(); } private static string Number(float value) { return value.ToString("0.#", CultureInfo.InvariantCulture); } } internal static class NumbersPatch { private const string DrainColor = "#5AB4FF"; private const string DrainTag = "<color=#5AB4FF>"; private static HitData _protectedHit; internal static void Apply(Harmony harmony) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0038: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown harmony.Patch((MethodBase)WardAccess.RpcDamage, new HarmonyMethod(typeof(NumbersPatch), "RpcDamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(NumbersPatch), "RpcDamageFinalizer", (Type[])null), (HarmonyMethod)null); harmony.Patch((MethodBase)WardAccess.DamageNumber, new HarmonyMethod(typeof(NumbersPatch), "DamageNumberPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)WardAccess.AddWorldText, (HarmonyMethod)null, new HarmonyMethod(typeof(NumbersPatch), "AddWorldTextPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } internal static void ShowPiece(HitData hit, float reaches) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)DamageText.instance == (Object)null)) { bool flag = reaches <= 0.001f; DamageText.instance.ShowText((TextType)(flag ? 3 : 0), hit.m_point, flag ? 0f : reaches, false); } } internal static void ShowDrain(PrivateArea ward, float amount) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)DamageText.instance == (Object)null) && !((Object)(object)ward == (Object)null)) { Vector3 val = ((Component)ward).transform.position + Vector3.up * Mathf.Max(1.5f, ward.m_hoverOffset); string text = amount.ToString("0.#", CultureInfo.InvariantCulture); DamageText.instance.ShowText((TextType)7, val, "<color=#5AB4FF>" + text + "</color>", false); } } private static void AddWorldTextPostfix(DamageText __instance) { Transform transform = ((Component)__instance).transform; if (transform.childCount != 0) { TMP_Text component = ((Component)transform.GetChild(transform.childCount - 1)).GetComponent<TMP_Text>(); string text = (((Object)(object)component != (Object)null) ? component.text : null); if (text != null && text.StartsWith("<color=#5AB4FF>", StringComparison.Ordinal)) { component.richText = true; } } } private static void RpcDamagePrefix(WearNTear __instance, HitData hit) { ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); _protectedHit = ((hit != null && (Object)(object)component != (Object)null && component.IsValid() && component.IsOwner() && DamagePatch.Protects(__instance, hit)) ? hit : null); } private static void RpcDamageFinalizer() { _protectedHit = null; } private static bool DamageNumberPrefix(Vector3 pos, float dmg) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (_protectedHit != null && !(dmg <= 0f)) { return pos != _protectedHit.m_point; } return true; } } internal static class TooltipPatch { internal static void Apply(Harmony harmony) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown harmony.Patch((MethodBase)WardAccess.ItemTooltip, (HarmonyMethod)null, new HarmonyMethod(typeof(TooltipPatch), "TooltipPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static void TooltipPostfix(ItemData item, bool appending, ref string __result) { if (!appending && item != null) { string text = Items.PrefabOf(item); if (text != null && Settings.Cores.TryGetMax(text, out var max)) { __result = __result + "\n\n" + Lang.WardPower(max); } if (text != null && Settings.FuelEnabled && text == Settings.Fuel) { __result = __result + "\n\n" + Lang.WardFuel(Settings.FuelSecondsPerItem.Value, Settings.FuelRechargePercentPerSecond.Value); } } } } internal static class WardPatches { private const float FeedRepeatSeconds = 0.2f; private static float _lastFeed; internal static void Apply(Harmony harmony) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Expected O, but got Unknown harmony.Patch((MethodBase)WardAccess.AreaAwake, (HarmonyMethod)null, new HarmonyMethod(typeof(WardPatches), "AwakePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)WardAccess.AreaUpdateStatus, (HarmonyMethod)null, new HarmonyMethod(typeof(WardPatches), "UpdateStatusPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)WardAccess.AreaGetHoverText, (HarmonyMethod)null, new HarmonyMethod(typeof(WardPatches), "GetHoverTextPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)WardAccess.AreaUseItem, new HarmonyMethod(typeof(WardPatches), "UseItemPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)WardAccess.AreaInteract, new HarmonyMethod(typeof(WardPatches), "InteractPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)WardAccess.AreaToggleEnabled, new HarmonyMethod(typeof(WardPatches), "ToggleEnabledPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)WardAccess.AreaTogglePermitted, new HarmonyMethod(typeof(WardPatches), "TogglePermittedPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)WardAccess.ObjectDbAwake, (HarmonyMethod)null, new HarmonyMethod(typeof(WardPatches), "ObjectDbAwakePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static void AwakePostfix(PrivateArea __instance) { ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if (!WardStore.IsPlayerWard(__instance) || (Object)(object)component == (Object)null || !component.IsValid()) { return; } WardRpc.Register(__instance, component); WearNTear component2 = ((Component)__instance).GetComponent<WearNTear>(); if ((Object)(object)component2 != (Object)null) { component2.m_onDestroyed = (Action)Delegate.Combine(component2.m_onDestroyed, (Action)delegate { DropContents(__instance); }); } WardStore.Settle(__instance); } private static void UpdateStatusPostfix(PrivateArea __instance) { if (WardStore.IsPlayerWard(__instance)) { WardStore.Settle(__instance); } } private static void GetHoverTextPostfix(PrivateArea __instance, ref string __result) { if (string.IsNullOrEmpty(__result) || !WardStore.IsPlayerWard(__instance)) { return; } ZDO val = WardStore.Zdo(__instance); if (val == null) { return; } long now = WardStore.Now(); string text = (Settings.FuelEnabled ? Items.DisplayName(Settings.Fuel) : null); __result = __result + "\n" + Lang.Core(Items.DisplayName(WardStore.CorePrefab(val))); float max; bool flag = WardStore.TryGetMax(val, out max); float energy = (flag ? WardStore.Energy(val, max, now) : 0f); if (flag) { __result = __result + "\n" + Lang.Charge(energy, max); } if (text != null) { float num = WardStore.FuelSeconds(val, now); __result = __result + "\n" + Lang.Fuel(text, WardStore.FuelItems(num, countBurning: true), Settings.FuelCapacity.Value, num); } __result = __result + "\n" + (flag ? Status(__instance, val, energy, max, now, text) : Lang.NotConfigured); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && WardStore.HasAccess(__instance, localPlayer.GetPlayerID())) { __result = __result + "\n" + Lang.InstallHint; if (text != null) { __result = __result + "\n" + Lang.FeedHint(text); } } } private static string Status(PrivateArea area, ZDO zdo, float energy, float max, long now, string fuel) { float holds = WardStore.Holds(energy, max); if (WardStore.IsReady(area, energy, max)) { return Lang.Ready(holds); } double num = WardStore.SecondsUntilReady(zdo, max, now); if (!double.IsInfinity(num)) { return Lang.Charging(num, holds); } if (fuel == null) { return Lang.NotCharging(holds); } return Lang.NeedsFuel(fuel, holds); } private static bool UseItemPrefix(PrivateArea __instance, Humanoid user, ItemData item, ref bool __result) { string text = Items.PrefabOf(item); ZDO val = WardStore.Zdo(__instance); if (!WardStore.IsPlayerWard(__instance) || val == null || text == null || !Settings.TryGetCoreMax(text, out var _)) { return true; } __result = true; Player val2 = (Player)(object)((user is Player) ? user : null); if ((Object)(object)val2 == (Object)null) { return false; } string core = Items.DisplayName(text); if (!WardStore.HasAccess(__instance, val2.GetPlayerID())) { ((Character)user).Message((MessageType)2, Lang.NoAccess, 0, (Sprite)null, false); return false; } if (WardStore.CorePrefab(val) == text) { ((Character)user).Message((MessageType)2, Lang.AlreadyInstalled(core), 0, (Sprite)null, false); return false; } string text2 = WardStore.InstalledItem(val); if (!user.GetInventory().RemoveOneItem(item)) { return false; } if (text2.Length > 0) { Items.Give(user, text2); } WardRpc.SendInstall(__instance, text); ((Character)user).Message((MessageType)2, Lang.Installed(core), 0, (Sprite)null, false); return false; } private static bool InteractPrefix(PrivateArea __instance, Humanoid human, bool hold, bool alt, ref bool __result) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) string text = ((alt && Settings.FuelEnabled && WardStore.IsPlayerWard(__instance)) ? Items.SharedName(Settings.Fuel) : null); if (text == null) { return true; } __result = false; Player val = (Player)(object)((human is Player) ? human : null); ZDO val2 = WardStore.Zdo(__instance); if ((Object)(object)val == (Object)null || val2 == null || (hold && Time.time - _lastFeed < 0.2f)) { return false; } if (!WardStore.HasAccess(__instance, val.GetPlayerID())) { if (!hold) { ((Character)human).Message((MessageType)2, Lang.NoAccess, 0, (Sprite)null, false); } return false; } Inventory inventory = human.GetInventory(); if (!inventory.HaveItem(text, true)) { ((Character)human).Message((MessageType)2, Lang.OutOf(text), 0, (Sprite)null, false); return false; } if (WardStore.FuelItems(WardStore.FuelSeconds(val2, WardStore.Now()) + WardRpc.PendingFuelFor(val2.m_uid), countBurning: true) >= Settings.FuelCapacity.Value) { ((Character)human).Message((MessageType)2, Lang.CantAddMore(text), 0, (Sprite)null, false); return false; } inventory.RemoveItem(text, 1, -1, true); WardRpc.SendFeed(__instance, 1); ((Character)human).Message((MessageType)2, Lang.Adding(text), 0, (Sprite)null, false); _lastFeed = Time.time; __result = true; return false; } private static bool ToggleEnabledPrefix(PrivateArea __instance, long playerID) { Piece component = ((Component)__instance).GetComponent<Piece>(); if (!WardStore.IsPlayerWard(__instance) || (Object)(object)component == (Object)null || component.GetCreator() != playerID) { return true; } return WardStore.ToggleRequested(__instance); } private static bool TogglePermittedPrefix(PrivateArea __instance) { ZDO val = WardStore.Zdo(__instance); if (WardStore.IsPlayerWard(__instance) && val != null) { return !WardStore.IsDepleted(val); } return true; } private static void DropContents(PrivateArea area) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) ZNetView component = ((Component)area).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner()) { ZDO zDO = component.GetZDO(); Vector3 position = ((Component)area).transform.position + Vector3.up; string text = WardStore.InstalledItem(zDO); if (text.Length > 0) { Items.DropAt(text, position); } int num = WardStore.FuelItems(WardStore.FuelSeconds(zDO, WardStore.Now()), countBurning: false); if (Settings.FuelEnabled && num > 0) { Items.DropAt(Settings.Fuel, position, num); } } } private static void ObjectDbAwakePostfix(ObjectDB __instance) { if (__instance.m_items == null || __instance.m_items.Count == 0) { return; } foreach (CoreEntry entry in Settings.Cores.Entries) { if ((Object)(object)__instance.GetItemPrefab(entry.Prefab) == (Object)null) { Plugin.Log.LogWarning((object)("[Cores] List: the game has no item '" + entry.Prefab + "', nobody can install it")); } } if (!Settings.TryGetMax("", out var _)) { Plugin.Log.LogWarning((object)("[Cores] BuiltIn '" + Settings.BuiltIn.Value + "' is not in the list: wards without an installed core have no shield")); } if (Settings.FuelEnabled && (Object)(object)__instance.GetItemPrefab(Settings.Fuel) == (Object)null) { Plugin.Log.LogWarning((object)("[Fuel] FuelItem: the game has no item '" + Settings.Fuel + "', nobody can feed wards")); } } } } namespace WardCores.Net { internal static class WardRpc { private sealed class Ledger { private readonly Dictionary<ZDOID, List<KeyValuePair<float, float>>> _sent = new Dictionary<ZDOID, List<KeyValuePair<float, float>>>(); internal void Add(ZDOID id, float amount) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (!_sent.TryGetValue(id, out var value)) { value = new List<KeyValuePair<float, float>>(); _sent.Add(id, value); } value.Add(new KeyValuePair<float, float>(Time.time, amount)); } internal float Sum(ZDOID id) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (!_sent.TryGetValue(id, out var value)) { return 0f; } float cutoff = Time.time - 1f; value.RemoveAll((KeyValuePair<float, float> entry) => entry.Key < cutoff); if (value.Count == 0) { _sent.Remove(id); return 0f; } float num = 0f; foreach (KeyValuePair<float, float> item in value) { num += item.Value; } return num; } } private const string DrainRpc = "wardcore_Drain"; private const string InstallRpc = "wardcore_Install"; private const string FeedRpc = "wardcore_Feed"; private const float PendingSeconds = 1f; private static readonly Ledger Drains = new Ledger(); private static readonly Ledger Feeds = new Ledger(); internal static void Register(PrivateArea area, ZNetView nview) { nview.Register<float>("wardcore_Drain", (Action<long, float>)delegate(long sender, float amount) { WardStore.Drain(area, amount); }); nview.Register<string>("wardcore_Install", (Action<long, string>)delegate(long sender, string prefab) { WardStore.Install(area, prefab); }); nview.Register<int>("wardcore_Feed", (Action<long, int>)delegate(long sender, int count) { WardStore.Feed(area, count); }); } internal static void SendDrain(PrivateArea area, float amount) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) ZNetView component = ((Component)area).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { if (component.IsOwner()) { WardStore.Drain(area, amount); return; } Drains.Add(component.GetZDO().m_uid, amount); component.InvokeRPC("wardcore_Drain", new object[1] { amount }); } } internal static void SendInstall(PrivateArea area, string prefab) { ((Component)area).GetComponent<ZNetView>().InvokeRPC("wardcore_Install", new object[1] { prefab }); } internal static void SendFeed(PrivateArea area, int count) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) ZNetView component = ((Component)area).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { if (component.IsOwner()) { WardStore.Feed(area, count); return; } Feeds.Add(component.GetZDO().m_uid, (float)count * Settings.FuelSecondsPerItem.Value); component.InvokeRPC("wardcore_Feed", new object[1] { count }); } } internal static float PendingFor(ZDOID id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Drains.Sum(id); } internal static float PendingFuelFor(ZDOID id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Feeds.Sum(id); } } internal static class WardStrike { private const string StrikeEffect = "fx_chainlightning_hit"; private const float GuaranteedStagger = 100f; private static bool _effectMissingLogged; internal static void Strike(ZDOID attacker, float damage) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) if (((ZDOID)(ref attacker)).IsNone() || (Object)(object)ZNetScene.instance == (Object)null) { return; } GameObject val = ZNetScene.instance.FindInstance(attacker); Character val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Character>() : null); if ((Object)(object)val2 == (Object)null || val2.IsDead()) { return; } bool flag = Settings.StaggerAttackers.Value && Staggerable(val2); if (!(damage <= 0f) || flag) { Vector3 centerPoint = val2.GetCenterPoint(); HitData val3 = new HitData { m_point = centerPoint, m_dir = Vector3.down, m_hitType = (HitType)0, m_staggerMultiplier = (flag ? 100f : 1f) }; val3.m_damage.m_lightning = damage; val2.Damage(val3); PlayEffect(centerPoint); if (Settings.LogAbsorb.Value) { Plugin.Log.LogInfo((object)(Plugin.Stamp() + "Strike " + Localization.instance.Localize(val2.m_name) + " for " + damage.ToString("0.#", CultureInfo.InvariantCulture) + " lightning")); } } } private static bool Staggerable(Character target) { if (!target.IsPlayer()) { return target.m_staggerDamageFactor > 0f; } return true; } private static void PlayEffect(Vector3 position) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) GameObject prefab = ZNetScene.instance.GetPrefab("fx_chainlightning_hit"); if ((Object)(object)prefab != (Object)null) { Object.Instantiate<GameObject>(prefab, position, Quaternion.identity); } else if (!_effectMissingLogged) { _effectMissingLogged = true; Plugin.Log.LogWarning((object)"[Thunderstone] The game has no effect 'fx_chainlightning_hit'; strikes land without it"); } } } } namespace WardCores.Core { internal sealed class CoreEntry { internal string Prefab { get; } internal float Max { get; } internal CoreEntry(string prefab, float max) { Prefab = prefab; Max = max; } } internal sealed class CorePart { internal string Text { get; } internal string Prefab { get; } internal string Number { get; } internal CorePart(string text, string prefab, string number) { Text = text; Prefab = prefab; Number = number; } } internal sealed class CoreTable { internal const string DefaultList = "SurtlingCore:600, Upgrader0Armor:800, Upgrader1Armor:1000, Upgrader2Armor:1200, FrostCore:1400, DragonTear:1600, Upgrader3Armor:1800, YagluthDrop:2000, Upgrader4Armor:2200, Wisp:2400, BlackCore:2600, Upgrader5Armor:2800, MoltenCore:3000, FaderEmber:3200, Upgrader6Armor:3400, FrozenFuel:3600, OrbFrostFire:3800, OrbThunderBlood:3800, Upgrader7Armor:4000"; private readonly List<CoreEntry> _entries = new List<CoreEntry>(); private readonly Dictionary<string, CoreEntry> _byPrefab = new Dictionary<string, CoreEntry>(StringComparer.Ordinal); private readonly List<string> _problems = new List<string>(); internal IReadOnlyList<CoreEntry> Entries => _entries; internal IReadOnlyList<string> Problems => _problems; internal static CoreTable Parse(string list) { CoreTable coreTable = new CoreTable(); foreach (CorePart item in Split(list)) { coreTable.Add(item); } return coreTable; } internal static List<CorePart> Split(string list) { List<CorePart> list2 = new List<CorePart>(); if (string.IsNullOrWhiteSpace(list)) { return list2; } string[] array = list.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0) { int num = text.IndexOf(':'); list2.Add((num < 0) ? new CorePart(text, text, null) : new CorePart(text, text.Substring(0, num).Trim(), text.Substring(num + 1).Trim())); } } return list2; } internal static string Check(string prefab, string number, out float max) { max = 0f; if (number == null) { return "expected Prefab:MaxEnergy"; } if (string.IsNullOrEmpty(prefab) || HasWhitespace(prefab)) { return "the prefab name is empty or contains spaces"; } if (!float.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out max) || float.IsNaN(max) || float.IsInfinity(max) || max <= 0f) { max = 0f; return "max energy must be a number above 0"; } return null; } internal static string Join(IEnumerable<CoreEntry> entries) { List<string> list = new List<string>(); foreach (CoreEntry entry in entries) { list.Add(entry.Prefab + ":" + entry.Max.ToString(CultureInfo.InvariantCulture)); } return string.Join(", ", list); } internal bool TryGetMax(string prefab, out float max) { if (prefab != null && _byPrefab.TryGetValue(prefab, out var value)) { max = value.Max; return true; } max = 0f; return false; } private void Add(CorePart part) { float max; string text = Check(part.Prefab, part.Number, out max); if (text == null && _byPrefab.ContainsKey(part.Prefab)) { text = part.Prefab + " is listed twice, the first entry is used"; } if (text != null) { _problems.Add("'" + part.Text + "': " + text); return; } CoreEntry coreEntry = new CoreEntry(part.Prefab, max); _entries.Add(coreEntry); _byPrefab.Add(part.Prefab, coreEntry); } private static bool HasWhitespace(string text) { for (int i = 0; i < text.Length; i++) { if (char.IsWhiteSpace(text[i])) { return true; } } return false; } } internal static class Items { internal static bool CanLookUp => (Object)(object)ObjectDB.instance != (Object)null; internal static string PrefabOf(ItemData item) { if (item == null) { return null; } GameObject val = item.m_dropPrefab; if ((Object)(object)val == (Object)null && (Object)(object)ObjectDB.instance != (Object)null) { val = ObjectDB.instance.GetItemPrefab(item.m_shared); } if (!((Object)(object)val != (Object)null)) { return null; } return Utils.GetPrefabName(val); } internal static bool IsKnown(string prefab) { return (Object)(object)Drop(prefab) != (Object)null; } internal static string DisplayName(string prefab) { ItemDrop val = Drop(prefab); if (!((Object)(object)val != (Object)null)) { return prefab; } return Localization.instance.Localize(val.m_itemData.m_shared.m_name); } internal static void Give(Humanoid user, string prefab) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) ItemDrop val = Drop(prefab); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)("Cannot return core '" + prefab + "': the game has no such item")); return; } Inventory inventory = user.GetInventory(); if (inventory.CanAddItem(((Component)val).gameObject, 1)) { inventory.AddItem(((Component)val).gameObject, 1); } else { DropAt(prefab, ((Component)user).transform.position + Vector3.up); } } internal static void DropAt(string prefab, Vector3 position, int count = 1) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) ItemDrop val = Drop(prefab); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)("Cannot drop '" + prefab + "': the game has no such item")); return; } int num = Mathf.Max(1, val.m_itemData.m_shared.m_maxStackSize); for (int num2 = count; num2 > 0; num2 -= num) { int num3 = Mathf.Min(num, num2); ItemData obj = val.m_itemData.Clone(); obj.m_dropPrefab = ((Component)val).gameObject; obj.m_stack = num3; ItemDrop.DropItem(obj, num3, position, Quaternion.identity); } } internal static string SharedName(string prefab) { ItemDrop val = Drop(prefab); if (!((Object)(object)val != (Object)null)) { return null; } return val.m_itemData.m_shared.m_name; } private static ItemDrop Drop(string prefab) { if ((Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrEmpty(prefab)) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab); if (!((Object)(object)itemPrefab != (Object)null)) { return null; } return itemPrefab.GetComponent<ItemDrop>(); } } internal sealed class SplashBatch<TAttacker, TWard> { internal sealed class Splash { private readonly Dictionary<TWard, float> _shares = new Dictionary<TWard, float>(); internal TAttacker Attacker { get; } internal int Hits { get; private set; } internal float Sum { get; private set; } internal Splash(TAttacker attacker) { Attacker = attacker; } internal IEnumerable<KeyValuePair<TWard, float>> Charges() { foreach (KeyValuePair<TWard, float> share in _shares) { yield return new KeyValuePair<TWard, float>(share.Key, share.Value / (float)Hits); } } internal void Add(IReadOnlyList<TWard> wards, float[] shares) { Hits++; for (int i = 0; i < wards.Count; i++) { _shares.TryGetValue(wards[i], out var value); _shares[wards[i]] = value + shares[i]; Sum += shares[i]; } } } private readonly Dictionary<(TAttacker Attacker, int Frame), Splash> _open = new Dictionary<(TAttacker, int), Splash>(); private readonly List<(TAttacker, int)> _closing = new List<(TAttacker, int)>(); internal int Open => _open.Count; internal void Add(TAttacker attacker, int frame, IReadOnlyList<TWard> wards, float[] shares) { (TAttacker, int) key = (attacker, frame); if (!_open.TryGetValue(key, out var value)) { value = new Splash(attacker); _open.Add(key, value); } value.Add(wards, shares); } internal void TakeClosed(int frame, List<Splash> closed) { closed.Clear(); _closing.Clear(); foreach (KeyValuePair<(TAttacker, int), Splash> item in _open) { if (item.Key.Item2 < frame) { _closing.Add(item.Key); closed.Add(item.Value); } } foreach (var item2 in _closing) { _open.Remove(item2); } } } internal static class Thunderstone { internal const string Prefab = "Thunderstone"; internal const string NotInList = "has its own [Thunderstone] section"; internal const float DefaultMax = 1000f; internal const float DefaultRetaliation = 0.5f; internal static float StrikeDamage(float energyPaid, float damageToEnergyFactor, float retaliationFactor) { if (!(damageToEnergyFactor > 0f)) { return 0f; } return energyPaid / damageToEnergyFactor * retaliationFactor; } } internal static class WardAccess { private const BindingFlags Any = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static FieldInfo _allAreas; private static Func<PrivateArea, bool> _isEnabled; private static Action<PrivateArea, bool> _setEnabled; private static Func<PrivateArea, long, bool> _isPermitted; private static Func<PrivateArea, Vector3, float, bool> _isInside; private static Action<PrivateArea, bool> _flashShield; internal static MethodInfo AreaAwake; internal static MethodInfo AreaUpdateStatus; internal static MethodInfo AreaGetHoverText; internal static MethodInfo AreaUseItem; internal static MethodInfo AreaInteract; internal static MethodInfo AreaToggleEnabled; internal static MethodInfo AreaTogglePermitted; internal static MethodInfo RpcDamage; internal static MethodInfo ApplyDamage; internal static MethodInfo ItemTooltip; internal static MethodInfo DamageNumber; internal static MethodInfo AddWorldText; internal static MethodInfo ObjectDbAwake; internal static List<PrivateArea> AllAreas => (List<PrivateArea>)_allAreas.GetValue(null); internal static List<string> Resolve() { List<string> list = new List<string>(); Type typeFromHandle = typeof(PrivateArea); _allAreas = typeFromHandle.GetField("m_allAreas", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (_allAreas == null || _allAreas.FieldType != typeof(List<PrivateArea>) || !_allAreas.IsStatic) { list.Add("PrivateArea.m_allAreas (static List<PrivateArea>)"); } _isEnabled = Bind<Func<PrivateArea, bool>>(Method(typeFromHandle, "IsEnabled", list), list); _setEnabled = Bind<Action<PrivateArea, bool>>(Method(typeFromHandle, "SetEnabled", list, typeof(bool)), list); _isPermitted = Bind<Func<PrivateArea, long, bool>>(Method(typeFromHandle, "IsPermitted", list, typeof(long)), list); _isInside = Bind<Func<PrivateArea, Vector3, float, bool>>(Method(typeFromHandle, "IsInside", list, typeof(Vector3), typeof(float)), list); _flashShield = Bind<Action<PrivateArea, bool>>(Method(typeFromHandle, "FlashShield", list, typeof(bool)), list); AreaAwake = Method(typeFromHandle, "Awake", list); AreaUpdateStatus = Method(typeFromHandle, "UpdateStatus", list); AreaGetHoverText = Method(typeFromHandle, "GetHoverText", list); AreaUseItem = Method(typeFromHandle, "UseItem", list, typeof(Humanoid), typeof(ItemData)); AreaInteract = Method(typeFromHandle, "Interact", list, typeof(Humanoid), typeof(bool), typeof(bool)); AreaToggleEnabled = Method(typeFromHandle, "RPC_ToggleEnabled", list, typeof(long), typeof(long)); AreaTogglePermitted = Method(typeFromHandle, "RPC_TogglePermitted", list, typeof(long), typeof(long), typeof(string)); RpcDamage = Method(typeof(WearNTear), "RPC_Damage", list, typeof(long), typeof(HitData)); ApplyDamage = Method(typeof(WearNTear), "ApplyDamage", list, typeof(float), typeof(HitData)); DamageNumber = Method(typeof(DamageText), "ShowText", list, typeof(DamageModifier), typeof(Vector3), typeof(float), typeof(bool)); AddWorldText = Method(typeof(DamageText), "AddInworldText", list, typeof(TextType), typeof(Vector3), typeof(float), typeof(string), typeof(bool)); ItemTooltip = Method(typeof(ItemData), "GetTooltip", list, typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int), typeof(bool)); ObjectDbAwake = Method(typeof(ObjectDB), "Awake", list); return list; } internal static bool IsEnabled(PrivateArea area) { return _isEnabled(area); } internal static void SetEnabled(PrivateArea area, bool enabled) { _setEnabled(area, enabled); } internal static bool IsPermitted(PrivateArea area, long playerId) { return _isPermitted(area, playerId); } internal static bool IsInside(PrivateArea area, Vector3 point) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return _isInside(area, point, 0f); } internal static void Flash(PrivateArea area) { _flashShield(area, arg2: false); } private static MethodInfo Method(Type type, string name, List<string> missing, params Type[] args) { MethodInfo method = type.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, args, null); if (method == null) { string[] array = new string[args.Length]; for (int i = 0; i < args.Length; i++) { array[i] = args[i].Name; } missing.Add(type.Name + "." + name + "(" + string.Join(", ", array) + ")"); } return method; } private static T Bind<T>(MethodInfo method, List<string> missing) where T : class { if (method == null) { return null; } try { return (T)(object)Delegate.CreateDelegate(typeof(T), method); } catch (ArgumentException) { missing.Add(method.DeclaringType?.Name + "." + method.Name + " has an unexpected signature"); return null; } } } internal readonly struct StoredCharge { internal float Energy { get; } internal float Fuel { get; } internal long StoredAtTicks { get; } internal long FullFromTicks { get; } internal StoredCharge(float energy, float fuel, long storedAtTicks, long fullFromTicks) { Energy = energy; Fuel = fuel; StoredAtTicks = storedAtTicks; FullFromTicks = fullFromTicks; } } internal readonly struct Charge { internal float Energy { get; } internal float Fuel { get; } internal Charge(float energy, float fuel) { Energy = energy; Fuel = fuel; } } internal readonly struct RechargeRule { internal float BasePercent { get; } internal float FuelPercent { get; } internal float CooldownFactor { get; } internal RechargeRule(float basePercent, float fuelPercent, float cooldownFactor) { BasePercent = basePercent; FuelPercent = fuelPercent; CooldownFactor = cooldownFactor; } } internal static class WardMath { internal static Charge At(StoredCharge stored, long nowTicks, float max, RechargeRule rule) { double fuel = Math.Max(0.0, stored.Fuel); if (max <= 0f) { return new Charge(0f, (float)fuel); } double energy = Math.Max(0.0, stored.Energy); long num = Math.Max(stored.StoredAtTicks, stored.FullFromTicks); Advance(ref energy, ref fuel, Seconds(Math.Min(nowTicks, num) - stored.StoredAtTicks), Clamp01(rule.CooldownFactor), max, rule); Advance(ref energy, ref fuel, Seconds(nowTicks - num), 1.0, max, rule); return new Charge((float)Math.Min(max, energy), (float)fuel); } private static void Advance(ref double energy, ref double fuel, double seconds, double factor, float max, RechargeRule rule) { if (seconds <= 0.0 || factor <= 0.0 || energy >= (double)max) { return; } if (fuel > 0.0 && rule.FuelPercent > 0f) { double num = Gain(max, rule.BasePercent + rule.FuelPercent, factor, 1.0); double num2 = Math.Min(seconds, fuel); double num3 = ((double)max - energy) / num; if (num3 <= num2) { energy = max; fuel -= num3; return; } energy += Gain(max, rule.BasePercent + rule.FuelPercent, factor, num2); fuel -= num2; seconds -= num2; } energy += Gain(max, rule.BasePercent, factor, seconds); } private static double Gain(float max, float percentPerSecond, double factor, double seconds) { return (double)max * (double)percentPerSecond * factor * seconds / 100.0; } private static double Seconds(long ticks) { return (double)Math.Max(0L, ticks) / 10000000.0; } private static double Clamp01(float value) { return Math.Min(1.0, Math.Max(0.0, value)); } internal static bool HasReached(float energy, float max, float percent) { if (max > 0f) { return (double)energy * 100.0 >= (double)max * (double)percent; } return false; } internal static float Unweakened(float dealt, float weaknessFactor) { if (!(weaknessFactor > 1f)) { return dealt; } return dealt / weaknessFactor; } internal static double SecondsUntil(StoredCharge stored, long nowTicks, float max, RechargeRule rule, float percent) { if (max <= 0f) { return double.PositiveInfinity; } Charge charge = At(stored, nowTicks, max, rule); if (HasReached(charge.Energy, max, percent)) { return 0.0; } double energy = charge.Energy; double fuel = charge.Fuel; double elapsed = 0.0; double seconds = Seconds(Math.Max(stored.StoredAtTicks, stored.FullFromTicks) - nowTicks); if (Reach(ref energy, ref fuel, ref elapsed, seconds, Clamp01(rule.CooldownFactor), max, rule, percent) || Reach(ref energy, ref fuel, ref elapsed, double.PositiveInfinity, 1.0, max, rule, percent)) { return elapsed; } return double.PositiveInfinity; } private static bool Reach(ref double energy, ref double fuel, ref double elapsed, double seconds, double factor, float max, RechargeRule rule, float percent) { if (seconds <= 0.0) { return false; } if (factor > 0.0 && fuel > 0.0 && rule.FuelPercent > 0f) { double num = Math.Min(seconds, fuel); double num2 = SecondsToReach(energy, max, percent, rule.BasePercent + rule.FuelPercent, factor); if (num2 <= num) { elapsed += num2; return true; } energy += Gain(max, rule.BasePercent + rule.FuelPercent, factor, num); fuel -= num; seconds -= num; elapsed += num; } if (factor > 0.0 && rule.BasePercent > 0f) { double num3 = SecondsToReach(energy, max, percent, rule.BasePercent, factor); if (num3 <= seconds) { elapsed += num3; return true; } energy += Gain(max, rule.BasePercent, factor, seconds); } elapsed += seconds; return false; } private static double SecondsToReach(double energy, float max, float percent, float percentPerSecond, double factor) { return ((double)max * (double)percent - energy * 100.0) / ((double)max * (double)percentPerSecond * factor); } internal static float Split(float cost, IReadOnlyList<float> available, float[] shares) { int count = available.Count; if (shares.Length != count) { throw new ArgumentException("shares must have one slot per ward", "shares"); } Array.Clear(shares, 0, count); if (cost <= 0f) { return 0f; } float[] array = new float[count]; int[] array2 = new int[count]; for (int i = 0; i < count; i++) { array[i] = Math.Max(0f, available[i]); array2[i] = i; } Array.Sort(array, array2); double num = cost; for (int j = 0; j < count; j++) { if (!(num > 0.0)) { break; } double num2 = Math.Min(array[j], num / (double)(count - j)); shares[array2[j]] = (float)num2; num -= num2; } return (float)Math.Max(0.0, num); } internal static float Holds(float chargePercent, float bleedBelowPercent, float bleedMaxPercent) { if (bleedBelowPercent <= 0f || chargePercent >= bleedBelowPercent) { return 1f; } float num = Math.Min(100f, Math.Max(0f, bleedMaxPercent)) / 100f; float num2 = 1f - Math.Max(0f, chargePercent) / bleedBelowPercent; return 1f - num * num2; } internal static float SplitWithBleed(float cost, IReadOnlyList<float> available, IReadOnlyList<float> chargePercent, float bleedBelowPercent, float bleedMaxPercent, float[] shares) { int count = available.Count; if (chargePercent.Count != count) { throw new ArgumentException("chargePercent must have one entry per ward", "chargePercent"); } List<int> list = new List<int>(count); List<float> list2 = new List<float>(count); for (int i = 0; i < count; i++) { if (Holds(chargePercent[i], bleedBelowPercent, bleedMaxPercent) >= 1f) { list.Add(i); list2.Add(available[i]); } } if (list.Count == count) { return Split(cost, available, shares); } if (shares.Length != count) { throw new ArgumentException("shares must have one slot per ward", "shares"); } Array.Clear(shares, 0, count); if (cost <= 0f) { return 0f; } double num = (double)cost / (double)count; double num2 = 0.0; for (int j = 0; j < count; j++) { float num3 = Holds(chargePercent[j], bleedBelowPercent, bleedMaxPercent); if (num3 < 1f) { double num4 = Math.Min((double)num3 * num, Math.Max(0f, available[j])); shares[j] = (float)num4; num2 += num - num4; } } if (list.Count == 0) { return (float)num2; } float[] array = new float[list.Count]; float result = Split((float)(num * (double)list.Count + num2), list2, array); for (int k = 0; k < list.Count; k++) { shares[list[k]] = array[k]; } return result; } } internal static class WardStore { internal const float Empty = 0.001f; private const string ItemKey = "wardcore_item"; private const string EnergyKey = "wardcore_energy"; private const string RechargeFromKey = "wardcore_time"; private const string StoredAtKey = "wardcore_stored"; private const string FuelKey = "wardcore_fuel"; private const double FuelRounding = 0.0001; private const string DepletedKey = "wardcore_depleted"; internal static long Now() { return ZNet.instance.GetTime().Ticks; } internal static bool IsPlayerWard(PrivateArea area) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 return (int)area.m_ownerFaction == 0; } internal static ZDO Zdo(PrivateArea area) { ZNetView component = ((Component)area).GetComponent<ZNetView>(); if (!((Object)(object)component != (Object)null) || !component.IsValid()) { return null; } return component.GetZDO(); } internal static string InstalledItem(ZDO zdo) { return zdo.GetString("wardcore_item", ""); } internal static string CorePrefab(ZDO zdo) { string text = InstalledItem(zdo); if (text.Length <= 0) { return Settings.BuiltIn.Value; } return text; } internal static bool TryGetMax(ZDO zdo, out float max) { return Settings.TryGetMax(InstalledItem(zdo), out max); } internal static bool StrikesBack(PrivateArea area) { ZDO val = Zdo(area); if (val != null) { return CorePrefab(val) == "Thunderstone"; } return false; } internal static bool IsDepleted(ZDO zdo) { return zdo.GetBool("wardcore_depleted", false); } internal static float Energy(ZDO zdo, float max, long now) { if (!Stored(zdo, now, out var stored)) { return 0f; } return WardMath.At(stored, now, max, Settings.Recharge).Energy; } internal static float FuelSeconds(ZDO zdo, long now) { StoredCharge stored; return ChargeAt(zdo, now, out stored).Fuel; } internal static int FuelItems(float seconds, bool countBurning) { double num = (double)seconds / (double)Settings.FuelSecondsPerItem.Value; if (!countBurning) { return (int)Math.Floor(num + 0.0001); } return (int)Math.Ceiling(num - 0.0001); } internal static double SecondsUntilReady(ZDO zdo, float max, long now) { Stored(zdo, now, out var stored); return WardMath.SecondsUntil(stored, now, max, Settings.Recharge, Settings.ReactivateAtPercent.Value); } internal static bool IsReady(PrivateArea area, float energy, float max) { if (!WardAccess.IsEnabled(area)) { return ReachedThreshold(energy, max); } return true; } internal static float ChargePercent(float energy, float max) { return energy * 100f / max; } internal static float Holds(float energy, float max) { return WardMath.Holds(ChargePercent(energy, max), Settings.BleedBelowPercent.Value, Settings.BleedMaxPercent.Value); } internal static bool HasAccess(PrivateArea area, long playerId) { Piece component = ((Component)area).GetComponent<Piece>(); if (!((Object)(object)component != (Object)null) || component.GetCreator() != playerId) { return WardAccess.IsPermitted(area, playerId); } return true; } internal static void Settle(PrivateArea area) { ZDO val = OwnedZdo(area); if (val == null) { return; } long num = Now(); if (!Stored(val, num, out var _)) { Store(val, new Charge(0f, 0f), num, num); } if (TryGetMax(val, out var max)) { float num2 = Energy(val, max, num); bool flag = WardAccess.IsEnabled(area); if (flag && num2 <= 0.001f) { val.Set("wardcore_depleted", true); WardAccess.SetEnabled(area, enabled: false); } else if (!flag && IsDepleted(val) && ReachedThreshold(num2, max)) { val.Set("wardcore_depleted", false); WardAccess.SetEnabled(area, enabled: true); } } } internal static void Drain(PrivateArea area, float amount) { ZDO val = OwnedZdo(area); if (val != null && TryGetMax(val, out var max)) { long num = Now(); long num2 = (long)(Settings.RechargeCooldownSeconds.Value * 10000000f); Charge charge = WardMath.At(StoredOrEmpty(val, num), num, max, Settings.Recharge); Store(val, new Charge(Math.Max(0f, charge.Energy - amount), charge.Fuel), num, num + num2); Settle(area); } } internal static void Install(PrivateArea area, string prefab) { ZDO val = OwnedZdo(area); if (val != null) { long num = Now(); StoredCharge stored; float fuel = ChargeAt(val, num, out stored).Fuel; val.Set("wardcore_item", prefab); Store(val, new Charge(0f, fuel), num, num); Settle(area); } } internal static void Feed(PrivateArea area, int count) { ZDO val = OwnedZdo(area); if (val != null && count > 0) { long num = Now(); StoredCharge stored; Charge charge = ChargeAt(val, num, out stored); float num2 = Math.Min(Settings.FuelCapacitySeconds, charge.Fuel + (float)count * Settings.FuelSecondsPerItem.Value); Store(val, new Charge(charge.Energy, num2), num, Math.Max(num, stored.FullFromTicks)); Settle(area); if (Settings.LogAbsorb.Value) { Plugin.Log.LogInfo((object)(Plugin.Stamp() + "Fed " + count + " " + Settings.Fuel + ": " + FuelItems(num2, countBurning: true) + " / " + Settings.FuelCapacity.Value + " (" + num2.ToString("0.#", CultureInfo.InvariantCulture) + " s)")); } } } internal static bool ToggleRequested(PrivateArea area) { ZDO val = OwnedZdo(area); if (val == null) { return true; } if (WardAccess.IsEnabled(area)) { val.Set("wardcore_depleted", false); return true; } if (!TryGetMax(val, out var max) || ReachedThreshold(Energy(val, max, Now()), max)) { val.Set("wardcore_depleted", false); return true; } val.Set("wardcore_depleted", !IsDepleted(val)); return false; } private static ZDO OwnedZdo(PrivateArea area) { ZNetView component = ((Component)area).GetComponent<ZNetView>(); if (!((Object)(object)component != (Object)null) || !component.IsValid() || !component.IsOwner()) { return null; } return component.GetZDO(); } private static bool ReachedThreshold(float energy, float max) { return WardMath.HasReached(energy, max, Settings.ReactivateAtPercent.Value); } private static Charge ChargeAt(ZDO zdo, long now, out StoredCharge stored) { stored = StoredOrEmpty(zdo, now); if (!TryGetMax(zdo, out var max)) { return new Charge(stored.Energy, stored.Fuel); } return WardMath.At(stored, now, max, Settings.Recharge); } private static StoredCharge StoredOrEmpty(ZDO zdo, long now) { Stored(zdo, now, out var stored); return stored; } private static bool Stored(ZDO zdo, long now, out StoredCharge stored) { long num = zdo.GetLong("wardcore_time", 0L); if (num == 0L) { stored = new StoredCharge(0f, 0f, now, now); return false; } stored = new StoredCharge(zdo.GetFloat("wardcore_energy", 0f), zdo.GetFloat("wardcore_fuel", 0f), zdo.GetLong("wardcore_stored", num), num); return true; } private static void Store(ZDO zdo, Charge charge, long storedAt, long fullFrom) { zdo.Set("wardcore_energy", charge.Energy); zdo.Set("wardcore_fuel", charge.Fuel); zdo.Set("wardcore_stored", storedAt); zdo.Set("wardcore_time", fullFrom); } } }