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 EliteCreaturesReborn v3.36.0
plugins\EliteCreaturesReborn.dll
Decompiled a week 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.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 BundlePrefabs; using Charter; using ConfigReload; using EliteCraftingLink; using EliteCreaturesReborn.Altars; using EliteCreaturesReborn.Arena; using EliteCreaturesReborn.Arena.Gate; using EliteCreaturesReborn.Arena.Hall; using EliteCreaturesReborn.Arena.Rewards; using EliteCreaturesReborn.Arena.Travel; using EliteCreaturesReborn.Arena.Trial; using EliteCreaturesReborn.Aspects; using EliteCreaturesReborn.Breeding; using EliteCreaturesReborn.Commands; using EliteCreaturesReborn.Compendium; using EliteCreaturesReborn.Config; using EliteCreaturesReborn.Creatures.BatFiend; using EliteCreaturesReborn.Display; using EliteCreaturesReborn.Hints; using EliteCreaturesReborn.Horde; using EliteCreaturesReborn.Loot; using EliteCreaturesReborn.Mutations; using EliteCreaturesReborn.Patches; using EliteCreaturesReborn.Raids; using EliteCreaturesReborn.Recap; using EliteCreaturesReborn.Recap.Window; using EliteCreaturesReborn.Rendering; using EliteCreaturesReborn.Rules; using EliteCreaturesReborn.Runtime; using EliteCreaturesReborn.Scaling; using EliteCreaturesReborn.Tally; using EliteCreaturesReborn.Taming; using EliteCreaturesReborn.Traits; using EliteCreaturesReborn.Util; using EliteCreaturesReborn.Visuals; using HarmonyLib; using Hotkeys; using LocalEffects; using Microsoft.CodeAnalysis; using PatchGuard; using PlateColumn; using PlayerGrid; using RecipeCounts; using StatusTemplates; using TMPro; using Translations; using Unity.Collections; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; using WindowInput; using YamlDotNet.Core; using YamlDotNet.Core.Events; 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: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Gerald Glasgow")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) Gerald Glasgow. Licensed under the GNU General Public License v3.0.")] [assembly: AssemblyDescription("Elite Creatures Reborn: named, visible mutations that make every creature a different fight")] [assembly: AssemblyFileVersion("3.36.0.0")] [assembly: AssemblyInformationalVersion("3.36.0+0fc4cd5425184492114823d48d99397018a1a46b")] [assembly: AssemblyProduct("Elite Creatures Reborn")] [assembly: AssemblyTitle("EliteCreaturesReborn")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("3.36.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 EliteCreaturesReborn { [BepInPlugin("gglasgow.elitecreaturesreborn", "Elite Creatures Reborn", "3.36.0")] public sealed class Plugin : BaseUnityPlugin { private void Awake() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown Log.Bind(((BaseUnityPlugin)this).Logger); ConfigReloader.HoldSaves(((BaseUnityPlugin)this).Config); Configuration.BindAll(((BaseUnityPlugin)this).Config); RuleFile.Initialise(); ServerLock.Setup("gglasgow.elitecreaturesreborn", "Elite Creatures Reborn", "3.36.0"); Harmony val = new Harmony("gglasgow.elitecreaturesreborn"); val.PatchAll(typeof(Plugin).Assembly); Lang.Install(val, typeof(Plugin).Assembly, "EliteCreaturesReborn", ((BaseUnityPlugin)this).Logger); AnyQuality.Install(val); RecapHost.Attach(((Component)this).gameObject, val); GameWindow.Add(() => AltarWindow.IsOpen, AltarWindow.Close); ((Component)this).gameObject.AddComponent<TallyFlush>(); RaidMarker.Setup(val); BatFiendPrefabs.Install(val); ((Component)this).gameObject.AddComponent<RaidTicker>(); ((Component)this).gameObject.AddComponent<TameTicker>(); ServerLock.Install(val); Guard.Install(val, ((BaseUnityPlugin)this).Logger, typeof(Plugin).Assembly); ConfigReloader.Setup(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); RuleReload.Attach(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Elite Creatures Reborn 3.36.0 ready."); } } internal static class PluginInfo { public const string Guid = "gglasgow.elitecreaturesreborn"; public const string Name = "Elite Creatures Reborn"; public const string PluginVersion = "3.36.0"; public const string Author = "Gerald Glasgow"; public const string Licence = "GNU General Public License v3.0"; } } namespace EliteCreaturesReborn.Visuals { public static class CosmeticClone { public static GameObject? Spawn(GameObject? prefab, Transform parent, Vector3 position, bool endless) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (!Machine.Headless) { return LocalEffect.Attach(prefab, parent, position, endless, Density()); } return null; } public static void Flash(GameObject? prefab, Vector3 position, float radius) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!Machine.Headless) { LocalEffect.Flash(prefab, position, radius, Density()); } } public static void FlashWhole(GameObject? prefab, Vector3 position, float radius, float scale) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!Machine.Headless) { LocalEffect.FlashWhole(prefab, position, radius, scale, Density()); } } public static void Sound(GameObject? prefab, Vector3 position) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!Machine.Headless) { LocalEffect.Sound(prefab, position); } } public static void SoundOnly(GameObject? prefab, Vector3 position, float volume) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!Machine.Headless) { LocalEffect.SoundOnly(prefab, position, volume); } } private static float Density() { return Mathf.Clamp01(Configuration.EffectDensity.Value); } } public static class EffectResolver { public static readonly string[] Cloud = new string[7] { "poison", "blob", "ooze", "puke", "gas", "spore", "smoke" }; public static readonly string[] Blast = new string[7] { "explos", "blast", "fire", "bomb", "death", "burst", "flame" }; public static readonly string[] BlastSound = new string[3] { "explo", "bomb", "blast" }; public static readonly string[] Warning = new string[6] { "smoke", "fire", "burn", "charge", "glow", "spark" }; public static readonly string[] Reflect = new string[6] { "staffshield_hit", "shield", "spark", "hit", "block", "staff" }; public static readonly string[] Devour = new string[7] { "aspect_death", "death", "gore", "corpse", "destr", "blood", "hit" }; public static readonly string[] Steal = new string[6] { "steal", "pickpocket", "pocket", "grab", "loot", "coin" }; public static readonly string[] Summon = new string[5] { "spawn", "summon", "portal", "smoke", "puff" }; public static readonly string[] Phantom = new string[6] { "ghost", "wisp", "puff", "smoke", "vanish", "poof" }; private const string SoundKey = "sound:"; private static readonly Dictionary<string, GameObject?> Cache = new Dictionary<string, GameObject>(); public static GameObject? Resolve(string name, string[] keywords, string setting = "") { return Lookup(name, keywords, setting, sound: false); } public static GameObject? ResolveSound(string name, string[] keywords, string setting = "") { return Lookup(name, keywords, setting, sound: true); } public static GameObject? ForRole(string role) { switch (role) { case "reflect": case "devour": return ByKeyword(role, (role == "reflect") ? Reflect : Devour); case "summon": case "phantom": return ByKeyword(role, (role == "summon") ? Summon : Phantom); default: return null; case "steal": return ByKeyword(role, Steal); } } public static GameObject? ByKeyword(string role, string[] keywords) { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return null; } if (Cache.TryGetValue(role, out GameObject value)) { return value; } GameObject val = Nearest(instance, keywords, sound: false); if ((Object)(object)val == (Object)null) { Log.Warn("no vanilla effect matched the " + role + " tell; it will not be shown"); } else if (Log.Diagnostics) { Log.Diag("the " + role + " tell is " + ((Object)val).name); } Cache[role] = val; return val; } public static IEnumerable<GameObject> Prefabs(ZNetScene scene) { foreach (GameObject prefab in scene.m_prefabs) { yield return prefab; } foreach (GameObject nonNetViewPrefab in scene.m_nonNetViewPrefabs) { yield return nonNetViewPrefab; } } public static bool IsEffectName(GameObject? prefab) { if ((Object)(object)prefab != (Object)null) { if (!Named(prefab, sound: false)) { return Named(prefab, sound: true); } return true; } return false; } private static GameObject? Lookup(string name, string[] keywords, string setting, bool sound) { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || string.IsNullOrEmpty(name)) { return null; } string key = (sound ? ("sound:" + name) : name); if (Cache.TryGetValue(key, out GameObject value)) { return value; } GameObject val = Find(instance, name, keywords, setting, sound); Cache[key] = val; return val; } private static GameObject? Find(ZNetScene scene, string name, string[] keywords, string setting, bool sound) { GameObject val = Fits(scene.GetPrefab(name), sound); if ((Object)(object)val != (Object)null) { return val; } string text = (string.IsNullOrEmpty(setting) ? "" : (" (from setting '" + setting + "')")); GameObject val2 = Nearest(scene, keywords, sound); Log.Warn(((Object)(object)val2 != (Object)null) ? ("effect prefab '" + name + "'" + text + " not found; falling back to '" + ((Object)val2).name + "'") : ("effect prefab '" + name + "'" + text + " not found and no fallback effect exists - it stays " + (sound ? "silent" : "invisible"))); return val2; } private static GameObject? Nearest(ZNetScene scene, string[] keywords, bool sound) { foreach (string value in keywords) { foreach (GameObject item in Prefabs(scene)) { if ((Object)(object)item != (Object)null && Named(item, sound) && ((Object)item).name.ToLowerInvariant().Contains(value) && (Object)(object)Fits(item, sound) != (Object)null) { return item; } } } return null; } private static bool Named(GameObject prefab, bool sound) { string text = ((Object)prefab).name.ToLowerInvariant(); if (!sound) { if (!text.StartsWith("fx_")) { return text.StartsWith("vfx_"); } return true; } return text.StartsWith("sfx_"); } private static GameObject? Fits(GameObject? prefab, bool sound) { if (!sound) { return Usable(prefab); } return Audible(prefab); } private static GameObject? Usable(GameObject? prefab) { if ((Object)(object)prefab == (Object)null) { return null; } if (!((Object)(object)prefab.GetComponentInChildren<ParticleSystem>(true) != (Object)null) && !((Object)(object)prefab.GetComponentInChildren<Renderer>(true) != (Object)null)) { return null; } return prefab; } private static GameObject? Audible(GameObject? prefab) { if (!((Object)(object)prefab != (Object)null) || !((Object)(object)prefab.GetComponentInChildren<ZSFX>(true) != (Object)null)) { return null; } return prefab; } } public sealed class LingeringVisual : MonoBehaviour { private const float BaselineRadius = 4f; private const float FadeSeconds = 1f; private ParticleSystem[] _systems = Array.Empty<ParticleSystem>(); private Light[] _lights = Array.Empty<Light>(); private AudioSource[] _audio = Array.Empty<AudioSource>(); private float[] _lightBase = Array.Empty<float>(); private float[] _audioBase = Array.Empty<float>(); private float _life; private float _age; private bool _fading; public static void Attach(GameObject host, GameObject? prefab, float life, float radius) { LingeringVisual visual = host.AddComponent<LingeringVisual>(); visual._life = Mathf.Max(life, 0.01f); Guard.Run("LingeringVisual.Build", delegate { visual.Build(host, prefab, radius); }); } public static void Hold(GameObject host, GameObject? prefab, float radius) { Attach(host, prefab, float.PositiveInfinity, radius); } private void Build(GameObject host, GameObject? prefab, float radius) { //IL_000d: 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) //IL_0047: Unknown result type (might be due to invalid IL or missing references) GameObject val = CosmeticClone.Spawn(prefab, host.transform, host.transform.position, endless: true); if ((Object)(object)val == (Object)null) { ((Behaviour)this).enabled = false; return; } Transform transform = val.transform; transform.localScale *= Mathf.Max(radius, 0.01f) / 4f; Loop(val); Capture(val); } private void Loop(GameObject clone) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) _systems = clone.GetComponentsInChildren<ParticleSystem>(true); ParticleSystem[] systems = _systems; foreach (ParticleSystem obj in systems) { MainModule main = obj.main; ((MainModule)(ref main)).loop = true; obj.Play(true); } } private void Capture(GameObject clone) { _lights = clone.GetComponentsInChildren<Light>(true); _audio = clone.GetComponentsInChildren<AudioSource>(true); _lightBase = Baseline(_lights, (Light l) => l.intensity); _audioBase = Baseline(_audio, (AudioSource a) => a.volume); } private static float[] Baseline<T>(T[] items, Func<T, float> read) { float[] array = new float[items.Length]; for (int i = 0; i < items.Length; i++) { array[i] = read(items[i]); } return array; } private void Update() { Guard.Run("LingeringVisual.Update", delegate(LingeringVisual self) { self.Step(); }, this); } private void Step() { _age += Time.deltaTime; if (!_fading && _age >= _life - 1f) { BeginFade(); } if (_fading) { Dim(Mathf.Clamp01((_life - _age) / 1f)); } } private void BeginFade() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) _fading = true; ParticleSystem[] systems = _systems; for (int i = 0; i < systems.Length; i++) { EmissionModule emission = systems[i].emission; ((EmissionModule)(ref emission)).enabled = false; } } private void Dim(float factor) { for (int i = 0; i < _lights.Length; i++) { if ((Object)(object)_lights[i] != (Object)null) { _lights[i].intensity = _lightBase[i] * factor; } } for (int j = 0; j < _audio.Length; j++) { if ((Object)(object)_audio[j] != (Object)null) { _audio[j].volume = _audioBase[j] * factor; } } } } internal static class StarCorpse { public static void Made(Character creature, Ragdoll ragdoll) { int num = StarLook.StarsOf(creature); ZNetView component = ((Component)ragdoll).GetComponent<ZNetView>(); if (num > 0 && !((Object)(object)component == (Object)null) && component.IsValid()) { component.GetZDO().Set("ecr_corpse_stars", num); Show(ragdoll, num); } } public static void Wake(Ragdoll ragdoll) { ZNetView component = ((Component)ragdoll).GetComponent<ZNetView>(); int num = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO().GetInt("ecr_corpse_stars", 0) : 0); if (num > 0) { Show(ragdoll, num); } } private static void Show(Ragdoll ragdoll, int stars) { LevelEffects[] componentsInChildren = ((Component)ragdoll).GetComponentsInChildren<LevelEffects>(); for (int i = 0; i < componentsInChildren.Length; i++) { StarLook.Show(componentsInChildren[i], stars); } } } public static class StarLook { public static void Apply(Character character, int stars) { LevelEffects[] componentsInChildren = ((Component)character).GetComponentsInChildren<LevelEffects>(); foreach (LevelEffects obj in componentsInChildren) { obj.m_character = character; Show(obj, stars); } } public static void Show(LevelEffects effects, int stars) { //IL_0013: 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_0026: Unknown result type (might be due to invalid IL or missing references) int num = LevelFor(effects, stars); if (num > 1) { Vector3 localScale = ((Component)effects).transform.localScale; effects.SetupLevelVisualization(num); ((Component)effects).transform.localScale = localScale; } } public static LevelSetup? SetupFor(LevelEffects effects) { int num = LevelFor(effects, StarsOf(effects.m_character)); if (num <= 1) { return null; } return effects.m_levelSetups[num - 2]; } public static int StarsOf(Character? character) { EliteController eliteController = (((Object)(object)character != (Object)null) ? ((Component)character).GetComponent<EliteController>() : null); if (!((Object)(object)eliteController != (Object)null) || !eliteController.Ready) { return 0; } return eliteController.Traits.Stars; } private static int LevelFor(LevelEffects effects, int stars) { if (stars > 0) { return Mathf.Min(stars + 1, effects.m_levelSetups.Count + 1); } return 1; } } } namespace EliteCreaturesReborn.Util { internal static class ColorParse { public static Color Hex(string text, Color fallback) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) Color result = default(Color); if (!string.IsNullOrWhiteSpace(text) && ColorUtility.TryParseHtmlString(text.Trim(), ref result)) { return result; } return fallback; } } internal static class Dice { public static bool Percent(float chancePercent) { if (chancePercent <= 0f) { return false; } return Random.Range(0f, 100f) <= chancePercent; } public static float Draw() { return Random.Range(0f, 100f); } public static int Round(float amount) { int num = Mathf.FloorToInt(amount); if (!(Random.value < amount - (float)num)) { return num; } return num + 1; } } internal static class Log { private static ManualLogSource? _log; public static bool Diagnostics; public static void Bind(ManualLogSource log) { _log = log; } public static void Info(string message) { ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)message); } } public static void Debug(string message) { ManualLogSource? log = _log; if (log != null) { log.LogDebug((object)message); } } public static void Warn(string message) { ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)message); } } public static void Error(string message) { ManualLogSource? log = _log; if (log != null) { log.LogError((object)message); } } public static void Diag(string message) { if (Diagnostics) { ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)("[diag] " + message)); } } } } internal static class Machine { public static bool Headless { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsDedicated(); } return false; } } } public static class NetTime { public static long NowMs() { if (!((Object)(object)ZNet.instance != (Object)null)) { return 0L; } return (long)(ZNet.instance.GetTimeSeconds() * 1000.0); } public static float SecondsSince(long stampMs) { return (float)(NowMs() - stampMs) / 1000f; } public static double DaySeconds() { if (!((Object)(object)EnvMan.instance != (Object)null)) { return 1800.0; } return EnvMan.instance.m_dayLengthSec; } } } namespace EliteCreaturesReborn.Traits { public enum Aspect { None, Reflective, Shielded, Mending, Summoner, Elementalist, Enraged, Twin, Phantom, Adaptive, Fixated, Stormbound, Gravitic, Colossal, Tethered, Bountiful, Portalbound, Nightfall, Brutal, Echoing } public static class AspectCatalog { public static readonly Aspect[] InOrder = new Aspect[19] { Aspect.Reflective, Aspect.Shielded, Aspect.Mending, Aspect.Summoner, Aspect.Elementalist, Aspect.Enraged, Aspect.Twin, Aspect.Phantom, Aspect.Adaptive, Aspect.Fixated, Aspect.Stormbound, Aspect.Gravitic, Aspect.Colossal, Aspect.Tethered, Aspect.Bountiful, Aspect.Portalbound, Aspect.Nightfall, Aspect.Brutal, Aspect.Echoing }; public static readonly Aspect[] Outcomes = new Aspect[20] { Aspect.None, Aspect.Reflective, Aspect.Shielded, Aspect.Mending, Aspect.Summoner, Aspect.Elementalist, Aspect.Enraged, Aspect.Twin, Aspect.Phantom, Aspect.Adaptive, Aspect.Fixated, Aspect.Stormbound, Aspect.Gravitic, Aspect.Colossal, Aspect.Tethered, Aspect.Bountiful, Aspect.Portalbound, Aspect.Nightfall, Aspect.Brutal, Aspect.Echoing }; public static string Word(Aspect aspect) { if (aspect != Aspect.None) { return aspect.ToString(); } return ""; } public static string Name(Aspect aspect) { if (aspect != Aspect.None) { return Lang.Get("ecr_aspect_" + aspect.ToString().ToLowerInvariant()); } return ""; } public static string Key(Aspect aspect) { if (aspect != Aspect.None) { return aspect.ToString(); } return "none"; } public static Aspect? FromName(string name) { Aspect[] outcomes = Outcomes; foreach (Aspect aspect in outcomes) { if (string.Equals(Key(aspect), name, StringComparison.OrdinalIgnoreCase)) { return aspect; } } return null; } } public static class AspectRoller { private const int BodyMask = 16768; public static Aspect Roll(string bossPrefab, Aspect? exclude) { AspectRules aspects = RuleState.Active.Boss.Aspects; if (!aspects.Enabled) { return Aspect.None; } List<KeyValuePair<Aspect, float>> list = Pool(aspects, bossPrefab, exclude); if (list.Count != 0) { return Pick(list); } return exclude.GetValueOrDefault(); } public static List<Aspect> Choices(string bossPrefab) { AspectRules aspects = RuleState.Active.Boss.Aspects; if (!aspects.Enabled) { return new List<Aspect>(); } return Pool(aspects, bossPrefab, null).ConvertAll((KeyValuePair<Aspect, float> entry) => entry.Key); } public static BossAspects RollBoss(string bossPrefab, Aspect? exclude) { Aspect num = Roll(bossPrefab, exclude); return new BossAspects(num, (num == Aspect.Bountiful) ? RollExtras(bossPrefab) : 0); } public static int RollExtras(string bossPrefab, int keep = 0) { List<KeyValuePair<Aspect, float>> list = ExtrasPool(RuleState.Active.Boss.Aspects, bossPrefab); list.RemoveAll((KeyValuePair<Aspect, float> entry) => !Fits(keep, entry.Key)); int num = keep; for (int num2 = Count(keep); num2 < ExtraCount(); num2++) { if (list.Count <= 0) { break; } Aspect drawn = Pick(list); num |= 1 << (int)drawn; list.RemoveAll((KeyValuePair<Aspect, float> entry) => entry.Key == drawn || (BringsBodies(drawn) && BringsBodies(entry.Key))); } return num; } public static int ExtraCount() { return Mathf.Max(0, Mathf.RoundToInt(RuleState.Active.Boss.Aspects.PowerOf(Aspect.Bountiful, "extra aspects"))); } public static List<Aspect> ExtraChoices(string bossPrefab) { AspectRules aspects = RuleState.Active.Boss.Aspects; if (!aspects.Enabled) { return new List<Aspect>(); } return ExtrasPool(aspects, bossPrefab).ConvertAll((KeyValuePair<Aspect, float> entry) => entry.Key); } public static bool CanFill(string bossPrefab, int keep) { if (Count(keep) < ExtraCount()) { return ExtraChoices(bossPrefab).Exists((Aspect aspect) => Fits(keep, aspect)); } return false; } public static bool Fits(int mask, Aspect aspect) { if ((mask & (1 << (int)aspect)) == 0) { if (BringsBodies(aspect)) { return !HoldsBodies(mask); } return true; } return false; } public static int Count(int mask) { int num = 0; for (int num2 = mask; num2 != 0; num2 &= num2 - 1) { num++; } return num; } public static bool BringsBodies(Aspect aspect) { return (0x4180 & (1 << (int)aspect)) != 0; } public static bool HoldsBodies(int mask) { return (0x4180 & mask) != 0; } private static List<KeyValuePair<Aspect, float>> ExtrasPool(AspectRules rules, string bossPrefab) { List<KeyValuePair<Aspect, float>> list = Pool(rules, bossPrefab, Aspect.None); list.RemoveAll((KeyValuePair<Aspect, float> entry) => entry.Key == Aspect.Bountiful); if (list.Count == 0 && Registrations.AspectsOf(bossPrefab) != null) { list = RulePool(rules, bossPrefab, Aspect.None); list.RemoveAll((KeyValuePair<Aspect, float> entry) => entry.Key == Aspect.Bountiful); } return list; } private static List<KeyValuePair<Aspect, float>> Pool(AspectRules rules, string bossPrefab, Aspect? exclude) { Aspect[] array = Registrations.AspectsOf(bossPrefab); if (array == null) { return RulePool(rules, bossPrefab, exclude); } return OwnPool(rules, array, exclude); } private static List<KeyValuePair<Aspect, float>> OwnPool(AspectRules rules, Aspect[] own, Aspect? exclude) { List<KeyValuePair<Aspect, float>> list = new List<KeyValuePair<Aspect, float>>(); foreach (Aspect aspect in own) { if (aspect != exclude) { list.Add(new KeyValuePair<Aspect, float>(aspect, rules.ChanceOf(aspect))); } } if (list.Exists((KeyValuePair<Aspect, float> entry) => entry.Value > 0f)) { list.RemoveAll((KeyValuePair<Aspect, float> entry) => entry.Value <= 0f); return list; } return list.ConvertAll((KeyValuePair<Aspect, float> entry) => new KeyValuePair<Aspect, float>(entry.Key, 1f)); } private static List<KeyValuePair<Aspect, float>> RulePool(AspectRules rules, string bossPrefab, Aspect? exclude) { List<KeyValuePair<Aspect, float>> list = new List<KeyValuePair<Aspect, float>>(); Aspect[] outcomes = AspectCatalog.Outcomes; foreach (Aspect aspect in outcomes) { float num = rules.ChanceOf(aspect); if (aspect != exclude && num > 0f && rules.InRotation(bossPrefab, aspect)) { list.Add(new KeyValuePair<Aspect, float>(aspect, num)); } } return list; } private static Aspect Pick(List<KeyValuePair<Aspect, float>> pool) { float num = 0f; foreach (KeyValuePair<Aspect, float> item in pool) { num += item.Value; } float num2 = Random.Range(0f, num); foreach (KeyValuePair<Aspect, float> item2 in pool) { num2 -= item2.Value; if (num2 < 0f) { return item2.Key; } } return pool[pool.Count - 1].Key; } } public static class AspectStore { private static readonly int AltarAspectHash = StringExtensionMethods.GetStableHashCode("ecr_altar_aspect"); private static readonly int AltarExtraHash = StringExtensionMethods.GetStableHashCode("ecr_altar_extra"); private static readonly int AltarShiftAtHash = StringExtensionMethods.GetStableHashCode("ecr_altar_shift"); private static readonly int AltarStarsHash = StringExtensionMethods.GetStableHashCode("ecr_altar_stars"); private static readonly int AltarPickStarsHash = StringExtensionMethods.GetStableHashCode("ecr_altar_pick_stars"); private static readonly int AltarPickAspectHash = StringExtensionMethods.GetStableHashCode("ecr_altar_pick_aspect"); private static readonly int AltarPickExtrasHash = StringExtensionMethods.GetStableHashCode("ecr_altar_pick_extras"); private static readonly int SummonWavesHash = StringExtensionMethods.GetStableHashCode("ecr_waves"); private static readonly int PhantomOrderHash = StringExtensionMethods.GetStableHashCode("ecr_phantom_order"); private static readonly int PhantomSplitsHash = StringExtensionMethods.GetStableHashCode("ecr_phantom_splits"); private static readonly KeyValuePair<int, int> TwinPartnerKey = ZDO.GetHashZDOID("ecr_twin"); private static readonly KeyValuePair<int, int> TetherPartnerKey = ZDO.GetHashZDOID("ecr_tether"); private static readonly KeyValuePair<int, int> PhantomOfKey = ZDO.GetHashZDOID("ecr_phantom_of"); private static readonly KeyValuePair<int, int> EchoOfKey = ZDO.GetHashZDOID("ecr_echo_of"); private const int Unrolled = -1; public static bool AltarRolled(ZDO zdo) { return zdo.GetInt(AltarAspectHash, -1) != -1; } public static Aspect GetAltarAspect(ZDO zdo) { int num = zdo.GetInt(AltarAspectHash, -1); if (num != -1) { return (Aspect)num; } return Aspect.None; } public static BossAspects GetAltarAspects(ZDO zdo) { return new BossAspects(GetAltarAspect(zdo), zdo.GetInt(AltarExtraHash, 0)); } public static long GetAltarShiftAt(ZDO zdo) { return zdo.GetLong(AltarShiftAtHash, 0L); } public static void SetAltarAspects(ZDO zdo, BossAspects aspects) { zdo.Set("ecr_altar_aspect", (int)aspects.Headline); if (zdo.GetInt(AltarExtraHash, 0) != aspects.Extras) { zdo.Set("ecr_altar_extra", aspects.Extras); } } public static void SetAltarShiftAt(ZDO zdo, long shiftAtMs) { zdo.Set("ecr_altar_shift", shiftAtMs); } public static bool AltarStarsRolled(ZDO zdo) { return zdo.GetInt(AltarStarsHash, -1) != -1; } public static int GetAltarStars(ZDO zdo) { int num = zdo.GetInt(AltarStarsHash, -1); if (num >= 0) { return num; } return 0; } public static void SetAltarStars(ZDO zdo, int stars) { zdo.Set("ecr_altar_stars", stars); } public static int GetPickedStars(ZDO zdo) { return zdo.GetInt(AltarPickStarsHash, -1); } public static int GetPickedAspect(ZDO zdo) { return zdo.GetInt(AltarPickAspectHash, -1); } public static void SetPicks(ZDO zdo, int stars, int aspect) { zdo.Set("ecr_altar_pick_stars", stars); zdo.Set("ecr_altar_pick_aspect", aspect); } public static int GetPickedExtras(ZDO zdo) { return zdo.GetInt(AltarPickExtrasHash, 0); } public static void SetPickedExtras(ZDO zdo, int extras) { if (zdo.GetInt(AltarPickExtrasHash, 0) != extras) { zdo.Set("ecr_altar_pick_extras", extras); } } public static ZDOID GetTwin(ZDO zdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return zdo.GetZDOID(TwinPartnerKey); } public static void SetTwin(ZDO zdo, ZDOID partner) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) zdo.Set("ecr_twin", partner); } public static ZDOID GetTether(ZDO zdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return zdo.GetZDOID(TetherPartnerKey); } public static void SetTether(ZDO zdo, ZDOID partner) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) zdo.Set("ecr_tether", partner); } public static ZDOID GetPhantomOf(ZDO zdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return zdo.GetZDOID(PhantomOfKey); } public static void SetPhantomOf(ZDO zdo, ZDOID boss) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) zdo.Set("ecr_phantom_of", boss); } public static ZDOID GetEchoOf(ZDO zdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return zdo.GetZDOID(EchoOfKey); } public static void SetEchoOf(ZDO zdo, ZDOID boss) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) zdo.Set("ecr_echo_of", boss); } public static int GetWaves(ZDO zdo) { return zdo.GetInt(SummonWavesHash, 0); } public static void SetWaves(ZDO zdo, int waves) { zdo.Set("ecr_waves", waves); } public static float GetPhantomOrder(ZDO zdo) { return zdo.GetFloat(PhantomOrderHash, 0f); } public static void SetPhantomOrder(ZDO zdo, float order) { zdo.Set("ecr_phantom_order", order); } public static int GetSplits(ZDO zdo) { return zdo.GetInt(PhantomSplitsHash, 0); } public static void SetSplits(ZDO zdo, int splits) { zdo.Set("ecr_phantom_splits", splits); } } public static class BodySize { public const int LargeBars = 5120; private const float LargeRadius = 1f; private const float LargeLength = 3.5f; private const float MinHealth = 300f; private const float Slack = 0.01f; private static readonly Dictionary<int, bool> Verdicts = new Dictionary<int, bool>(); public static bool IsLarge(Character creature) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) int num = PrefabHash(creature); if (Verdicts.TryGetValue(num, out var value)) { return value; } GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(num) : null); if (!((Object)(object)val != (Object)null)) { return Measure(((Component)creature).gameObject, Vector3.one); } return Verdict(num, val); } public static int Barred(Character creature) { if (!IsLarge(creature) && !GrowsLarge(((Component)creature).GetComponent<Growup>())) { return 0; } return 5120; } private static bool GrowsLarge(Growup? young) { if ((Object)(object)young == (Object)null) { return false; } bool flag = IsLargePrefab(young.m_grownPrefab); foreach (GrownEntry item in young.m_altGrownPrefabs ?? new List<GrownEntry>()) { flag |= IsLargePrefab(item.m_prefab); } return flag; } private static bool IsLargePrefab(GameObject? prefab) { if ((Object)(object)prefab != (Object)null) { return Verdict(StringExtensionMethods.GetStableHashCode(((Object)prefab).name), prefab); } return false; } private static bool Verdict(int hash, GameObject prefab) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!Verdicts.TryGetValue(hash, out var value)) { value = Measure(prefab, prefab.transform.localScale); Verdicts[hash] = value; } return value; } private static bool Measure(GameObject body, Vector3 scale) { CapsuleCollider component = body.GetComponent<CapsuleCollider>(); Character component2 = body.GetComponent<Character>(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || component2.m_health < 300f) { return false; } int num = Mathf.Clamp(component.direction, 0, 2); float num2 = Mathf.Abs(((Vector3)(ref scale))[num]); float num3 = Mathf.Max(Mathf.Abs(((Vector3)(ref scale))[(num + 1) % 3]), Mathf.Abs(((Vector3)(ref scale))[(num + 2) % 3])); if (!(component.radius * num3 >= 0.99f)) { return component.height * num2 >= 3.49f; } return true; } private static int PrefabHash(Character creature) { ZNetView nview = creature.m_nview; ZDO val = (((Object)(object)nview != (Object)null && nview.IsValid()) ? nview.GetZDO() : null); if (val == null) { return StringExtensionMethods.GetStableHashCode(Utils.GetPrefabName(((Component)creature).gameObject)); } return val.GetPrefab(); } } public readonly struct BossAspects { public static readonly BossAspects None = new BossAspects(Aspect.None, 0); public readonly Aspect Headline; public readonly int Extras; public BossAspects(Aspect headline, int extras) { Headline = headline; Extras = extras; } public CreatureTraits ToTraits(int stars) { return new CreatureTraits(stars, Headline) { ExtraAspects = Extras }; } public IEnumerable<Aspect> ExtrasInOrder() { Aspect[] inOrder = AspectCatalog.InOrder; foreach (Aspect aspect in inOrder) { if (aspect != Headline && (Extras & (1 << (int)aspect)) != 0) { yield return aspect; } } } } public readonly struct BossDraw { public readonly int Stars; public readonly BossAspects Aspects; public BossDraw(int stars, BossAspects aspects) { Stars = stars; Aspects = aspects; } public CreatureTraits ToTraits() { return Aspects.ToTraits(Stars); } public static BossDraw Roll(string bossPrefab, float starBoost) { return new BossDraw(RollStars(starBoost), AspectRoller.RollBoss(bossPrefab, null)); } public static int RollStars(float starBoost) { BossRules boss = RuleState.Active.Boss; if (!boss.Enabled) { return 0; } return TraitRoller.RollStars(BossView.For(boss), starBoost); } } public sealed class CreatureTraits { public int Stars; public int Mask; public Aspect Aspect; public int ExtraAspects; public bool PhantomCopy; public bool Echo; public bool Decoy; public int Tier; public bool GameStars; public bool Any => Mask != 0; public bool AnyAspect { get { if (Aspect == Aspect.None) { return ExtraAspects != 0; } return true; } } public int LargeGlyphs => Stars / 5; public int Count { get { int num = 0; foreach (Mutation item in Active()) { _ = item; num++; } return num; } } public CreatureTraits(int stars, int mask) { Stars = stars; Mask = mask; } public CreatureTraits(int stars, Aspect aspect) { Stars = stars; Aspect = aspect; } public bool Has(Mutation mutation) { if (!Decoy && !PhantomCopy) { return Wears(mutation); } return false; } public bool Wears(Mutation mutation) { return (Mask & (1 << (int)mutation)) != 0; } public void Add(Mutation mutation) { Mask |= 1 << (int)mutation; } public void Remove(Mutation mutation) { Mask &= ~(1 << (int)mutation); } public bool HasAspect(Aspect aspect) { if (aspect != Aspect.None) { if (Aspect != aspect) { return (ExtraAspects & (1 << (int)aspect)) != 0; } return true; } return false; } public IEnumerable<Aspect> Aspects() { if (Aspect != Aspect.None) { yield return Aspect; } Aspect[] inOrder = AspectCatalog.InOrder; foreach (Aspect aspect in inOrder) { if (aspect != Aspect && (ExtraAspects & (1 << (int)aspect)) != 0) { yield return aspect; } } } public bool OnLargeStar(Mutation mutation) { if (!Wears(mutation)) { return false; } int num = 0; foreach (Mutation item in Active()) { if (item == mutation) { return num < LargeGlyphs; } num++; } return false; } public IEnumerable<Mutation> Active() { Mutation[] inOrder = MutationCatalog.InOrder; foreach (Mutation mutation in inOrder) { if (Wears(mutation)) { yield return mutation; } } } } public static class DeathBrood { public const int Bars = 8; private static readonly Dictionary<string, bool> Verdicts = new Dictionary<string, bool>(StringComparer.Ordinal); public static int BarsOf(string prefab) { if (!Releases(prefab)) { return 0; } return 8; } public static bool Releases(string prefab) { if (Verdicts.TryGetValue(prefab, out var value)) { return value; } GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null); if ((Object)(object)val == (Object)null) { return false; } value = Read(val); Verdicts[prefab] = value; return value; } private static bool Read(GameObject body) { Character component = body.GetComponent<Character>(); CharacterDrop component2 = body.GetComponent<CharacterDrop>(); if (!((Object)(object)component != (Object)null) || !InEffects(component.m_deathEffects, ragdolls: true)) { if ((Object)(object)component2 != (Object)null) { return InDrops(component2.m_drops); } return false; } return true; } private static bool InEffects(EffectList? effects, bool ragdolls) { EffectData[] array = effects?.m_effectPrefabs ?? Array.Empty<EffectData>(); foreach (EffectData val in array) { GameObject val2 = ((val != null && val.m_enabled) ? val.m_prefab : null); if (!((Object)(object)val2 == (Object)null)) { Ragdoll val3 = (ragdolls ? val2.GetComponent<Ragdoll>() : null); if ((Object)(object)val2.GetComponent<Character>() != (Object)null || ((Object)(object)val3 != (Object)null && InEffects(val3.m_removeEffect, ragdolls: false))) { return true; } } } return false; } private static bool InDrops(List<Drop>? drops) { foreach (Drop item in drops ?? new List<Drop>()) { if ((Object)(object)item?.m_prefab != (Object)null && (Object)(object)item.m_prefab.GetComponent<Character>() != (Object)null) { return true; } } return false; } } public enum Mutation { Mad, Bloated, Cloaked, Splintering, Leeching, Warding, Plated, Miasmic, Devouring, Thieving, Gilded, Blinking, Relentless, Juggernaut, Screecher, Frostbound, Mudbound, Corrodent, Cloning, Piercing, Howling, Binding, Flamebound } public static class MutationBars { private static readonly Dictionary<string, int> KindBars = new Dictionary<string, int> { ["Deathsquito"] = 4 }; public static int Of(Character creature) { return BodySize.Barred(creature) | OfKind(Utils.GetPrefabName(((Component)creature).gameObject)); } public static int OfKind(string prefab) { int value; return (KindBars.TryGetValue(prefab, out value) ? value : 0) | DeathBrood.BarsOf(prefab); } } public static class MutationCatalog { public static readonly Mutation[] InOrder = new Mutation[23] { Mutation.Mad, Mutation.Bloated, Mutation.Cloaked, Mutation.Splintering, Mutation.Leeching, Mutation.Warding, Mutation.Plated, Mutation.Miasmic, Mutation.Devouring, Mutation.Thieving, Mutation.Gilded, Mutation.Blinking, Mutation.Relentless, Mutation.Juggernaut, Mutation.Screecher, Mutation.Frostbound, Mutation.Mudbound, Mutation.Corrodent, Mutation.Cloning, Mutation.Piercing, Mutation.Howling, Mutation.Binding, Mutation.Flamebound }; public static bool OnByDefault(Mutation mutation) { if (mutation != Mutation.Piercing && mutation != Mutation.Howling) { return mutation != Mutation.Binding; } return false; } public static Mutation? FromName(string name) { Mutation[] inOrder = InOrder; foreach (Mutation mutation in inOrder) { if (string.Equals(Word(mutation), name, StringComparison.OrdinalIgnoreCase)) { return mutation; } } return null; } public static string Name(Mutation mutation) { return Lang.Get(NameKey(mutation)); } public static string NameKey(Mutation mutation) { return "ecr_mut_" + mutation.ToString().ToLowerInvariant(); } public static string Word(Mutation mutation) { return mutation switch { Mutation.Mad => "Mad", Mutation.Bloated => "Bloated", Mutation.Cloaked => "Cloaked", Mutation.Splintering => "Splintering", Mutation.Leeching => "Leeching", Mutation.Warding => "Warding", Mutation.Plated => "Plated", Mutation.Miasmic => "Miasmic", Mutation.Devouring => "Devouring", Mutation.Thieving => "Thieving", Mutation.Gilded => "Gilded", Mutation.Blinking => "Blinking", Mutation.Relentless => "Relentless", Mutation.Juggernaut => "Juggernaut", Mutation.Screecher => "Screecher", Mutation.Frostbound => "Frostbound", Mutation.Mudbound => "Mudbound", Mutation.Corrodent => "Corrodent", Mutation.Cloning => "Cloning", Mutation.Piercing => "Piercing", Mutation.Howling => "Howling", Mutation.Binding => "Binding", Mutation.Flamebound => "Flamebound", _ => "", }; } public static string DefaultColorHex(Mutation mutation) { return mutation switch { Mutation.Mad => "#E23030", Mutation.Bloated => "#8B5A2B", Mutation.Cloaked => "#4AA6FF", Mutation.Splintering => "#FFFFFF", Mutation.Leeching => "#33CC33", Mutation.Warding => "#101C50", Mutation.Plated => "#F2C40C", Mutation.Miasmic => "#1E6B2E", Mutation.Devouring => "#7A0F0F", Mutation.Thieving => "#A64BE0", Mutation.Gilded => "#FFE066", Mutation.Blinking => "#29E0E0", Mutation.Relentless => "#FF7F24", Mutation.Juggernaut => "#6E6E6E", Mutation.Screecher => "#FF5CC8", Mutation.Frostbound => "#BFEFFF", Mutation.Mudbound => "#5E4A1E", Mutation.Corrodent => "#B7410E", Mutation.Cloning => "#B4A8FF", Mutation.Piercing => "#C2185B", Mutation.Howling => "#2E8B8B", Mutation.Binding => "#6B8E23", Mutation.Flamebound => "#FF4500", _ => "#FFFFFF", }; } } public static class PresetRoller { public static CreatureTraits Roll(BiomeRules rules, RuleSet ruleSet, int tier, Biome biome, int barred) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) PresetCell presetCell = PresetCell.For(ruleSet.Difficulty, tier, PresetTables.Rank(biome)); int num = (ruleSet.CreatureStars ? TraitRoller.PickWeighted(presetCell.Weights) : 0); int mask = (Dice.Percent(Mathf.Min(100f, presetCell.Mutation * (1f + 0.25f * (float)num))) ? PickOne(rules, ruleSet, num, barred) : 0); return new CreatureTraits(num, mask) { Tier = tier }; } private static int PickOne(BiomeRules rules, RuleSet ruleSet, int stars, int barred) { Mutation[] inOrder = MutationCatalog.InOrder; float[] array = new float[inOrder.Length]; float num = 0f; for (int i = 0; i < inOrder.Length; i++) { array[i] = (TraitRoller.MayRoll(inOrder[i], ruleSet, barred) ? Mathf.Max(0f, rules.ChanceOf(inOrder[i], stars)) : 0f); num += array[i]; } if (num <= 0f) { return 0; } return 1 << (int)inOrder[TraitRoller.PickWeighted(array)]; } } public static class RegisteredMutations { private static readonly HashSet<string> Warned = new HashSet<string>(StringComparer.Ordinal); public static int For(Character creature, string prefab) { int num = Registrations.MutationsOf(prefab); if (num != 0) { return Allowed(num, prefab, RuleState.Active, BodySize.Barred(creature), MutationBars.OfKind(prefab)); } return 0; } public static int Allowed(int mask, string prefab, RuleSet rules, int bodyBars, int kindBars) { Mutation[] inOrder = MutationCatalog.InOrder; foreach (Mutation mutation in inOrder) { string text = (((mask & Bit(mutation)) != 0) ? Refusal(mutation, rules, bodyBars, kindBars) : null); if (text != null) { mask &= ~Bit(mutation); WarnOnce(prefab, mutation, text); } } return mask; } public static string? Refusal(Mutation mutation, RuleSet rules, int bodyBars, int kindBars) { if ((kindBars & Bit(mutation)) != 0) { return "its kind never carries it"; } if ((bodyBars & Bit(mutation)) != 0) { return "its body is large: no Gilded or Relentless on large creatures"; } if (!rules.IsEnabled(mutation)) { return "it is switched off in the .cfg (2 - Mutations)"; } return null; } private static int Bit(Mutation mutation) { return 1 << (int)mutation; } private static void WarnOnce(string prefab, Mutation mutation, string why) { string text = MutationCatalog.Word(mutation); if (Warned.Add(prefab + "|" + text)) { Log.Warn(prefab + ": registered mutation '" + text + "' skipped - " + why); } } } public static class Registrations { private sealed class Entry { public int Mutations; public Aspect[]? Aspects; public string[]? PortalAttacks; public SummonPick[]? Summons; public bool Empty { get { if (Mutations == 0 && Aspects == null && PortalAttacks == null) { return Summons == null; } return false; } } } private static readonly Dictionary<string, Entry> ByPrefab = new Dictionary<string, Entry>(StringComparer.Ordinal); public static bool Has(string prefab) { return ByPrefab.ContainsKey(prefab); } public static int MutationsOf(string prefab) { return Find(prefab)?.Mutations ?? 0; } public static Aspect[]? AspectsOf(string prefab) { return Find(prefab)?.Aspects; } public static string[]? PortalAttacksOf(string prefab) { return Find(prefab)?.PortalAttacks; } public static SummonPick[]? SummonsOf(string prefab) { return Find(prefab)?.Summons; } public static void SetMutations(string prefab, int mask) { Change(prefab, delegate(Entry entry) { entry.Mutations = mask; }); } public static void SetAspects(string prefab, Aspect[] aspects) { Change(prefab, delegate(Entry entry) { entry.Aspects = ((aspects.Length != 0) ? aspects : null); }); } public static void SetPortalAttacks(string prefab, string[] attacks) { Change(prefab, delegate(Entry entry) { entry.PortalAttacks = ((attacks.Length != 0) ? attacks : null); }); } public static void SetSummons(string prefab, SummonPick[] summons) { Change(prefab, delegate(Entry entry) { entry.Summons = ((summons.Length != 0) ? summons : null); }); } public static void Clear(string prefab) { ByPrefab.Remove(prefab); } private static Entry? Find(string prefab) { if (prefab == null || !ByPrefab.TryGetValue(prefab, out Entry value)) { return null; } return value; } private static void Change(string prefab, Action<Entry> change) { if (!ByPrefab.TryGetValue(prefab, out Entry value)) { value = new Entry(); } change(value); if (value.Empty) { ByPrefab.Remove(prefab); } else { ByPrefab[prefab] = value; } } } public static class SplinterTable { private static readonly float[][] Weights = new float[5][] { new float[5] { 0f, 0f, 0f, 0f, 100f }, new float[5] { 15f, 0f, 0f, 0f, 85f }, new float[5] { 15f, 35f, 0f, 0f, 50f }, new float[5] { 15f, 35f, 30f, 0f, 20f }, new float[5] { 15f, 35f, 30f, 15f, 5f } }; public static int RollChildStars(int parentStars) { if (parentStars <= 0) { return 0; } float[] array = Weights[Mathf.Clamp(parentStars, 1, 5) - 1]; float num = Dice.Draw(); float num2 = 0f; for (int i = 0; i < 4; i++) { num2 += array[i]; if (num <= num2) { return Mathf.Max(0, parentStars - (i + 1)); } } return 0; } } public static class StarCap { public static int For(RuleSet set, BiomeRules rules, int tier, Biome biome) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (set.Difficulty != Difficulty.Custom) { return PresetCell.For(set.Difficulty, tier, PresetTables.Rank(biome)).Cap; } return rules.StarCeiling; } } public readonly struct SummonPick { public const int OwnStars = -1; public readonly string Prefab; public readonly int Stars; public SummonPick(string prefab, int stars) { Prefab = prefab; Stars = ((stars < 0) ? (-1) : stars); } public int StarsOr(int own) { if (Stars >= 0) { return Stars; } return own; } } public static class TraitKeys { public const string Resolved = "ecr_resolved"; public const string Stars = "ecr_stars"; public const string Mask = "ecr_mask"; public const string Biome = "ecr_biome"; public const string Tier = "ecr_tier"; public const string SireStars = "ecr_sire_stars"; public const string SireMask = "ecr_sire_mask"; public const string EggTraits = "ecr_egg"; public const string DevouredHealth = "ecr_dev_hp"; public const string DevouredDamage = "ecr_dev_dmg"; public const string Generation = "ecr_gen"; public const string CascadeRoot = "ecr_root"; public const string MiasmaTrail = "ecr_miasma"; public const string MiasmaSeq = "ecr_miasma_seq"; public const string DevourReadyAt = "ecr_dev_ready"; public const string LootFilledAt = "ecr_loot_at"; public const string DevouredBy = "ecr_devoured_by"; public const string DevouredMeals = "ecr_dev_meals"; public const string Pouch = "ecr_stolen"; public const string Aspect = "ecr_aspect"; public const string AspectExtra = "ecr_aspect_extra"; public const string AltarExtra = "ecr_altar_extra"; public const string AltarAspect = "ecr_altar_aspect"; public const string AltarShiftAt = "ecr_altar_shift"; public const string AltarStars = "ecr_altar_stars"; public const string AltarPickStars = "ecr_altar_pick_stars"; public const string AltarPickAspect = "ecr_altar_pick_aspect"; public const string AltarPickExtras = "ecr_altar_pick_extras"; public const string TwinPartner = "ecr_twin"; public const string PhantomOf = "ecr_phantom_of"; public const string PhantomSplits = "ecr_phantom_splits"; public const string PhantomOrder = "ecr_phantom_order"; public const string SummonWaves = "ecr_waves"; public const string BossDamage = "ecr_boss_dmg"; public const string Adapted = "ecr_adaptive"; public const string Fixated = "ecr_fixated"; public const string FixatedAt = "ecr_fixated_at"; public const string BlinkedAt = "ecr_blink_at"; public const string StormAt = "ecr_storm_at"; public const string StormCircles = "ecr_storm"; public const string Quarry = "ecr_quarry"; public const string GravityAt = "ecr_gravity_at"; public const string CorpseScale = "ecr_corpse_scale"; public const string CorpseStars = "ecr_corpse_stars"; public const string TetherPartner = "ecr_tether"; public const string PortalAt = "ecr_portal_at"; public const string PortalSpot = "ecr_portal_spot"; public const string ShriekAt = "ecr_shriek_at"; public const string FrostTrail = "ecr_frost_trail"; public const string FrostTrailSeq = "ecr_frost_trail_seq"; public const string MudTrail = "ecr_mud_trail"; public const string MudTrailSeq = "ecr_mud_trail_seq"; public const string FireTrail = "ecr_fire_trail"; public const string FireTrailSeq = "ecr_fire_trail_seq"; public const string BindTrail = "ecr_bind_trail"; public const string BindTrailSeq = "ecr_bind_trail_seq"; public const string HowlAt = "ecr_howl_at"; public const string CloneDecoy = "ecr_clone_decoy"; public const string CloneOf = "ecr_clone_of"; public const string Clones = "ecr_clones"; public const string CloneAt = "ecr_clone_at"; public const string TornadoAt = "ecr_tornado_at"; public const string TornadoWave = "ecr_tornado"; public const string TornadoTrack = "ecr_tornado_track"; public const string EchoOf = "ecr_echo_of"; public const string TameStrip = "ecr_tame_strip"; public const string TameStars = "ecr_tame_stars"; public const string TameMask = "ecr_tame_mask"; public const string GameLevel = "ecr_game_level"; public const string LootLevel = "ecr_loot_level"; } public static class TraitRoller { public static CreatureTraits Roll(BiomeRules rules, RuleSet ruleSet, int tier, Biome biome, int barred) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (ruleSet.Difficulty != Difficulty.Custom) { return PresetRoller.Roll(rules, ruleSet, tier, biome, barred); } int stars = (ruleSet.CreatureStars ? RollStars(rules.StarChances, ruleSet.Tiers.StarBoostAt(tier)) : 0); int mask = RollMutations(rules, stars, ruleSet, ruleSet.Tiers.MutationBoostAt(tier), barred); return new CreatureTraits(stars, mask) { Tier = tier }; } internal static bool MayRoll(Mutation mutation, RuleSet ruleSet, int barred) { if (ruleSet.IsEnabled(mutation)) { return (barred & (1 << (int)mutation)) == 0; } return false; } public static int RollStars(BiomeRules rules) { return RollStars(rules.StarChances, 1f); } public static int RollStars(BiomeRules rules, float boost) { return RollStars(rules.StarChances, boost); } private static int RollStars(float[] chances, float boost) { return PickWeighted(Boosted(chances, boost)); } internal static int PickWeighted(float[] weights) { float num = 0f; foreach (float num2 in weights) { num += num2; } if (num <= 0f) { return 0; } return PickIndex(weights, Random.Range(0f, num)); } private static float[] Boosted(float[] chances, float boost) { float[] array = new float[chances.Length]; float num = 1f; for (int i = 0; i < chances.Length; i++) { array[i] = Mathf.Max(0f, chances[i]) * num; num *= Mathf.Max(0f, boost); } return array; } private static int PickIndex(float[] chances, float draw) { float num = 0f; for (int i = 0; i < chances.Length; i++) { num += Mathf.Max(0f, chances[i]); if (draw < num) { return i; } } return chances.Length - 1; } private static int RollMutations(BiomeRules rules, int stars, RuleSet ruleSet, float boost, int barred) { List<Mutation> list = new List<Mutation>(); Mutation[] inOrder = MutationCatalog.InOrder; foreach (Mutation mutation in inOrder) { float chancePercent = Mathf.Min(100f, rules.ChanceOf(mutation, stars) * boost); if (MayRoll(mutation, ruleSet, barred) && Dice.Percent(chancePercent)) { list.Add(mutation); } } Cap(list, ruleSet.MaxMutations); return Pack(list); } private static void Cap(List<Mutation> hits, int maxMutations) { if (maxMutations > 0 && hits.Count > maxMutations) { for (int num = hits.Count - 1; num > 0; num--) { int num2 = Random.Range(0, num + 1); int index = num; int index2 = num2; Mutation value = hits[num2]; Mutation value2 = hits[num]; hits[index] = value; hits[index2] = value2; } hits.RemoveRange(maxMutations, hits.Count - maxMutations); } } private static int Pack(List<Mutation> hits) { int num = 0; foreach (Mutation hit in hits) { num |= 1 << (int)hit; } return num; } } public static class TraitStore { private static readonly int ResolvedHash = StringExtensionMethods.GetStableHashCode("ecr_resolved"); private static readonly int StarsHash = StringExtensionMethods.GetStableHashCode("ecr_stars"); private static readonly int MaskHash = StringExtensionMethods.GetStableHashCode("ecr_mask"); private static readonly int AspectHash = StringExtensionMethods.GetStableHashCode("ecr_aspect"); private static readonly int AspectExtraHash = StringExtensionMethods.GetStableHashCode("ecr_aspect_extra"); private static readonly int TierHash = StringExtensionMethods.GetStableHashCode("ecr_tier"); private static readonly int SireStarsHash = StringExtensionMethods.GetStableHashCode("ecr_sire_stars"); private static readonly int SireMaskHash = StringExtensionMethods.GetStableHashCode("ecr_sire_mask"); private static readonly int BiomeHash = StringExtensionMethods.GetStableHashCode("ecr_biome"); private static readonly int DevouredHealthHash = StringExtensionMethods.GetStableHashCode("ecr_dev_hp"); private static readonly int DevouredDamageHash = StringExtensionMethods.GetStableHashCode("ecr_dev_dmg"); private static readonly int DevourReadyAtHash = StringExtensionMethods.GetStableHashCode("ecr_dev_ready"); private static readonly KeyValuePair<int, int> PhantomOfKey = ZDO.GetHashZDOID("ecr_phantom_of"); private static readonly KeyValuePair<int, int> CloneOfKey = ZDO.GetHashZDOID("ecr_clone_of"); private static readonly KeyValuePair<int, int> EchoOfKey = ZDO.GetHashZDOID("ecr_echo_of"); private static readonly KeyValuePair<int, int> DevouredByKey = ZDO.GetHashZDOID("ecr_devoured_by"); public static bool IsResolved(ZDO zdo) { if (zdo != null) { return zdo.GetBool(ResolvedHash, false); } return false; } public static CreatureTraits Load(ZDO zdo) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) int stars = zdo.GetInt(StarsHash, 0); int mask = zdo.GetInt(MaskHash, 0); return new CreatureTraits(stars, mask) { Aspect = (Aspect)zdo.GetInt(AspectHash, 0), ExtraAspects = zdo.GetInt(AspectExtraHash, 0), PhantomCopy = (zdo.GetZDOID(PhantomOfKey) != ZDOID.None), Decoy = (zdo.GetZDOID(CloneOfKey) != ZDOID.None), Echo = (zdo.GetZDOID(EchoOfKey) != ZDOID.None), Tier = zdo.GetInt(TierHash, 0) }; } public static void Save(ZDO zdo, CreatureTraits traits) { zdo.Set("ecr_stars", traits.Stars); zdo.Set("ecr_mask", traits.Mask); if (traits.Aspect != Aspect.None) { zdo.Set("ecr_aspect", (int)traits.Aspect); } if (traits.ExtraAspects != 0) { zdo.Set("ecr_aspect_extra", traits.ExtraAspects); } if (traits.Tier > 0) { zdo.Set("ecr_tier", traits.Tier); } zdo.Set("ecr_resolved", true); } public static void SetSire(ZDO zdo, CreatureTraits? sire) { zdo.Set("ecr_sire_stars", (sire != null) ? (sire.Stars + 1) : 0); zdo.Set("ecr_sire_mask", sire?.Mask ?? 0); } public static CreatureTraits? TakeSire(ZDO zdo) { int num = zdo.GetInt(SireStarsHash, 0); if (num <= 0) { return null; } CreatureTraits result = new CreatureTraits(num - 1, zdo.GetInt(SireMaskHash, 0)); SetSire(zdo, null); return result; } public static void SetBiome(ZDO zdo, Biome biome) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected I4, but got Unknown zdo.Set("ecr_biome", (int)biome); } public static Biome GetBiome(ZDO zdo) { return (Biome)zdo.GetInt(BiomeHash, 0); } public static float GetDevouredHealth(ZDO zdo) { return zdo.GetFloat(DevouredHealthHash, 0f); } public static float GetDevouredDamage(ZDO zdo) { return zdo.GetFloat(DevouredDamageHash, 0f); } public static void AddDevoured(ZDO zdo, float health, float damage) { zdo.Set("ecr_dev_hp", GetDevouredHealth(zdo) + health); zdo.Set("ecr_dev_dmg", GetDevouredDamage(zdo) + damage); } public static ZDOID GetDevouredBy(ZDO zdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return zdo.GetZDOID(DevouredByKey); } public static void MarkDevouredBy(ZDO zdo, ZDOID devourer) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) zdo.Set("ecr_devoured_by", devourer); } public static long GetDevourReadyAt(ZDO zdo) { return zdo.GetLong(DevourReadyAtHash, 0L); } public static void SetDevourReadyAt(ZDO zdo, long whenMs) { zdo.Set("ecr_dev_ready", whenMs); } } } namespace EliteCreaturesReborn.Taming { internal static class GameLevel { private static readonly int Hash = StringExtensionMethods.GetStableHashCode("ecr_game_level"); public static int Kept(ZDO zdo) { return zdo.GetInt(Hash, 0); } public static void Park(Character creature, ZDO zdo) { int level = creature.GetLevel(); if (level > 1) { zdo.Set(Hash, level, false); } creature.SetLevel(1); } public static void Follow(Character creature, ZDO zdo, bool gameStars) { if (gameStars) { Restore(creature, zdo); } else if (!RuleState.Active.KeepsLevel(TraitStore.Load(zdo).Stars, isBoss: false)) { Park(creature, zdo); } } private static void Restore(Character creature, ZDO zdo) { int num = zdo.GetInt(Hash, 0); if (num > 0) { zdo.Set(Hash, 0, false); } creature.SetLevel((num > 1) ? num : creature.GetLevel()); } } internal static class LocalReload { public static void Run(ZNetView nview) { ZNetScene instance = ZNetScene.instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)nview == (Object)null) && nview.IsValid()) { ZDO zDO = nview.GetZDO(); nview.ResetZDO(); instance.m_instances.Remove(zDO); Object.Destroy((Object)(object)((Component)nview).gameObject); } } } internal static class TameHold { private static readonly int OwnStarsHash = StringExtensionMethods.GetStableHashCode("ecr_stars"); private static readonly int OwnMaskHash = StringExtensionMethods.GetStableHashCode("ecr_mask"); private static readonly int HeldStarsHash = StringExtensionMethods.GetStableHashCode("ecr_tame_stars"); private static readonly int HeldMaskHash = StringExtensionMethods.GetStableHashCode("ecr_tame_mask"); public static CreatureTraits Rolled(ZDO zdo) { CreatureTraits creatureTraits = TraitStore.Load(zdo); int num = zdo.GetInt(HeldStarsHash, 0); if (num > 0) { creatureTraits.Stars = num; } creatureTraits.Mask |= zdo.GetInt(HeldMaskHash, 0); return creatureTraits; } public static bool Align(ZDO zdo, int strip) { bool num = Move(zdo, OwnStarsHash, HeldStarsHash, (strip & 1) != 0); bool flag = Move(zdo, OwnMaskHash, HeldMaskHash, (strip & 2) != 0); return num || flag; } private static bool Move(ZDO zdo, int own, int held, bool aside) { int num = (aside ? own : held); int num2 = zdo.GetInt(num, 0); if (num2 == 0) { return false; } zdo.Set(aside ? held : own, num2, false); zdo.Set(num, 0, false); return true; } } internal static class TameSettle { public static bool Settle(Character creature, ZDO zdo) { int num = TameStrip.Wanted(creature); int num2 = TameStrip.Of(zdo); bool result = (num | num2) != 0 && TameHold.Align(zdo, num); if (num == num2) { return result; } TameStrip.Set(zdo, num); if ((num & ~num2 & 2) != 0) { GiveBack(creature, zdo); } if (((num ^ num2) & 1) != 0) { GameLevel.Follow(creature, zdo, (num & 1) != 0); } return true; } private static void GiveBack(Character creature, ZDO zdo) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) List<PouchStore.Entry> list = PouchStore.Load(zdo); if (list.Count > 0) { PouchDrop.DropAll(list, ((Component)creature).transform.position); PouchStore.Save(zdo, new List<PouchStore.Entry>()); } } } internal static class TameStrip { public const int Stars = 1; public const int Mutations = 2; private static readonly int Hash = StringExtensionMethods.GetStableHashCode("ecr_tame_strip"); public static int Wanted(Character creature) { if (!creature.IsTamed() || creature.IsBoss()) { return 0; } return ((!TamedSettings.StarsKept) ? 1 : 0) | ((!TamedSettings.MutationsKept) ? 2 : 0); } public static int Of(ZDO zdo) { return zdo.GetInt(Hash, 0); } public static void Set(ZDO zdo, int strip) { zdo.Set(Hash, strip, false); } public static CreatureTraits View(CreatureTraits stored, int strip) { if ((strip & 1) != 0) { stored.Stars = 0; stored.GameStars = true; } if ((strip & 2) != 0) { stored.Mask = 0; } return stored; } } internal sealed class TameTicker : MonoBehaviour { private const float Seconds = 1f; private float _next; private void Update() { float time = Time.time; if (!(time < _next)) { _next = time + 1f; Guard.Run("tame watch", TameWatch.Tick); } } } internal static class TameWatch { private static readonly Dictionary<EliteController, int> Applied = new Dictionary<EliteController, int>(); private static readonly List<EliteController> Scratch = new List<EliteController>(); public static void Track(EliteController controller) { if (!controller.Creature.IsBoss()) { Applied[controller] = TameStrip.Of(controller.View.GetZDO()); } } public static void Forget(EliteController controller) { Applied.Remove(controller); } public static void Tick() { Scratch.Clear(); Scratch.AddRange(Applied.Keys); foreach (EliteController item in Scratch) { Check(item); } } private static void Check(EliteController controller) { ZNetView val = (((Object)(object)controller != (Object)null) ? controller.View : null); ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null); if (val2 != null && Applied.TryGetValue(controller, out var value)) { if (val.IsOwner() && ServerLock.RulesArrived) { TameSettle.Settle(controller.Creature, val2); } int num = TameStrip.Of(val2); if (num != value) { CatchUp(controller, val2, num); } } } private static void CatchUp(EliteController controller, ZDO zdo, int strip) { if (!Differs(controller, zdo, strip)) { Applied[controller] = strip; } else if (!Ridden(controller.Creature)) { Applied.Remove(controller); LocalReload.Run(controller.View); } } private static bool Differs(EliteController controller, ZDO zdo, int strip) { CreatureTraits creatureTraits = TameStrip.View(TraitStore.Load(zdo), strip); CreatureTraits traits = controller.Traits; if (creatureTraits.Stars == traits.Stars && creatureTraits.Mask == traits.Mask && creatureTraits.GameStars == traits.GameStars) { return zdo.GetInt(ZDOVars.s_level, 1) != controller.Creature.GetLevel(); } return true; } private static bool Ridden(Character creature) { Tameable component = ((Component)creature).GetComponent<Tameable>(); if ((Object)(object)component != (Object)null && (Object)(object)component.m_saddle != (Object)null) { return component.m_saddle.HaveValidUser(); } return false; } } } namespace EliteCreaturesReborn.Tally { internal sealed class BossBoard { public string Fight = ""; public string BossName = ""; public List<DamageTally.Entry> Entries = new List<DamageTally.Entry>(); public static BossBoard? Latest { get; private set; } public static bool Remember(BossBoard board) { if (Latest != null && Latest.Fight == board.Fight) { return false; } Latest = board; return true; } public static void Forget() { Latest = null; } public void Write(ZPackage pkg) { pkg.Write(Fight); pkg.Write(BossName); DamageTally.Write(pkg, Entries); } public static BossBoard Read(ZPackage pkg) { BossBoard bossBoard = new BossBoard { Fight = pkg.ReadString(), BossName = pkg.ReadString() }; DamageTally.Read(pkg, bossBoard.Entries); return bossBoard; } } internal static class BossBoardRecall { public const string Ask = "ecr_boss_board_ask"; public const string Answer = "ecr_boss_board_answer"; private static ZRoutedRpc? _registeredOn; private static Action<BossBoard?>? _pending; public static void EnsureRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && instance != _registeredOn) { _registeredOn = instance; _pending = null; instance.Register("ecr_boss_board_ask", (Action<long>)OnAsk); instance.Register<ZPackage>("ecr_boss_board_answer", (Action<long, ZPackage>)OnAnswer); } } public static bool Request(Action<BossBoard?> done) { if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || ZRoutedRpc.instance == null) { return false; } EnsureRegistered(); _pending = done; ZRoutedRpc.instance.InvokeRoutedRPC("ecr_boss_board_ask", Array.Empty<object>()); return true; } private static void OnAsk(long sender) { Guard.Run("BossBoardRecall.OnAsk", delegate { Reply(sender); }); } private static void Reply(long sender) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { BossBoard latest = BossBoard.Latest; ZPackage val = new ZPackage(); val.Write(latest != null); latest?.Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "ecr_boss_board_answer", new object[1] { val }); } } private static void OnAnswer(long sender, ZPackage pkg) { Guard.Run("BossBoardRecall.OnAnswer", delegate { Receive(pkg); }); } private static void Receive(ZPackage pkg) { BossBoard bossBoard = (pkg.ReadBool() ? BossBoard.Read(pkg) : null); if (bossBoard != null && BossBoard.Latest == null) { BossBoard.Remember(bossBoard); } Action<BossBoard?>? pending = _pending; _pending = null; pending?.Invoke(bossBoard); } } internal static class BossBoardRpc { public const string Rpc = "ecr_boss_board"; private static ZRoutedRpc? _registeredOn; public static void EnsureRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && instance != _registeredOn) { _registeredOn = instance; BossBoard.Forget(); instance.Register<ZPackage>("ecr_boss_board", (Action<long, ZPackage>)OnBoard); } } public static void Announce(Character boss, ZDO zdo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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) if (AspectStore.GetPhantomOf(zdo) != ZDOID.None || ZRoutedRpc.instance == null) { return; } ZDOID tether = AspectStore.GetTether(zdo); if (TetherPair.Standing(tether)) { BossCredit.HandOver(zdo, tether); return; } List<DamageTally.Entry> list = DamageTally.Load(zdo); ZDOID twin = ((tether != ZDOID.None) ? tether : AspectStore.GetTwin(zdo)); AddPartner(list, twin); if (list.Count > 0) { Send(new BossBoard { Fight = FightOf(zdo.m_uid, twin), BossName = boss.m_name, Entries = list }); } } private static void AddPartner(List<DamageTally.Entry> entries, ZDOID twin) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: 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) ZDO val = ((twin != ZDOID.None && ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(twin) : null); if (val == null) { return; } foreach (DamageTally.Entry item in DamageTally.Load(val)) { DamageTally.Merge(entries, item.Id, item.Name, item.Damage); } } private unsafe static string FightOf(ZDOID self, ZDOID twin) { //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) string text = ((object)(*(ZDOID*)(&self))/*cast due to .constrained prefix*/).ToString(); string text2 = ((twin == ZDOID.None) ? text : ((object)(*(ZDOID*)(&twin))/*cast due to .constrained prefix*/).ToString()); if (string.CompareOrdinal(text, text2) > 0) { return text2; } return text; } private static void Send(BossBoard board) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); board.Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ecr_boss_board", new object[1] { val }); } private static void OnBoard(long sender, ZPackage pkg) { Guard.Run("BossBoardRpc.OnBoard", delegate { Read(pkg); }); } private static void Read(ZPackage pkg) { BossBoard board = BossBoard.Read(pkg); if (BossBoard.Remember(board)) { BossBoardView.Show(board); } } } internal sealed class BossBoardView : MonoBehaviour { private const float Margin = 48f; private static BossBoardView? _instance; private TMP_Text _text; private float _hideAt; public static void Show(BossBoard board) { if (Configuration.ShowBossBoard.Value) { Replay(board); } } public static bool Replay(BossBoard board) { BossBoardView bossBoardView = Ensure(); if ((Object)(object)bossBoardView == (Object)null) { return false; } bossBoardView._text.text = Compose(board); ((Graphic)bossBoardView._text).CrossFadeAlpha(1f, 0f, true); bossBoardView._hideAt = Time.time + Mathf.Max(1f, Configuration.BossBoardSeconds.Value); ((Component)bossBoardView).gameObject.SetActive(true); return true; } private void Update() { if (Time.time >= _hideAt) { ((Component)this).gameObject.SetActive(false); } } private static BossBoardView? Ensure() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } MessageHud instance = MessageHud.instance; TMP_Text val = (((Object)(object)instance != (Object)null) ? instance.m_messageCenterText : null); if ((Object)(object)val == (Object)null || (Object)(object)((Graphic)val).canvas == (Object)null) { return null; } GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)((Graphic)val).canvas).transform); ((Object)val2).name = "ecr_boss_board"; _instance = val2.AddComponent<BossBoardView>(); _instance._text = val2.GetComponent<TMP_Text>(); Place((RectTransform)val2.transform, _instance._text); return _instance; } private static void Place(RectTransform rect, TMP_Text text) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = new Vector2(0f, 0.5f); rect.anchorMax = new Vector2(0f, 0.5f); rect.pivot = new Vector2(0f, 0.5f); rect.anchoredPosition = new Vector2(48f, 0f); rect.sizeDelta = new Vector2(520f, 600f); text.alignment = (TextAlignmentOptions)513; text.enableAutoSizing = false; text.fontSize = 24f; text.richText = true; } private static string Compose(BossBoard board) { string text = ((Localization.instance != null) ? Localization.instance.Localize(board.BossName) : board.BossName); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<b>").Append(Lang.Format("ecr_tally_defeated", text)).Append("</b>"); foreach (DamageTally.Entry item in board.Entries.OrderByDescending((DamageTally.Entry e) => e.Damage)) { stringBuilder.Append("\n<noparse>").Append(item.Name).Append("</noparse> ") .Append(Mathf.RoundToInt(item.Damage).ToString("N0")); } return stringBuilder.ToString(); } } internal static class BossCredit { public const string Rpc = "ecr_boss_credit"; private static ZRoutedRpc? _registeredOn; public static void EnsureRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && instance != _registeredOn) { _registeredOn = instance; instance.Register<ZPackage>("ecr_boss_credit", (Action<long, ZPackage>)OnCredit); } } public static void Add(ZDO hit, Player player, float amount) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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) ZDOID phantomOf = AspectStore.GetPhantomOf(hit); if (phantomOf == ZDOID.None) { DamageTally.Add(hit, player.GetPlayerID(), player.GetPlayerName(), amount); } else { Route(phantomOf, player.GetPlayerID(), player.GetPlayerName(), amount); } } public static void HandOver(ZDO fallen, ZDOID partner) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) foreach (DamageTally.Entry item in DamageTally.Load(fallen)) { Route(partner, item.Id, item.Name, item.Damage); } DamageTally.Clear(fallen); } private static void Route(ZDOID boss, long playerId, string playerName, float amount) { //IL_000c: 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_0052: Unknown result type (might be due to invalid IL or missing references) ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(boss) : null); if (val != null && val.IsOwner()) { DamageTally.Add(val, playerId, playerName, amount); return; } ZRoutedRpc instance2 = ZRoutedRpc.instance; long num = ((val != null) ? val.GetOwner() : 0); if (instance2 != null) { if (num != 0L) { instance2.InvokeRoutedRPC(num, "ecr_boss_credit", new object[1] { Pack(boss, playerId, playerName, amount) }); } else { instance2.InvokeRoutedRPC("ecr_boss_credit", new object[1] { Pack(boss, playerId, playerName, amount) }); } } } private static ZPackage Pack(ZDOID boss, long playerId, string playerName, float amount) { //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_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(boss); val.Write(playerId); val.Write(playerName); val.Write(amount); return val; } private static void OnCredit(long sender, ZPackage pkg) { Guard.Run("BossCredit.OnCredit", delegate { Receive(pkg); }); } private static void Receive(ZPackage pkg) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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_008c: Unknown result type (might be due to invalid IL or missing references) ZDOID val = pkg.ReadZDOID(); long playerId = pkg.ReadLong(); string playerName = pkg.ReadString(); float amount = pkg.ReadSingle(); ZDOMan instance = ZDOMan.instance; ZDO val2 = ((instance != null) ? instance.GetZDO(val) : null); if (val2 == null) { return; } if (val2.IsOwner()) { DamageTally.Add(val2, playerId, playerName, amount); return; } long owner = val2.GetOwner(); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && owner != 0L && owner != ZDOMan.GetSessionID()) { ZRoutedRpc.instance.InvokeRoutedRPC(owner, "ecr_boss_credit", new object[1] { Pack(val, playerId, playerName, amount) }); } } } internal static class DamageTally { public sealed class Entry { public long Id; public string Name = ""; public float Damage; } private sealed class Open { public ZDO Zdo; public List<Entry> Entries; } private const float FlushInterval = 1f; private static readonly int BossDamageHash = StringExtensionMethods.GetStableHashCode("ecr_boss_dmg"); private static readonly Dictionary<ZDOID, Open> Opened = new Dictionary<ZDOID, Open>(); private static readonly List<ZDOID> Closing = new List<ZDOID>(); private static float _nextFlush; public static List<Entry> Load(ZDO zdo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Flush(zdo.m_uid); List<Entry> list = new List<Entry>(); byte[] byteArray = zdo.GetByteArray(BossDamageHash, (byte[])null); if (byteArray != null && byteArray.Length != 0) { Read(new ZPackage(byteArray), list); } return list; } public static void Add(ZDO zdo, long playerId, string playerName, float amount) { //IL_0006: 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) if (!Opened.TryGetValue(zdo.m_uid, out Open value) || value.Zdo != zdo) { value = new Open { Zdo = zdo, Entries = Load(zdo) }; Opened[zdo.m_uid] = value; } Merge(value.Entries, playerId, playerName, amount); } public static void Clear(ZDO zdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) Opened.Remove(zdo.m_uid); zdo.Set(BossDamageHash, new byte[0]); } public static void FlushDue(float now) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (Opened.Count == 0 || now < _nextFlush) { return; } _nextFlush = now + 1f; Closing.Clear(); Closing.AddRange(Opened.Keys); foreach (ZDOID item in Closing) { Flush(item); } } private static void Flush(ZDOID id) { //IL_0005: 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) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown if (Opened.TryGetValue(id, out Open value)) { Opened.Remove(id); if (((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(id) : null) == value.Zdo && value.Zdo.m_uid == id && value.Zdo.IsOwner()) { ZPackage val = new ZPackage(); Write(val, value.Entries); value.Zdo.Set(BossDamageHash, val.GetArray()); } } } public static void Merge(List<Entry> entries, long id, string name, float amount) { foreach (Entry entry in entries) { if (entry.Id == id) { entry.Name = name; entry.Damage += amount; return; } } entries.Add(new Entry { Id = id, Name = name, Damage = amount }); } public static void Write(ZPackage pkg, List<Entry> entries) { pkg.Write(entries.Count); foreach (Entry entry in entries) { pkg.Write(entry.Id); pkg.Write(entry.Name); pkg.Write(entry.Damage); } } public static void Read(ZPackage pkg, List<Entry> into) { int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { into.Add(new Entry { Id = pkg.ReadLong(), Name = pkg.ReadString(), Damage = pkg.ReadSingle() }); } } } internal sealed class TallyFlush : MonoBehaviour { private void Update() { Guard.Run("DamageTally.Flush", delegate(float now) { DamageTally.FlushDue(now); }, Time.time); } } } namespace EliteCreaturesReborn.Scaling { public static class AspectDamage { public static void Outgoing(EliteController attacker, Character victim, HitData hit) { attacker = EchoLink.Source(attacker); CreatureTraits traits = attacker.Traits; float num = AspectMath.DamageFactor(traits); if (!Mathf.Approximately(num, 1f)) { hit.ApplyModifier(num); } if (traits.HasAspect(Aspect.Enraged)) { Physical(hit, AspectMath.Boost(AspectMath.Power(Aspect.Enraged, "physical bonus"))); } if (traits.HasAspect(Aspect.Elementalist)) { Elemental(hit, AspectMath.Boost(AspectMath.Power(Aspect.Elementalist, "elemental bonus"))); } if (traits.HasAspect(Aspect.Fixated)) { FixatedDamage.Apply(attacker, victim, hit); } } public static void Incoming(EliteController victim, HitData hit) { if (victim.Traits.HasAspect(Aspect.Shielded) && IsArrow(hit)) { hit.ApplyModifier(AspectMath.Cut(AspectMath.Power(Aspect.Shielded, "arrow reduction"))); } if (victim.Traits.HasAspect(Aspect.Adaptive)) { AdaptiveResist.Apply(victim, hit); } if (victim.Traits.HasAspect(Aspect.Tethered)) { TetherLink.Brace(victim, hit); } } private static bool IsArrow(HitData hit) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 if ((int)hit.m_skill != 8) { return (int)hit.m_skill == 14; } return true; } private static void Physical(HitData hit, float factor) { hit.m_damage.m_blunt *= factor; hit.m_damage.m_slash *= factor; hit.m_damage.m_pierce *= factor; } private static void Elemental(HitData hit, float factor) { hit.m_damage.m_fire *= factor; hit.m_damage.m_frost *= factor; hit.m_damage.m_lightning *= factor; hit.m_damage.m_poison *= factor; hit.m_damage.m_spirit *= factor; } } public static class AspectMath { private const float Floor = 0.05f; public static float Power(Aspect aspect, string field) { return RuleState.Active.Boss.Aspects.PowerOf(aspect, field); } public static float HealthFactor(CreatureTraits traits) { float num = 1f; if (traits.HasAspect(Aspect.Twin)) { num *= Cut(Power(Aspect.Twin, "less health")); } if (traits.HasAspect(Aspect.Tethered)) { num *= Cut(Power(Aspect.Tethered, "less health")); } if (traits.HasAspect(Aspect.Colossal)) { num *= Boost(Power(Aspect.Colossal, "more health")); } return num; } public static float SizeFactor(CreatureTraits traits) { if (!traits.HasAspect(Aspect.Colossal)) { return 1f; } return Boost(Power(Aspect.Colossal, "bigger")); } public static float SpeedFactor(CreatureTraits traits) { if (!traits.HasAspect(Aspect.Colossal)) { return 1f; } return Cut(Power(Aspect.Colossal, "slower")); } public static float PhantomHealth(AspectRules rules) { return Mathf.Max(1f, rules.PowerOf(Aspect.Phantom, "health per tier") * (float)Mathf.Max(1, WorldTier.Current())); } public static float PhantomHealth() { return PhantomHealth(RuleState.Active.Boss.Aspects); } public static float[] PhantomSplits() { return RuleState.Active.Boss.Aspects.ListOf(Aspect.Phantom, "split at"); } public static float DamageFactor(CreatureTraits traits) { if (traits.PhantomCopy) { return Cut(Power(Aspect.Phantom, "less damage")); } float num = (traits.HasAspect(Aspect.Twin) ? Cut(Power(Aspect.Twin, "less damage")) : 1f); if (!traits.HasAspect(Aspect.Tethered)) { return num; } return num * Cut(Power(Aspect.Tethered, "less damage")); } public static float Cut(float percentLess) { return Mathf.Max(0.05f, 1f - percentLess / 100f); } public static float Boost(float percentMore) { return Mathf.Max(0f, 1f + percentMore / 100f); } } public static class DamageMath { public static float OutgoingMultiplier(BiomeRules rules, CreatureTraits traits, float healthFraction) { float num = rules.Star.AttackAt(traits.Stars) - 1f; if (traits.Has(Mutation.Splintering)) { num += rules.PowerOf(Mutation.Splintering, "damage") - 1f; } if (traits.Has(Mutation.Plated)) { num += Enhance.Magnitude(rules, traits, Mutation.Plated, "damage") / 100f * (1f - healthFraction); } return Mathf.Max(0f, 1f + num); } public static float DevouredFlatDamage(CreatureTraits traits, ZDO zdo) { if (!traits.Has(Mutation.Devouring) || zdo == null) { return 0f; } return TraitStore.GetDevouredDamage(zdo); } public static float PlatedPercent(BiomeRules rules, CreatureTraits traits) { if (!traits.Has(Mutation.Plated)) { return 0f; } return Mathf.Min(Enhance.Magnitude(rules, traits, Mutation.Plated, "armour"), rules.PowerOf(Mutation.Plated, "max reduction")); } public static float PlatedReduction(BiomeRules rules, CreatureTraits traits, float healthFraction) { return PlatedPercent(rules, traits) / 100f * Mathf.Clamp01(healthFraction); } } public static class Enhance { public static float Stat(BiomeRules rules, CreatureTraits traits, Mutation mutation, string field) { float num = rules.PowerOf(mutation, field); if (!traits.OnLargeStar(mutation)) { return num; } return 1f + (num - 1f) * rules.LargeStarPower; } public static float Magnitude(BiomeRules rules, CreatureTraits traits, Mutation mutation, string field) { float num = rules.PowerOf(mutation, field); if (!traits.OnLargeStar(mutation)) { return num; } return num * rules.LargeStarPower; } } public static class StatApplier { public static void ApplySize(Character character, BiomeRules rules, CreatureTraits traits) { //IL_0023: 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) float num = StatMath.SizeMultiplier(rules, traits) * AspectMath.SizeFactor(traits); if (!Mathf.Approximately(num, 1f)) { Transform transform = ((Component)character).transform; transform.localScale *= num; } } public static void ApplyHealth(Character character, BiomeRules rules, CreatureTraits traits, bool freshlyResolved) { ZDO zDO = ((Component)character).GetComponent<ZNetView>().GetZDO(); float num = ((zDO != null && traits.Wears(Mutation.Devouring)) ? TraitStore.GetDevouredHealth(zDO) : 0f); float num2 = LevelledBase(character, traits); float num3 = (traits.PhantomCopy ? AspectMath.PhantomHealth() : (num2 * StatMath.HealthMultiplier(rules, traits) * AspectMath.HealthFactor(traits) * HordeFactors.Health(character, zDO) + num)); if (!RuleState.Active.KeepsLevel(traits, character.IsBoss()) || !Mathf.Approximately(num3, num2)) { character.SetMaxHealth(num3); } if (zDO != null && (freshlyResolved || PinnedAt(zDO, num3))) { zDO.RemoveFloat(ZDOVars.s_health); } } private static float LevelledBase(Character character, CreatureTraits traits) { return character.GetMaxHealthBase() * (float)((traits.Stars != 0) ? 1 : Mathf.Max(1, character.GetLevel())); } private static bool PinnedAt(ZDO zdo, float max) { return Mathf.Approximately(zdo.GetFloat(ZDOVars.s_health, -1f), max); } } public static class StatMath { public const float HealthFloor = 0.05f; public const float MoveFloor = 0.05f; public static float HealthMultiplier(BiomeRules rules, CreatureTraits traits) { float num = rules.Star.HpAt(traits.Stars) - 1f; if (traits.Wears(Mutation.Mad)) { num += rules.PowerOf(Mutation.Mad, "health") - 1f; } if (traits.Wears(Mutation.Bloated)) { num += Enhance.Stat(rules, traits, Mutation.Bloated, "health") - 1f; } if (traits.Wears(Mutation.Blinking)) { num += rules.PowerOf(Mutation.Blinking, "health") - 1f; } return Mathf.Max(0.05f, 1f + num); } public static float SizeMultiplier(BiomeRules rules, CreatureTraits traits) { return 1f + rules.Star.GrowthAt(traits.Stars); } public static float MoveMultiplier(BiomeRules rules, CreatureTraits traits, float devourBonus) { float num = rules.Star.SpeedAt(traits.Stars) - 1f + devourBonus; if (traits.Wears(Mutation.Mad)) { num += Enhance.Stat(rules, traits, Mutation.Mad, "move") - 1f; } if (traits.Wears(Mutation.Devouring)) { num += rules.PowerOf(Mutation.Devouring, "move") - 1f; } float num2 = Mathf.Max(0.05f, 1f + num) * AspectMath.SpeedFactor(traits); if (!traits.Wears(Mutation.Relentless)) { return num2; } return Mathf.Min(1f, num2); } public static float SwingSpeedMultiplier(BiomeRules rules, CreatureTraits traits) { float num = rules.Star.SwingSpeedAt(traits.Stars) - 1f; if (traits.Wears(Mutation.Mad)) { num += Enhance.Stat(rules, traits, Mutation.Mad,