using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("CompanionKit.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.4.20.0")] [assembly: AssemblyInformationalVersion("0.4.20+091b206910305beb491301afe1db01b8cd7b8e72")] [assembly: AssemblyProduct("CompanionKit.Core")] [assembly: AssemblyTitle("CompanionKit.Core")] [assembly: AssemblyMetadata("BuildStamp", "091b2069 2026-08-28")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [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 CompanionKit.Core { public static class AgentFit { public const float MaxBaseOffset = 3f; public static float BaseOffset(float pivotY, float boundsMinY) { float num = pivotY - boundsMinY; if (float.IsNaN(num) || num < 0f) { return 0f; } if (!(num > 3f)) { return num; } return 3f; } } public enum AnchorGlueMode { Off, Combat, Always } public enum AnchorGlueAction { None, Write, SafeTeleport, AgentWarp } public static class AnchorGlue { public const float SafeTeleportGap = 2f; public static bool Engaged(AnchorGlueMode mode, bool hasBody, bool inCombat) { if (!hasBody) { return false; } return mode switch { AnchorGlueMode.Always => true, AnchorGlueMode.Combat => inCombat, _ => false, }; } public static bool LeashApplies(AnchorGlueMode mode, bool hasBody, bool inCombat, bool externallyDriven) { if (!externallyDriven) { return !Engaged(mode, hasBody, inCombat); } return false; } public static AnchorGlueAction Decide(AnchorGlueMode mode, bool hasBody, bool inCombat, bool anchorAlive, bool isMasterClient, bool positionsSane, bool agentDrivesTransform, float separation) { if (!Engaged(mode, hasBody, inCombat)) { return AnchorGlueAction.None; } if (!anchorAlive || !isMasterClient || !positionsSane) { return AnchorGlueAction.None; } if (agentDrivesTransform) { return AnchorGlueAction.AgentWarp; } if (!(separation > 2f)) { return AnchorGlueAction.Write; } return AnchorGlueAction.SafeTeleport; } public static void WeldPosition(float puppetX, float puppetY, float puppetZ, float facingX, float facingZ, float offsetBehind, out float x, out float y, out float z) { y = puppetY; double num = Math.Sqrt((double)facingX * (double)facingX + (double)facingZ * (double)facingZ); if (num < 1E-06 || offsetBehind == 0f) { x = puppetX; z = puppetZ; } else { x = puppetX - (float)((double)facingX / num * (double)offsetBehind); z = puppetZ - (float)((double)facingZ / num * (double)offsetBehind); } } public static bool ShouldAssertLock(bool unifyEnabled, bool petHasTarget, bool anchorAlive, bool anchorLockIsSameTarget) { if (unifyEnabled && petHasTarget && anchorAlive) { return !anchorLockIsSameTarget; } return false; } } public enum AnchorCollisionMode { Block, PassPlayer, Phantom } public enum AnchorPhysAction { None, Stamp, Unstamp, Restamp } public static class AnchorPhysicsPolicy { public static AnchorPhysAction Decide(AnchorCollisionMode mode, bool weStampedBefore, bool ignoredNow, bool collidersReady) { if (!collidersReady) { return AnchorPhysAction.None; } switch (mode) { case AnchorCollisionMode.Block: if (!(weStampedBefore && ignoredNow)) { return AnchorPhysAction.None; } return AnchorPhysAction.Unstamp; case AnchorCollisionMode.Phantom: return AnchorPhysAction.None; default: if (ignoredNow) { return AnchorPhysAction.None; } if (!weStampedBefore) { return AnchorPhysAction.Stamp; } return AnchorPhysAction.Restamp; } } } public struct AnchorReplicaPlan { public bool LocalOwner; public bool NeutralizeLifetime; public bool RetuneNetConvergence; public bool HideRenderers; public bool MuteAudio; public bool NeuterWeapon; public bool KillHealthBar; public bool ExemptPlayerCollision; public string LogScope; } public static class AnchorReplicaPolicy { public const string ForeignScope = "foreign anchor"; public const string OwnScope = "own-pet anchor replica"; public static AnchorReplicaPlan For(bool localOwner) { return new AnchorReplicaPlan { LocalOwner = localOwner, NeutralizeLifetime = true, RetuneNetConvergence = true, HideRenderers = true, MuteAudio = true, NeuterWeapon = true, KillHealthBar = true, ExemptPlayerCollision = true, LogScope = (localOwner ? "own-pet anchor replica" : "foreign anchor") }; } } public static class AnchorVitals { public static float RestoreHealth(double frac, double max) { if (double.IsNaN(frac) || double.IsInfinity(frac)) { frac = 1.0; } if (!(max > 0.0) || double.IsInfinity(max)) { return 0f; } if (frac >= 1.0) { return (float)max; } double num = Math.Round(((frac < 0.0) ? 0.0 : frac) * max); if (num < 1.0) { num = 1.0; } if (num > max) { num = max; } return (float)num; } } public static class AnimParamLatch { public static bool IsAnswer(int parameterCount) { return parameterCount > 0; } public static bool ShouldProbe(bool latched, int parameterCount) { if (!latched) { return IsAnswer(parameterCount); } return false; } public static bool Latch(bool latched, int parameterCount) { if (!latched) { return IsAnswer(parameterCount); } return true; } public static bool ShouldLatchCensus(bool found, bool allAnswered) { return found || allAnswered; } } public enum AuraCaptureState { NotNeeded, Pending, InFlight, Captured, Failed } public static class AuraBook { public const int SlotBase = 100; public const string KeyPrefix = "aura."; public static bool IsAuraKey(string key) { if (!string.IsNullOrEmpty(key) && key.StartsWith("aura.", StringComparison.OrdinalIgnoreCase)) { return key.Length > "aura.".Length; } return false; } public static string ValidateRegistration(string key, int slot, IEnumerable existingKeys, IEnumerable existingSlots) { if (!IsAuraKey(key)) { return "aura key '" + (key ?? "") + "' is not legal — keys must be 'aura.'."; } if (slot < 100) { return $"aura '{key}' slot {slot} is below the reserved aura band (must be >= {100} — " + "slots below it belong to consumer spell tables)."; } if (existingKeys != null) { foreach (string existingKey in existingKeys) { if (string.Equals(existingKey, key, StringComparison.OrdinalIgnoreCase)) { return "aura key '" + key + "' is already registered."; } } } if (existingSlots != null) { foreach (int existingSlot in existingSlots) { if (existingSlot == slot) { return $"aura '{key}' slot {slot} is already taken by another aura."; } } } return null; } public static List FilterCandidates(string filter) { List list = new List(); if (string.IsNullOrEmpty(filter)) { return list; } string[] array = filter.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { list.Add(text); } } return list; } public static bool MatchesFilter(string nodeName, IReadOnlyList candidates) { if (string.IsNullOrEmpty(nodeName) || candidates == null) { return false; } for (int i = 0; i < candidates.Count; i++) { if (nodeName.IndexOf(candidates[i], StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } public static int SelectCaptureDepth(IReadOnlyList leafToRootNames, IReadOnlyList candidates) { if (leafToRootNames == null) { return -1; } for (int i = 0; i < leafToRootNames.Count; i++) { if (MatchesFilter(leafToRootNames[i], candidates)) { return i; } } return -1; } public static string CaptureKey(string speciesKey, string filter) { return (speciesKey ?? "").Trim().ToLowerInvariant() + "|" + (filter ?? "").Trim().ToLowerInvariant(); } } public static class BodyFxDescribe { public const string NoRenderer = "NONE"; private static string Or(string s, string fallback) { if (!string.IsNullOrEmpty(s)) { return s; } return fallback; } public static string Played(string spellKey, string verb, string target, string source) { return "played '" + Or(spellKey, "?") + "' via " + Or(verb, "?") + " on " + Or(target, "?") + " src=" + Or(source, "?"); } public static string Binding(string componentType, string node, string rendererName) { return Or(componentType, "?") + "@" + Or(node, "?") + " -> renderer=" + Or(rendererName, "NONE"); } public static string Bindings(IEnumerable fragments) { if (fragments == null) { return ""; } StringBuilder stringBuilder = new StringBuilder(); foreach (string fragment in fragments) { if (!string.IsNullOrEmpty(fragment)) { if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } stringBuilder.Append(fragment); } } return stringBuilder.ToString(); } } public static class BodyFxMath { public const float MinSeconds = 0.5f; public const float MaxSeconds = 10f; public const float EncaseAuthoredHeight = 2f; public const float EncaseMinFactor = 0.3f; public const float EncaseMaxFactor = 4f; public const float TopYFloor = 0.2f; public const float TopYDefault = 1.5f; public static float ClampSeconds(float seconds) { if (!(seconds > 0.5f)) { return 0.5f; } if (!(seconds > 10f)) { return seconds; } return 10f; } public static float EncaseScale(float sizeX, float sizeY, float sizeZ, float userScale) { float num = Math.Max(SaneDim(sizeX), Math.Max(SaneDim(sizeY), SaneDim(sizeZ))); float num2 = ((userScale > 0f) ? userScale : 1f); float num3 = num / 2f * num2; if (!(num3 > 0.3f)) { return 0.3f; } if (!(num3 > 4f)) { return num3; } return 4f; } public static bool FlattenForward(float fx, float fz, out float nx, out float nz) { if (!float.IsNaN(fx) && !float.IsNaN(fz) && EffigyPinMath.HorizontalFacing(fx, fz, out nx, out nz)) { return true; } nx = 0f; nz = 1f; return false; } public static float TopY(float boundsMaxY, float bodyY) { float num = boundsMaxY - bodyY; if (float.IsNaN(num) || float.IsInfinity(num)) { return 1.5f; } if (!(num < 0.2f)) { return num; } return 0.2f; } public static string Signature(int source, string statusName, int skillItemId, int attach, float offX, float offY, float offZ, float scale, float seconds, int soundId) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; return string.Join("|", source.ToString(invariantCulture), statusName ?? "", skillItemId.ToString(invariantCulture), attach.ToString(invariantCulture), offX.ToString("G9", invariantCulture), offY.ToString("G9", invariantCulture), offZ.ToString("G9", invariantCulture), scale.ToString("G9", invariantCulture), seconds.ToString("G9", invariantCulture), soundId.ToString(invariantCulture)); } public static string Signature(int source, string statusName, int skillItemId, int attach, float offX, float offY, float offZ, float scale, float seconds, int soundId, string speciesKey, string subtreeFilter) { string text = Signature(source, statusName, skillItemId, attach, offX, offY, offZ, scale, seconds, soundId); if (string.IsNullOrEmpty(speciesKey) && string.IsNullOrEmpty(subtreeFilter)) { return text; } return text + "|" + speciesKey + "|" + subtreeFilter; } private static float SaneDim(float v) { if (float.IsNaN(v) || float.IsInfinity(v) || v < 0f) { return 0f; } return v; } public static bool SelfPlayNeedsRebind(bool rendererResolved, bool bodySmrAvailable) { return !rendererResolved && bodySmrAvailable; } } public enum ReapVerdict { Keep, Warn, Reap } public static class BodyReaperPolicy { public const float DefaultGraceSeconds = 90f; public const float DefaultCondemnSeconds = 20f; public static ReapVerdict Decide(float ageSeconds, bool claimedNow, float unclaimedForSeconds, bool reapEnabled, float graceSeconds = 90f, float condemnSeconds = 20f, bool everClaimed = true, bool warnedAlready = false) { if (claimedNow) { return ReapVerdict.Keep; } if (ageSeconds < graceSeconds) { return ReapVerdict.Keep; } if (unclaimedForSeconds < condemnSeconds) { return ReapVerdict.Warn; } if (!everClaimed && !warnedAlready) { return ReapVerdict.Warn; } if (!reapEnabled) { return ReapVerdict.Warn; } return ReapVerdict.Reap; } } public enum CombatStyle { Chase, Opposite } public sealed class CombatStationTuning { public float RingFraction = 0.6f; public float LineAngleDeg = 20f; public float RestationMeters = 3f; public float RestationSeconds = 2f; public float ArriveMeters = 0.8f; public int MaxRestations = 6; public float FarMeters = 3f; public float ProgressMeters = 1f; public float EnemyFastMetersPerSecond; } public struct CombatStationState { public bool Has; public float X; public float Z; public float EnemyAtX; public float EnemyAtZ; public float LastAt; public int Restations; public int Reaches; public bool CapFallback; public string LastWhy; public float LastStationDist; public int Stalls; public int FastHolds; public float LastFastHoldAt; } public static class CombatStation { public const string WhyFirst = "first"; public const string WhyCorridor = "corridor"; public const string WhyCap = "cap"; public const string WhyNear = "near"; public const string WhyFar = "far"; public const string WhyReach = "reach"; public const string WhyStalled = "stalled"; public const float SettleSlackMeters = 0.5f; public const float ReachSlackMeters = 0.5f; public static bool Decide(ref CombatStationState s, float enemyX, float enemyZ, float ownerX, float ownerZ, float petX, float petZ, float attackRange, float now, CombatStationTuning t) { return Decide(ref s, enemyX, enemyZ, ownerX, ownerZ, petX, petZ, attackRange, now, t, float.NaN); } public static bool Decide(ref CombatStationState s, float enemyX, float enemyZ, float ownerX, float ownerZ, float petX, float petZ, float attackRange, float now, CombatStationTuning t, float enemySpeed) { if (t == null) { t = new CombatStationTuning(); } if (s.CapFallback) { return false; } float num = attackRange * t.RingFraction; if (!s.Has) { Place(ref s, enemyX, enemyZ, ownerX, ownerZ, petX, petZ, num); s.Has = true; s.LastStationDist = Dist(s.X, s.Z, petX, petZ); s.Stalls = 0; s.Restations = 0; s.LastAt = now; s.LastWhy = "first"; return true; } float num2 = Dist(petX, petZ, enemyX, enemyZ); bool flag = num2 > attackRange + t.FarMeters; bool num3 = InCorridor(enemyX, enemyZ, ownerX, ownerZ, petX, petZ, t.LineAngleDeg, num); bool num4 = num2 <= num + 0.5f; float num5 = Dist(enemyX, enemyZ, s.EnemyAtX, s.EnemyAtZ); bool flag2 = !num4 && num5 < t.RestationMeters; bool flag3 = Dist(petX, petZ, s.X, s.Z) <= t.ArriveMeters + 0.5f; bool flag4 = flag3 && num2 > attackRange + 0.5f; bool flag5 = num3 && !flag2; if (flag && t.EnemyFastMetersPerSecond > 0f && !float.IsNaN(enemySpeed) && enemySpeed > t.EnemyFastMetersPerSecond) { flag = false; flag4 = false; if (!flag5 && now - s.LastAt >= t.RestationSeconds && now - s.LastFastHoldAt >= t.RestationSeconds) { s.FastHolds++; s.LastFastHoldAt = now; } } if (!flag && !flag4 && !flag5) { return false; } string text = (flag5 ? "corridor" : (flag ? "far" : "reach")); if (now - s.LastAt < t.RestationSeconds) { return false; } Ideal(enemyX, enemyZ, ownerX, ownerZ, petX, petZ, num, out var x, out var z); if (text == "far" && !flag3) { if (Dist(x, z, petX, petZ) > s.LastStationDist - t.ProgressMeters) { s.Stalls++; if (s.Stalls >= 2) { s.CapFallback = true; s.LastWhy = "stalled"; s.LastAt = now; return false; } } else { s.Stalls = 0; } } if (Dist(x, z, petX, petZ) <= t.ArriveMeters || (!flag3 && Dist(x, z, s.X, s.Z) <= t.ArriveMeters)) { s.LastAt = now; s.LastWhy = "near"; return false; } bool flag6 = text != "reach"; if (flag6 && s.Restations >= t.MaxRestations) { s.CapFallback = true; s.LastWhy = "cap"; s.LastAt = now; return false; } s.X = x; s.Z = z; s.LastStationDist = Dist(x, z, petX, petZ); s.EnemyAtX = enemyX; s.EnemyAtZ = enemyZ; if (flag6) { s.Restations++; } else { s.Reaches++; } s.LastAt = now; s.LastWhy = text; return true; } public static void Reset(ref CombatStationState s) { s = default(CombatStationState); } public static float CorridorAngleDeg(float enemyX, float enemyZ, float ownerX, float ownerZ, float petX, float petZ) { float num = ownerX - enemyX; float num2 = ownerZ - enemyZ; float num3 = petX - enemyX; float num4 = petZ - enemyZ; float num5 = (float)Math.Sqrt(num * num + num2 * num2); float num6 = (float)Math.Sqrt(num3 * num3 + num4 * num4); if (num5 < 0.0001f || num6 < 0.0001f) { return 180f; } float num7 = (num * num3 + num2 * num4) / (num5 * num6); if (num7 > 1f) { num7 = 1f; } else if (num7 < -1f) { num7 = -1f; } return (float)(Math.Acos(num7) * 180.0 / Math.PI); } public static float CorridorBlockAngleDeg(float enemyX, float enemyZ, float ownerX, float ownerZ, float petX, float petZ) { float num = enemyX - ownerX; float num2 = enemyZ - ownerZ; float num3 = petX - ownerX; float num4 = petZ - ownerZ; float num5 = (float)Math.Sqrt(num * num + num2 * num2); float num6 = (float)Math.Sqrt(num3 * num3 + num4 * num4); if (num5 < 0.0001f || num6 < 0.0001f) { return 180f; } float num7 = (num * num3 + num2 * num4) / (num5 * num6); if (num7 > 1f) { num7 = 1f; } else if (num7 < -1f) { num7 = -1f; } return (float)(Math.Acos(num7) * 180.0 / Math.PI); } public static bool InCorridor(float enemyX, float enemyZ, float ownerX, float ownerZ, float petX, float petZ, float lineAngleDeg, float depthMeters) { if (CorridorBlockAngleDeg(enemyX, enemyZ, ownerX, ownerZ, petX, petZ) >= lineAngleDeg) { return false; } return Dist(ownerX, ownerZ, enemyX, enemyZ) - Dist(ownerX, ownerZ, petX, petZ) > depthMeters; } private static void Place(ref CombatStationState s, float ex, float ez, float ox, float oz, float px, float pz, float ring) { Ideal(ex, ez, ox, oz, px, pz, ring, out s.X, out s.Z); s.EnemyAtX = ex; s.EnemyAtZ = ez; } private static void Ideal(float ex, float ez, float ox, float oz, float px, float pz, float ring, out float x, out float z) { float num = ex - ox; float num2 = ez - oz; float num3 = (float)Math.Sqrt(num * num + num2 * num2); if (num3 < 0.0001f) { num = px - ex; num2 = pz - ez; num3 = (float)Math.Sqrt(num * num + num2 * num2); if (num3 < 0.0001f) { num = 1f; num2 = 0f; num3 = 1f; } } x = ex + num / num3 * ring; z = ez + num2 / num3 * ring; } private static float Dist(float ax, float az, float bx, float bz) { float num = ax - bx; float num2 = az - bz; return (float)Math.Sqrt(num * num + num2 * num2); } } public static class CombatSweep { public static bool ShouldDrop(bool alive, float distToPlayer, float combatLeash) { if (alive) { return distToPlayer > combatLeash; } return true; } } public enum CombatTargetSource { None, Commanded, OwnerFocus, AnchorDefend, PlayerEngaged } public struct CombatTargetFacts { public bool StancePassive; public bool CommandedExists; public float CommandedDistance; public bool AssistOnOwnerHit; public bool OwnerFocusExists; public float OwnerFocusDistance; public float OwnerFocusRange; public bool AnchorDefendExists; public bool AnchorDefendIsEcho; public float AnchorDefendDistance; public bool PlayerInCombat; public bool PlayerEngagedExists; public float PlayerEngagedDistance; public float AggroRange; public float CombatLeashDistance; } public struct CombatTargetDecision { public CombatTargetSource Source; public string Reason; public bool DropCommandedOrder; } public static class CombatTargetPolicy { private const string StaleReason = "beyond-combat-leash (stale, dropped)"; public static CombatTargetDecision Decide(CombatTargetFacts f) { if (f.StancePassive) { return Clear("passive"); } if (f.CommandedExists) { if (f.CommandedDistance > f.CombatLeashDistance) { return new CombatTargetDecision { Source = CombatTargetSource.None, Reason = "beyond-combat-leash (stale, dropped)", DropCommandedOrder = true }; } return new CombatTargetDecision { Source = CombatTargetSource.Commanded, Reason = "commanded" }; } if (f.AssistOnOwnerHit && f.OwnerFocusExists && f.OwnerFocusDistance <= f.OwnerFocusRange) { if (f.OwnerFocusDistance > f.CombatLeashDistance) { return Clear("beyond-combat-leash (stale, dropped)"); } return new CombatTargetDecision { Source = CombatTargetSource.OwnerFocus, Reason = "owner-focus" }; } if (f.AnchorDefendExists && !f.AnchorDefendIsEcho && f.AnchorDefendDistance <= f.AggroRange) { if (f.AnchorDefendDistance > f.CombatLeashDistance) { return Clear("beyond-combat-leash (stale, dropped)"); } return new CombatTargetDecision { Source = CombatTargetSource.AnchorDefend, Reason = "anchor-defend" }; } if (!f.PlayerInCombat) { return Clear("player-not-in-combat"); } if (f.PlayerEngagedExists && f.PlayerEngagedDistance <= f.AggroRange) { if (f.PlayerEngagedDistance > f.CombatLeashDistance) { return Clear("beyond-combat-leash (stale, dropped)"); } return new CombatTargetDecision { Source = CombatTargetSource.PlayerEngaged, Reason = "player-engaged" }; } return Clear("none-in-aggro-range"); } private static CombatTargetDecision Clear(string reason) { return new CombatTargetDecision { Source = CombatTargetSource.None, Reason = reason }; } } public enum CommandMode { Engaged, Follow, Stay } public static class CommandCycle { public static CommandMode Next(CommandMode current) { return current switch { CommandMode.Engaged => CommandMode.Follow, CommandMode.Follow => CommandMode.Stay, _ => CommandMode.Engaged, }; } } public struct EquipState { public int ItemId; public float Durability; public float MaxDurability; public bool IsBroken => Durability <= 0f; public float Ratio { get { if (!(MaxDurability > 0f)) { return 1f; } return Clamp01(Durability / MaxDurability); } } public static float Clamp01(float v) { if (!(v < 0f)) { if (!(v > 1f)) { return v; } return 1f; } return 0f; } } public static class EquipCodec { public static string Pack(string key, EquipState s, Action warn = null) { if (s.ItemId == 0) { return ""; } string text = (key ?? "").Trim(); bool flag = text.Length == 0 || text.IndexOf(':') >= 0; if (!flag) { string text2 = text; for (int i = 0; i < text2.Length; i++) { if (char.IsControl(text2[i])) { flag = true; break; } } } if (flag) { warn?.Invoke("equipment key '" + key + "' is empty or carries ':'/control characters — stored as 'item'."); text = "item"; } if (!Finite(s.Durability) || !Finite(s.MaxDurability)) { warn?.Invoke($"equipment '{text}' has a non-finite durability ({s.Durability}/{s.MaxDurability}) — not persisted."); return ""; } return text + ":" + s.ItemId.ToString(CultureInfo.InvariantCulture) + ":" + s.Durability.ToString("R", CultureInfo.InvariantCulture) + ":" + s.MaxDurability.ToString("R", CultureInfo.InvariantCulture); } public static bool TryParse(string cell, out string key, out EquipState state, Action warn = null) { key = ""; state = default(EquipState); if (string.IsNullOrEmpty(cell)) { return false; } string[] array = cell.Split(new char[1] { ':' }); if (array.Length != 4) { warn?.Invoke("malformed equipment cell '" + cell + "' (want key:itemId:dur:max) — read as nothing equipped."); return false; } if (!int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result <= 0) { warn?.Invoke("unreadable equipment item id '" + array[1] + "' — read as nothing equipped."); return false; } if (!TryFinite(array[2], out var v) || !TryFinite(array[3], out var v2) || v2 <= 0f) { warn?.Invoke("unreadable equipment durability '" + array[2] + "/" + array[3] + "' — read as nothing equipped."); return false; } if (v < 0f) { warn?.Invoke("negative equipment durability " + array[2] + " — clamped to 0 (broken)."); v = 0f; } if (v > v2) { warn?.Invoke("equipment durability " + array[2] + " above max " + array[3] + " — clamped to max."); v = v2; } key = array[0]; state = new EquipState { ItemId = result, Durability = v, MaxDurability = v2 }; return true; } private static bool Finite(float v) { if (!float.IsNaN(v)) { return !float.IsInfinity(v); } return false; } private static bool TryFinite(string s, out float v) { if (float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out v)) { return Finite(v); } return false; } } public struct WearPolicy { public float PerDamage; public float MinPerHit; public static WearPolicy Default => new WearPolicy { PerDamage = 0.05f, MinPerHit = 1f }; } public static class EquipmentWear { public static float Loss(float preMitigationTotal, WearPolicy p) { if (preMitigationTotal <= 0f || float.IsNaN(preMitigationTotal)) { return 0f; } float num = preMitigationTotal * ((p.PerDamage > 0f) ? p.PerDamage : 0f); if (p.MinPerHit > 0f && num < p.MinPerHit) { num = p.MinPerHit; } return num; } public static EquipState AfterHit(EquipState s, float preMitigationTotal, WearPolicy p) { if (s.ItemId == 0 || s.IsBroken) { return s; } float num = s.Durability - Loss(preMitigationTotal, p); s.Durability = ((num < 0f) ? 0f : num); return s; } } public struct ResistGrant { public float Phys; public float AllOther; public bool IsZero { get { if (Phys == 0f) { return AllOther == 0f; } return false; } } } public static class EquipmentEffects { public static void Apply(CreatureAttributes eff, ResistGrant grant, bool broken) { if (eff == null || broken || grant.IsZero) { return; } for (int i = 0; i < eff.Resist.Length; i++) { float num = ((i == 0) ? grant.Phys : grant.AllOther); if (num != 0f) { float num2 = eff.Resist[i] + num; eff.Resist[i] = ((num2 < -100f) ? (-100f) : ((num2 > 100f) ? 100f : num2)); } } } } public enum RepairVerdict { Ok, AlreadyFull, NoReagent, NothingEquipped } public static class EquipmentRepair { public static RepairVerdict Evaluate(EquipState s, int reagentHeld, int reagentCost) { if (s.ItemId == 0) { return RepairVerdict.NothingEquipped; } if (s.Durability >= s.MaxDurability) { return RepairVerdict.AlreadyFull; } if (reagentHeld < reagentCost) { return RepairVerdict.NoReagent; } return RepairVerdict.Ok; } public static EquipState Repaired(EquipState s) { s.Durability = s.MaxDurability; return s; } } public enum BodilessAnchorPolicy { DestroyAnchor, UpkeepAnchor } public static class BodilessAnchorRule { public enum Action { None, Destroy, Upkeep } public static Action Decide(BodilessAnchorPolicy policy, bool anchorExists) { if (policy == BodilessAnchorPolicy.UpkeepAnchor) { return Action.Upkeep; } if (!anchorExists) { return Action.None; } return Action.Destroy; } } public static class HostTag { public const int MaxLength = 4; public static string Derive(string guidOrName) { if (string.IsNullOrEmpty(guidOrName)) { return null; } int num = guidOrName.LastIndexOf('.'); string obj = ((num >= 0 && num < guidOrName.Length - 1) ? guidOrName.Substring(num + 1) : guidOrName); StringBuilder stringBuilder = new StringBuilder(4); string text = obj; foreach (char c in text) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToUpperInvariant(c)); if (stringBuilder.Length >= 4) { break; } } } if (stringBuilder.Length != 0) { return stringBuilder.ToString(); } return null; } } public static class CompanionSceneMath { public static void SpotAhead(double px, double pz, double fx, double fz, double aheadMeters, out double x, out double z) { double num = Math.Sqrt(fx * fx + fz * fz); if (num < 1E-06) { x = px; z = pz; } else { x = px + fx / num * aheadMeters; z = pz + fz / num * aheadMeters; } } public static bool Arrived(double moverX, double moverZ, double spotX, double spotZ, double arriveDistance) { double num = moverX - spotX; double num2 = moverZ - spotZ; return num * num + num2 * num2 <= arriveDistance * arriveDistance; } } public static class ContaminationPolicy { public static int Total(int photonViews, int netControls, int characterAi, int charAiDisable) { return photonViews + netControls + characterAi + charAiDisable; } public static bool ShouldRebuild(int contamination) { return contamination > 0; } public static string Describe(int photonViews, int netControls, int characterAi, int charAiDisable, int characters) { return "pv=" + photonViews + " ncc=" + netControls + " charAI=" + characterAi + " caid=" + charAiDisable + " character=" + characters; } } public static class CoopCaptureNormalizer { public static float Divisor(bool isGuestInRoom, int playersInLobby, float[] maxHealthMultipliers) { if (!isGuestInRoom || playersInLobby < 2) { return 1f; } if (maxHealthMultipliers == null || maxHealthMultipliers.Length == 0) { return 1f; } int num = playersInLobby - 2; if (num > maxHealthMultipliers.Length - 1) { num = maxHealthMultipliers.Length - 1; } float num2 = maxHealthMultipliers[num]; if (float.IsNaN(num2) || float.IsInfinity(num2) || num2 <= 0f) { return 1f; } return num2; } public static float Normalize(float capturedMaxHealth, float divisor) { if (!(divisor > 0f) || float.IsNaN(divisor) || float.IsInfinity(divisor)) { return capturedMaxHealth; } return capturedMaxHealth / divisor; } } public sealed class CreatureAttributes { public const int TypeCount = 9; public float MaxHealth; public float MoveSpeed; public float Impact; public float ImpactResistance; public float Barrier; public float ProtectionAll; public float StatusResistance; public float[] Resist = new float[9]; public float[] Protection = new float[9]; public float[] Damage = new float[9]; public float DamageTotal { get { float num = 0f; for (int i = 0; i < Damage.Length; i++) { num += Damage[i]; } return num; } } public bool HasDamage => DamageTotal > 0f; public CreatureAttributes Clone() { CreatureAttributes creatureAttributes = new CreatureAttributes { MaxHealth = MaxHealth, MoveSpeed = MoveSpeed, Impact = Impact, ImpactResistance = ImpactResistance, Barrier = Barrier, ProtectionAll = ProtectionAll, StatusResistance = StatusResistance }; Array.Copy(Resist, creatureAttributes.Resist, 9); Array.Copy(Protection, creatureAttributes.Protection, 9); Array.Copy(Damage, creatureAttributes.Damage, 9); return creatureAttributes; } public float[] DistributeTotal(float total) { return DistributeTotal(Damage, total, 9); } public static float[] DistributeTotal(float[]? profile, float total, int typeCount) { if (typeCount < 1) { typeCount = 1; } float[] array = new float[typeCount]; int num = ((profile != null) ? Math.Min(typeCount, profile.Length) : 0); float num2 = 0f; for (int i = 0; i < num; i++) { if (profile[i] > 0f) { num2 += profile[i]; } } if (num2 <= 0f) { array[0] = total; return array; } float num3 = total / num2; for (int j = 0; j < num; j++) { if (profile[j] > 0f) { array[j] = profile[j] * num3; } } return array; } public string Flatten() { return Flatten(null); } public string Flatten(Action warn) { StringBuilder stringBuilder = new StringBuilder(); Scalar(stringBuilder, "hp", MaxHealth, warn); Scalar(stringBuilder, "spd", MoveSpeed, warn); Scalar(stringBuilder, "imp", Impact, warn); Scalar(stringBuilder, "impres", ImpactResistance, warn); Scalar(stringBuilder, "bar", Barrier, warn); Scalar(stringBuilder, "protall", ProtectionAll, warn); Scalar(stringBuilder, "statres", StatusResistance, warn); Sparse(stringBuilder, "res", Resist, warn); Sparse(stringBuilder, "prot", Protection, warn); Sparse(stringBuilder, "dmg", Damage, warn); return stringBuilder.ToString(); } public static CreatureAttributes Parse(string s) { return Parse(s, null); } public static CreatureAttributes Parse(string s, Action warn) { if (string.IsNullOrWhiteSpace(s)) { return null; } CreatureAttributes creatureAttributes = new CreatureAttributes(); bool flag = false; string[] array = s.Split(new char[1] { ';' }); foreach (string text in array) { if (text.Trim().Length == 0) { continue; } int num = text.IndexOf('='); if (num <= 0) { warn?.Invoke("attributes: unreadable segment '" + text + "' (expected key=value) — skipped."); continue; } string text2 = text.Substring(0, num).Trim(); string text3 = text.Substring(num + 1).Trim(); switch (text2) { case "hp": flag |= TryF(text3, ref creatureAttributes.MaxHealth, warn, text2); break; case "spd": flag |= TryF(text3, ref creatureAttributes.MoveSpeed, warn, text2); break; case "imp": flag |= TryF(text3, ref creatureAttributes.Impact, warn, text2); break; case "impres": flag |= TryF(text3, ref creatureAttributes.ImpactResistance, warn, text2); break; case "bar": flag |= TryF(text3, ref creatureAttributes.Barrier, warn, text2); break; case "protall": flag |= TryF(text3, ref creatureAttributes.ProtectionAll, warn, text2); break; case "statres": flag |= TryF(text3, ref creatureAttributes.StatusResistance, warn, text2); break; case "res": flag |= ParseSparse(text3, creatureAttributes.Resist, warn, text2); break; case "prot": flag |= ParseSparse(text3, creatureAttributes.Protection, warn, text2); break; case "dmg": flag |= ParseSparse(text3, creatureAttributes.Damage, warn, text2); break; default: warn?.Invoke("attributes: unknown key '" + text2 + "' — ignored."); break; } } if (!flag) { warn?.Invoke("attributes: nothing readable in '" + s + "' — no capture restored."); } if (!flag) { return null; } return creatureAttributes; } private static bool IsFinite(float v) { if (!float.IsNaN(v)) { return !float.IsInfinity(v); } return false; } private static void Scalar(StringBuilder sb, string key, float v, Action warn) { if (v == 0f) { return; } if (!IsFinite(v)) { warn?.Invoke($"attributes: non-finite '{key}' ({v}) — omitted."); return; } if (sb.Length > 0) { sb.Append(';'); } sb.Append(key).Append('=').Append(v.ToString("R", CultureInfo.InvariantCulture)); } private static void Sparse(StringBuilder sb, string key, float[] values, Action warn) { bool flag = false; for (int i = 0; i < values.Length; i++) { if (values[i] == 0f) { continue; } if (!IsFinite(values[i])) { warn?.Invoke($"attributes: non-finite '{key}[{i}]' ({values[i]}) — omitted."); continue; } if (!flag) { if (sb.Length > 0) { sb.Append(';'); } sb.Append(key).Append('='); flag = true; } else { sb.Append(','); } sb.Append(i.ToString(CultureInfo.InvariantCulture)).Append(':').Append(values[i].ToString("R", CultureInfo.InvariantCulture)); } } private static bool ParseSparse(string list, float[] into, Action warn, string key) { bool result = false; string[] array = list.Split(new char[1] { ',' }); foreach (string text in array) { if (text.Trim().Length == 0) { continue; } int num = text.IndexOf(':'); int result2; if (num <= 0) { warn?.Invoke("attributes: unreadable '" + key + "' entry '" + text + "' (expected index:value) — skipped."); } else if (!int.TryParse(text.Substring(0, num), NumberStyles.Integer, CultureInfo.InvariantCulture, out result2)) { warn?.Invoke("attributes: unreadable '" + key + "' index '" + text.Substring(0, num) + "' — skipped."); } else if (result2 < 0 || result2 >= into.Length) { warn?.Invoke($"attributes: '{key}' index {result2} out of range (0..{into.Length - 1}) — skipped."); } else { float v = into[result2]; if (TryF(text.Substring(num + 1), ref v, warn, $"{key}[{result2}]")) { into[result2] = v; result = true; } } } return result; } private static bool TryF(string s, ref float v, Action warn, string field) { if (!float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { warn?.Invoke("attributes: unreadable '" + field + "' value '" + s + "' — skipped."); return false; } if (!IsFinite(result)) { warn?.Invoke("attributes: non-finite '" + field + "' value '" + s + "' — skipped."); return false; } v = result; return true; } } public static class DamageRider { public static float Amount(float total, float fraction) { if (!(fraction > 0f) || !(total > 0f)) { return 0f; } return total * fraction; } } public static class DisplayChainRisk { private static readonly string[] Tokens = new string[4] { "waterpro", "waterreflection", "waterrender", "aquas" }; public static bool IsWaterReflectionRigName(string name) { if (string.IsNullOrEmpty(name)) { return false; } for (int i = 0; i < Tokens.Length; i++) { if (name.IndexOf(Tokens[i], StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } } public enum DonorMatch { None, Substring, Exact } public struct DonorChainPick { public int UsedIndex; public bool Revisit; public DonorChainPick(int usedIndex, bool revisit) { UsedIndex = usedIndex; Revisit = revisit; } } public static class DonorChain { public static bool AcceptSubstringAt(int chainIndex, int chainCount, bool haveEarlierFallback) { if (chainIndex >= chainCount - 1) { return !haveEarlierFallback; } return false; } public static DonorChainPick Resolve(IReadOnlyList probes) { int num = probes?.Count ?? 0; int num2 = -1; for (int i = 0; i < num; i++) { switch (probes[i]) { case DonorMatch.Exact: return new DonorChainPick(i, revisit: false); case DonorMatch.Substring: if (AcceptSubstringAt(i, num, num2 >= 0)) { return new DonorChainPick(i, revisit: false); } if (num2 < 0) { num2 = i; } break; } } if (num2 < 0) { return new DonorChainPick(-1, revisit: false); } return new DonorChainPick(num2, revisit: true); } } public static class DonorTable { private static readonly string[] OversizedSceneNames = new string[14] { "ChersoneseNewTerrain", "Emercar", "HallowedMarshNewTerrain", "Abrassar", "AntiqueField", "Caldera", "CierzoNewTerrain", "Berg", "Monsoon", "Levant", "Harmattan", "NewSirocco", "CierzoDestroyed", "CierzoTutorial" }; public static IReadOnlyList OversizedScenes => OversizedSceneNames; public static Dictionary> Parse(string text) { Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (SpeciesTable.TableLine item in SpeciesTable.Lines(text)) { SplitPin(item.Key, out string key, out string _); if (key.Length == 0) { continue; } if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List()); } string[] fields = item.Fields; for (int i = 0; i < fields.Length; i++) { string text2 = fields[i].Trim(); if (text2.Length != 0 && !value.Contains(text2, StringComparer.OrdinalIgnoreCase)) { value.Add(text2); } } } return dictionary; } public static Dictionary ParsePins(string text) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (SpeciesTable.TableLine item in SpeciesTable.Lines(text)) { SplitPin(item.Key, out string key, out string pin); if (key.Length != 0 && pin.Length != 0 && !dictionary.ContainsKey(key)) { dictionary[key] = pin; } } return dictionary; } public static Dictionary MergePins(Dictionary builtIn, Dictionary overrides) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (builtIn != null) { foreach (KeyValuePair item in builtIn) { dictionary[item.Key] = item.Value; } } if (overrides != null) { foreach (KeyValuePair @override in overrides) { dictionary[@override.Key] = @override.Value; } } return dictionary; } private static void SplitPin(string rawKey, out string key, out string pin) { key = (rawKey ?? "").Trim(); pin = ""; int num = key.IndexOf('|'); if (num >= 0) { pin = key.Substring(num + 1).Trim(); key = key.Substring(0, num).Trim(); } } public static bool PinMatches(string gameObjectName, string pin) { if (string.IsNullOrEmpty(gameObjectName) || string.IsNullOrEmpty(pin)) { return false; } return string.Equals(StripInstanceSuffix(gameObjectName), StripInstanceSuffix(pin), StringComparison.OrdinalIgnoreCase); } public static string StripInstanceSuffix(string name) { string text = (name ?? "").Trim(); bool flag = true; while (flag) { flag = false; if (text.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - "(Clone)".Length).Trim(); flag = true; continue; } if (!text.EndsWith(")", StringComparison.Ordinal)) { break; } int num = text.LastIndexOf('('); if (num <= 0) { break; } bool flag2 = num + 1 < text.Length - 1; for (int i = num + 1; i < text.Length - 1 && flag2; i++) { if (!char.IsDigit(text[i])) { flag2 = false; } } if (!flag2) { break; } text = text.Substring(0, num).Trim(); flag = true; } return text; } public static Dictionary> Merge(Dictionary> builtIn, Dictionary> overrides) { Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); if (overrides != null) { foreach (KeyValuePair> @override in overrides) { dictionary[@override.Key] = new List(@override.Value); } } if (builtIn != null) { foreach (KeyValuePair> item in builtIn) { if (!dictionary.TryGetValue(item.Key, out var value)) { value = (dictionary[item.Key] = new List()); } foreach (string item2 in item.Value) { if (!value.Contains(item2, StringComparer.OrdinalIgnoreCase)) { value.Add(item2); } } } } return dictionary; } public static bool IsOversized(string sceneName) { if (string.IsNullOrEmpty(sceneName)) { return false; } string[] oversizedSceneNames = OversizedSceneNames; for (int i = 0; i < oversizedSceneNames.Length; i++) { if (string.Equals(oversizedSceneNames[i], sceneName, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public static List FilterViable(IEnumerable scenes, out List excluded) { List list = new List(); excluded = new List(); foreach (string item in scenes ?? new List()) { (IsOversized(item) ? excluded : list).Add(item); } return list; } public static List ExpeditionCandidates(IEnumerable scenes) { FilterViable(scenes, out List excluded); return excluded; } public static List KeysForScene(Dictionary> table, string sceneName) { return KeysForScene(table, sceneName, includeOversized: false); } public static List KeysForScene(Dictionary> table, string sceneName, bool includeOversized) { List list = new List(); if (table == null || string.IsNullOrEmpty(sceneName)) { return list; } foreach (KeyValuePair> item in table) { if (item.Value == null) { continue; } foreach (string item2 in item.Value) { if ((includeOversized || !IsOversized(item2)) && string.Equals(item2, sceneName, StringComparison.OrdinalIgnoreCase)) { list.Add(item.Key); break; } } } return list; } public static List OrderCandidates(IEnumerable scenes, string currentScene) { List list = new List(); List list2 = new List(); List list3 = new List(); string text = Regions.RegionOf(currentScene); foreach (string item in scenes ?? new List()) { if (string.Equals(item, currentScene, StringComparison.OrdinalIgnoreCase)) { list3.Add(item); } else if (text != null && Regions.RegionOf(item) == text) { list2.Add(item); } else { list.Add(item); } } list.AddRange(list2); list.AddRange(list3); return list; } private static bool Contains(this List list, string value, StringComparer cmp) { foreach (string item in list) { if (cmp.Equals(item, value)) { return true; } } return false; } } public static class Regions { private static readonly string[,] Keywords = new string[12, 2] { { "Chersonese", "Chersonese" }, { "Cierzo", "Chersonese" }, { "Emercar", "Emercar" }, { "Berg", "Emercar" }, { "Hallowed", "Hallowed" }, { "Monsoon", "Hallowed" }, { "Abrassar", "Abrassar" }, { "Levant", "Abrassar" }, { "Antique", "Antique" }, { "Harmattan", "Antique" }, { "Caldera", "Caldera" }, { "Sirocco", "Caldera" } }; public static string? RegionOf(string? sceneName) { if (sceneName == null || sceneName.Length == 0) { return null; } for (int i = 0; i < Keywords.GetLength(0); i++) { if (sceneName.IndexOf(Keywords[i, 0], StringComparison.OrdinalIgnoreCase) >= 0) { return Keywords[i, 1]; } } return null; } } public static class SpeciesFloats { public static List> Parse(string spec) { List> list = new List>(); foreach (SpeciesTable.TableLine item in SpeciesTable.Lines(spec, ',')) { float num = SpeciesTable.FloatOr(item.Fields, 0, float.NaN, null, item.Key); if (!float.IsNaN(num)) { list.Add(new KeyValuePair(item.Key, num)); } } return list; } public static float For(List> pairs, string speciesId) { string matchedKey; return ForKeyed(pairs, speciesId, out matchedKey); } public static float ForKeyed(List> pairs, string speciesId, out string matchedKey) { matchedKey = null; if (pairs == null || string.IsNullOrEmpty(speciesId)) { return float.NaN; } string text = null; float result = float.NaN; foreach (KeyValuePair pair in pairs) { if (!string.IsNullOrEmpty(pair.Key)) { if (string.Equals(pair.Key, speciesId, StringComparison.OrdinalIgnoreCase)) { matchedKey = pair.Key; return pair.Value; } if (Species.NameMatches(speciesId, pair.Key) && (text == null || pair.Key.Length > text.Length)) { text = pair.Key; result = pair.Value; } } } matchedKey = text; return result; } public static List> Merge(List> baseList, List> overrides) { List> list = new List>(); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); if (overrides != null) { foreach (KeyValuePair @override in overrides) { if (!string.IsNullOrEmpty(@override.Key)) { hashSet.Add(@override.Key); } } } if (baseList != null) { foreach (KeyValuePair @base in baseList) { if (string.IsNullOrEmpty(@base.Key) || !hashSet.Contains(@base.Key)) { list.Add(@base); } } } if (overrides != null) { list.AddRange(overrides); } return list; } public static float ForLayered(List> baseList, List> overrides, string speciesId, out string matchedKey) { float num = ForKeyed(overrides, speciesId, out matchedKey); if (!float.IsNaN(num)) { return num; } return ForKeyed(baseList, speciesId, out matchedKey); } } public static class SpeciesYaw { public static List> Parse(string spec) { return SpeciesFloats.Parse(spec); } public static float For(List> pairs, string speciesId) { return SpeciesFloats.For(pairs, speciesId); } public static Dictionary ParseTable(string text, Action warn = null) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (SpeciesTable.TableLine item in SpeciesTable.Lines(text)) { float num = SpeciesTable.FloatOr(item.Fields, 0, float.NaN, warn, item.Key); if (!float.IsNaN(num)) { dictionary[item.Key] = num; } } return dictionary; } public static float Resolve(List> embeddedPairs, List> cfgPairs, IReadOnlyDictionary sessionExact, string speciesId) { if (!string.IsNullOrEmpty(speciesId) && sessionExact != null && sessionExact.TryGetValue(speciesId, out var value)) { return value; } return SpeciesFloats.For(SpeciesFloats.Merge(embeddedPairs, cfgPairs), speciesId); } } public static class SpeciesSlope { public static Dictionary ParseTable(string text, Action warn = null) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (SpeciesTable.TableLine item in SpeciesTable.Lines(text)) { float num = BoolField(item.Fields, warn, item.Key); if (!float.IsNaN(num)) { dictionary[item.Key] = num; } } return dictionary; } public static float BoolField(string[] fields, Action warn, string key) { return BoolField(fields, warn, key, "slope-tilt"); } public static float BoolField(string[] fields, Action warn, string key, string what) { if (fields == null || fields.Length == 0) { return float.NaN; } string text = fields[0].Trim(); if (text.Length == 0) { return float.NaN; } if (text.Equals("true", StringComparison.OrdinalIgnoreCase) || text == "1") { return 1f; } if (text.Equals("false", StringComparison.OrdinalIgnoreCase) || text == "0") { return 0f; } warn?.Invoke("'" + key + "': unparsable " + what + " flag '" + text + "' (want true/false/1/0) — row skipped."); return float.NaN; } public static float Resolve(List> pairs, string speciesId) { return SpeciesFloats.For(pairs, speciesId); } } public static class SpeciesBackpedal { public static Dictionary ParseTable(string text, Action warn = null) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (SpeciesTable.TableLine item in SpeciesTable.Lines(text)) { float num = SpeciesSlope.BoolField(item.Fields, warn, item.Key, "backpedal"); if (!float.IsNaN(num)) { dictionary[item.Key] = num; } } return dictionary; } public static float Resolve(List> pairs, string speciesId) { return SpeciesFloats.For(pairs, speciesId); } } public static class DuplicateViewPolicy { public const int SceneBakedMax = 999; public static bool ShouldVeto(int viewId, bool oldAlive, string oldScene, string newScene, string activeScene) { if (!oldAlive) { return false; } if (viewId < 1 || viewId > 999) { return false; } if (string.IsNullOrEmpty(activeScene) || oldScene != activeScene) { return false; } return newScene != activeScene; } } public struct EffigyHarvestFacts { public bool Enabled; public bool ExpeditionBusy; public bool HarvestBusy; public bool HasAdditiveDonor; public bool HasExpeditionDonor; } public enum EffigyHarvestStep { Go, Disabled, NoDonor, ExpeditionOnly, Backoff, ExpeditionBusy, HarvestBusy } public sealed class EffigyHarvestGate { public const float DefaultRetrySeconds = 600f; private readonly Dictionary _nextAttemptAt = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _failures = new Dictionary(StringComparer.OrdinalIgnoreCase); public float RetrySeconds { get; set; } public EffigyHarvestGate(float retrySeconds = 600f) { RetrySeconds = retrySeconds; } public EffigyHarvestStep Decide(string species, in EffigyHarvestFacts facts, float now) { if (string.IsNullOrEmpty(species)) { return EffigyHarvestStep.NoDonor; } if (!facts.Enabled) { return EffigyHarvestStep.Disabled; } if (!facts.HasAdditiveDonor) { if (!facts.HasExpeditionDonor) { return EffigyHarvestStep.NoDonor; } return EffigyHarvestStep.ExpeditionOnly; } if (_nextAttemptAt.TryGetValue(species, out var value) && now < value) { return EffigyHarvestStep.Backoff; } if (facts.ExpeditionBusy) { return EffigyHarvestStep.ExpeditionBusy; } if (facts.HarvestBusy) { return EffigyHarvestStep.HarvestBusy; } _nextAttemptAt[species] = now + RetrySeconds; return EffigyHarvestStep.Go; } public void ReportResult(string species, bool success, float now) { if (!string.IsNullOrEmpty(species)) { if (success) { _nextAttemptAt.Remove(species); _failures.Remove(species); } else { _nextAttemptAt[species] = now + RetrySeconds; _failures.TryGetValue(species, out var value); _failures[species] = value + 1; } } } public int Failures(string species) { if (string.IsNullOrEmpty(species) || !_failures.TryGetValue(species, out var value)) { return 0; } return value; } public float SecondsUntilRetry(string species, float now) { if (string.IsNullOrEmpty(species) || !_nextAttemptAt.TryGetValue(species, out var value)) { return 0f; } float num = value - now; if (!(num > 0f)) { return 0f; } return num; } public void Reset() { _nextAttemptAt.Clear(); _failures.Clear(); } } public enum EffigyBodyRung { None, Real, Ghost } public enum EffigySetResult { Refused, Created, Updated, SpeciesChanged } public enum EffigyStep { None, Acquire, Upgrade, Despawn } public sealed class EffigyRow { internal float NextAttemptAt; internal float AnchorUnresolvedAt; public string OwnerUid { get; internal set; } public string Species { get; internal set; } public int Tier { get; internal set; } public string Extension { get; internal set; } = ""; public bool StancePassive { get; internal set; } public bool AnchorResolved { get; internal set; } public EffigyBodyRung Body { get; internal set; } } public sealed class EffigyLedger { public const float DefaultAcquireRetrySeconds = 3f; public const float DefaultUpgradeCheckSeconds = 10f; public const float AnchorLossGraceSeconds = 8f; private readonly float _acquireRetrySeconds; private readonly float _upgradeCheckSeconds; private readonly Dictionary _rows = new Dictionary(StringComparer.Ordinal); public int Count => _rows.Count; public int LiveBodies { get { int num = 0; foreach (KeyValuePair row in _rows) { if (row.Value.Body != EffigyBodyRung.None) { num++; } } return num; } } public EffigyLedger(float acquireRetrySeconds = 3f, float upgradeCheckSeconds = 10f) { _acquireRetrySeconds = acquireRetrySeconds; _upgradeCheckSeconds = upgradeCheckSeconds; } public List RowsSnapshot() { List list = new List(_rows.Count); foreach (KeyValuePair row in _rows) { list.Add(row.Value); } return list; } public bool TryGet(string ownerUid, out EffigyRow row) { return _rows.TryGetValue(ownerUid ?? "", out row); } public EffigySetResult Set(string ownerUid, string species, int tier) { return Set(ownerUid, species, tier, ""); } public EffigySetResult Set(string ownerUid, string species, int tier, string extension) { if (string.IsNullOrEmpty(ownerUid) || string.IsNullOrEmpty(species)) { return EffigySetResult.Refused; } tier = NetProtocol.ClampTier(tier); extension = extension ?? ""; if (!_rows.TryGetValue(ownerUid, out EffigyRow value)) { _rows[ownerUid] = new EffigyRow { OwnerUid = ownerUid, Species = species, Tier = tier, Extension = extension }; return EffigySetResult.Created; } bool num = !string.Equals(value.Species, species, StringComparison.Ordinal); value.Species = species; value.Tier = tier; value.Extension = extension; if (num) { value.NextAttemptAt = 0f; } if (!num) { return EffigySetResult.Updated; } return EffigySetResult.SpeciesChanged; } public bool SetStance(string ownerUid, bool passive) { if (!_rows.TryGetValue(ownerUid ?? "", out EffigyRow value) || value.StancePassive == passive) { return false; } value.StancePassive = passive; return true; } public bool Clear(string ownerUid) { return _rows.Remove(ownerUid ?? ""); } public int ClearAll() { int count = _rows.Count; _rows.Clear(); return count; } public void MarkAnchor(string ownerUid, bool resolved, float now) { if (_rows.TryGetValue(ownerUid ?? "", out EffigyRow value)) { if (value.AnchorResolved && !resolved) { value.AnchorUnresolvedAt = now; } value.AnchorResolved = resolved; } } public void MarkBody(string ownerUid, EffigyBodyRung rung) { if (_rows.TryGetValue(ownerUid ?? "", out EffigyRow value)) { value.Body = rung; if (rung == EffigyBodyRung.None) { value.NextAttemptAt = 0f; } } } public EffigyStep Reconcile(EffigyRow row, bool enabled, int maxBodies, float now) { if (row == null) { return EffigyStep.None; } if (!enabled) { if (row.Body == EffigyBodyRung.None) { return EffigyStep.None; } return EffigyStep.Despawn; } if (!row.AnchorResolved) { if (row.Body == EffigyBodyRung.None) { return EffigyStep.None; } if (!(now - row.AnchorUnresolvedAt >= 8f)) { return EffigyStep.None; } return EffigyStep.Despawn; } switch (row.Body) { case EffigyBodyRung.None: if (LiveBodies >= maxBodies) { return EffigyStep.None; } if (now < row.NextAttemptAt) { return EffigyStep.None; } row.NextAttemptAt = now + _acquireRetrySeconds; return EffigyStep.Acquire; case EffigyBodyRung.Ghost: if (now < row.NextAttemptAt) { return EffigyStep.None; } row.NextAttemptAt = now + _upgradeCheckSeconds; return EffigyStep.Upgrade; default: return EffigyStep.None; } } } public static class EffigyPinMath { public enum PinMove { Weld, Glide, Snap } public readonly struct PinStep { public readonly PinMove Mode; public readonly float MaxStep; public readonly float Distance; public PinStep(PinMove mode, float maxStep, float distance) { Mode = mode; MaxStep = maxStep; Distance = distance; } } public const float MaxAnimForward = 8f; public const float MinFacingSqr = 0.0001f; public static bool HorizontalFacing(float fx, float fz, out float nx, out float nz) { float num = fx * fx + fz * fz; if (num < 0.0001f) { nx = 0f; nz = 0f; return false; } float num2 = 1f / (float)Math.Sqrt(num); nx = fx * num2; nz = fz * num2; return true; } public static float HorizontalSpeed(float dx, float dz, float dt) { if (dt <= 0.0001f) { return 0f; } return (float)Math.Sqrt((double)dx * (double)dx + (double)dz * (double)dz) / dt; } public static PinStep CatchUpStep(float distance, float deadBand, float catchUpSpeed, float snapDistance, float dt) { if (!(distance > 0f)) { return new PinStep(PinMove.Weld, 0f, 0f); } if (snapDistance > 0f && distance >= snapDistance) { return new PinStep(PinMove.Snap, distance, distance); } if (distance <= deadBand) { return new PinStep(PinMove.Weld, 0f, distance); } float num = ((dt > 0f && catchUpSpeed > 0f) ? (catchUpSpeed * dt) : 0f); if (num > distance) { num = distance; } return new PinStep(PinMove.Glide, num, distance); } public static float AnimForward(float speed, bool moving) { if (!moving) { return 0f; } if (speed < 0f) { return 0f; } if (!(speed > 8f)) { return speed; } return 8f; } } public static class ExpeditionConfigMigration { public enum Outcome { SkipLegacyDefault, SkipInSync, SkipCkCustomized, Migrate } public static Outcome Decide(T legacy, T legacyDefault, T ck, T ckDefault) { EqualityComparer equalityComparer = EqualityComparer.Default; if (equalityComparer.Equals(legacy, legacyDefault)) { return Outcome.SkipLegacyDefault; } if (equalityComparer.Equals(legacy, ck)) { return Outcome.SkipInSync; } if (!equalityComparer.Equals(ck, ckDefault)) { return Outcome.SkipCkCustomized; } return Outcome.Migrate; } } public static class ExpeditionCoop { public struct GuestGateState { public bool Enabled; public bool IsGuest; public bool HostExpeditionActive; public bool GateArmed; public bool ContinueAlreadySet; public bool GameplayLoading; public bool AllPlayersDone; public bool MasterLoadingDisplayed; } public static bool ShouldRefuseBegin(bool allowCoop, bool inRoom, int otherPlayerCount) { if (!allowCoop && inRoom) { return otherPlayerCount > 0; } return false; } public static bool ShouldGuestContinue(in GuestGateState s) { if (s.Enabled && s.IsGuest && s.HostExpeditionActive && s.GateArmed && !s.ContinueAlreadySet && !s.GameplayLoading && s.AllPlayersDone) { return s.MasterLoadingDisplayed; } return false; } } public static class ExpeditionLog { public static List Parse(string text) { List list = new List(); if (string.IsNullOrEmpty(text)) { return list; } string[] array = text.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length != 0 && text2[0] != '#') { Append(list, text2); } } return list; } public static string Format(IEnumerable scenes) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("# Scenes whose body templates have been dumped by an expedition or in-place capture.\n"); stringBuilder.Append("# Read at boot for [Expedition] AutoWarmAtBoot=all; safe to edit or delete.\n"); if (scenes != null) { foreach (string scene in scenes) { if (!string.IsNullOrEmpty(scene)) { stringBuilder.Append(scene).Append('\n'); } } } return stringBuilder.ToString(); } public static bool Append(List scenes, string scene) { if (scenes == null || string.IsNullOrEmpty(scene)) { return false; } foreach (string scene2 in scenes) { if (string.Equals(scene2, scene, StringComparison.OrdinalIgnoreCase)) { return false; } } scenes.Add(scene); return true; } } public static class ExpeditionWarm { public enum Mode { Off, Needed, All } public struct SpeciesResolve { public string Species; public bool HasExpeditionScenes; public string FirstExpeditionScene; public bool HasAdditiveDonor; public bool HasCachedTemplate; public bool DonorSceneVisited; } public enum ListOutcome { Warm, SkippedAdditive, SkippedCached, DeferredUnvisited, Unknown, SkippedActiveCompanion } public struct ListDecision { public string Species; public ListOutcome Outcome; public string Scene; } public sealed class PlanInput { public Mode Mode; public SpeciesResolve? ActivePet; public IEnumerable ManifestScenes; public Func SceneHasUncachedKey; public List AlwaysWarm; public Func SceneVisited; } public sealed class Plan { public List Scenes = new List(); public List ListDecisions = new List(); public List DeferredScenes = new List(); public bool AnyDeferred => DeferredScenes.Count > 0; } public struct StorySafety { public bool InPrologue; public bool CurrentSceneFirstLoad; } public struct WarmRetryPolicy { public float MaxWaitSeconds; public float PollSeconds; } public struct WarmRetryState { public float ElapsedSeconds; public int Polls; } public enum WarmGate { Proceed, Wait, GiveUp } public static bool TryParseMode(string raw, out Mode mode) { mode = Mode.Off; string a = raw?.Trim(); if (string.Equals(a, "off", StringComparison.OrdinalIgnoreCase)) { mode = Mode.Off; return true; } if (string.Equals(a, "needed", StringComparison.OrdinalIgnoreCase)) { mode = Mode.Needed; return true; } if (string.Equals(a, "all", StringComparison.OrdinalIgnoreCase)) { mode = Mode.All; return true; } return false; } public static List ParseSpeciesList(string raw) { List list = new List(); if (string.IsNullOrEmpty(raw)) { return list; } string[] array = raw.Split(new char[1] { ',' }); foreach (string text in array) { ExpeditionLog.Append(list, text.Trim()); } return list; } public static bool StoryHold(in StorySafety s, out string reason) { if (s.InPrologue) { reason = "the prologue/tutorial is active"; return true; } if (s.CurrentSceneFirstLoad) { reason = "the current scene is being visited for the FIRST time (departing would save it mid-story and consume its one-shot beats)"; return true; } reason = null; return false; } public static string NeededScene(in SpeciesResolve r) { if (!r.HasExpeditionScenes || r.HasAdditiveDonor || r.HasCachedTemplate || !r.DonorSceneVisited || string.IsNullOrEmpty(r.FirstExpeditionScene)) { return null; } return r.FirstExpeditionScene; } public static ListDecision DecideListed(in SpeciesResolve r) { ListDecision result = new ListDecision { Species = r.Species }; if (!r.HasExpeditionScenes && !r.HasAdditiveDonor) { result.Outcome = ListOutcome.Unknown; } else if (r.HasAdditiveDonor) { result.Outcome = ListOutcome.SkippedAdditive; } else if (r.HasCachedTemplate) { result.Outcome = ListOutcome.SkippedCached; } else if (string.IsNullOrEmpty(r.FirstExpeditionScene)) { result.Outcome = ListOutcome.Unknown; } else if (!r.DonorSceneVisited) { result.Outcome = ListOutcome.DeferredUnvisited; result.Scene = r.FirstExpeditionScene; } else { result.Outcome = ListOutcome.Warm; result.Scene = r.FirstExpeditionScene; } return result; } public static Plan Decide(PlanInput input) { Plan plan = new Plan(); if (input == null) { return plan; } Func func = input.SceneVisited ?? ((Func)((string _) => true)); switch (input.Mode) { case Mode.Needed: { if (!input.ActivePet.HasValue) { break; } SpeciesResolve r = input.ActivePet.Value; string text = NeededScene(in r); if (text != null) { ExpeditionLog.Append(plan.Scenes, text); } else if (!r.DonorSceneVisited) { string text2 = NeededSceneIgnoringVisited(in r); if (text2 != null) { ExpeditionLog.Append(plan.DeferredScenes, text2); } } break; } case Mode.All: if (input.ManifestScenes == null || input.SceneHasUncachedKey == null) { break; } foreach (string manifestScene in input.ManifestScenes) { if (!string.IsNullOrEmpty(manifestScene) && input.SceneHasUncachedKey(manifestScene)) { if (func(manifestScene)) { ExpeditionLog.Append(plan.Scenes, manifestScene); } else { ExpeditionLog.Append(plan.DeferredScenes, manifestScene); } } } break; } if (input.AlwaysWarm != null) { bool flag = !input.ActivePet.HasValue; if (!flag) { foreach (SpeciesResolve item2 in input.AlwaysWarm) { if (string.Equals(item2.Species, input.ActivePet.Value.Species, StringComparison.OrdinalIgnoreCase)) { flag = true; break; } } } foreach (SpeciesResolve item3 in input.AlwaysWarm) { SpeciesResolve r2 = item3; if (!flag) { plan.ListDecisions.Add(new ListDecision { Species = r2.Species, Outcome = ListOutcome.SkippedActiveCompanion }); continue; } ListDecision item = DecideListed(in r2); plan.ListDecisions.Add(item); if (item.Outcome == ListOutcome.Warm) { ExpeditionLog.Append(plan.Scenes, item.Scene); } else if (item.Outcome == ListOutcome.DeferredUnvisited) { ExpeditionLog.Append(plan.DeferredScenes, item.Scene); } } } for (int num = plan.DeferredScenes.Count - 1; num >= 0; num--) { foreach (string scene in plan.Scenes) { if (string.Equals(scene, plan.DeferredScenes[num], StringComparison.OrdinalIgnoreCase)) { plan.DeferredScenes.RemoveAt(num); break; } } } return plan; } private static string NeededSceneIgnoringVisited(in SpeciesResolve r) { if (!r.HasExpeditionScenes || r.HasAdditiveDonor || r.HasCachedTemplate || string.IsNullOrEmpty(r.FirstExpeditionScene)) { return null; } return r.FirstExpeditionScene; } public static WarmGate WarmRetryStep(in WarmRetryState s, bool blocked, in WarmRetryPolicy p) { if (!blocked) { return WarmGate.Proceed; } if (s.ElapsedSeconds >= p.MaxWaitSeconds) { return WarmGate.GiveUp; } return WarmGate.Wait; } public static WarmRetryState WarmRetryAdvance(in WarmRetryState s, float pollSeconds) { return new WarmRetryState { ElapsedSeconds = s.ElapsedSeconds + ((pollSeconds > 0f) ? pollSeconds : 0f), Polls = s.Polls + 1 }; } public static int WarmRetryTerminates(in WarmRetryPolicy p, int guardSteps = 100000) { WarmRetryState s = default(WarmRetryState); for (int i = 0; i < guardSteps; i++) { switch (WarmRetryStep(in s, blocked: true, in p)) { case WarmGate.GiveUp: return i; default: return i; case WarmGate.Wait: break; } s = WarmRetryAdvance(in s, p.PollSeconds); } return -1; } } public static class ExpeditionResume { public enum Action { Ignore, RunPayloadThenReturn, Restore, AbortLoud } public struct State { public int Leg; public string ActiveScene; public string HomeScene; public string DonorScene; public bool ReturnRequested; public bool Restoring; public bool PayloadRan; public bool LoaderBusy; } public struct Decision { public Action Action; public string Reason; } public static Decision Decide(State s) { if (s.LoaderBusy) { return Make(Action.Ignore, "a load is still in flight (scene '" + s.ActiveScene + "') — not a landing"); } bool flag = SameScene(s.ActiveScene, s.HomeScene); bool flag2 = SameScene(s.ActiveScene, s.DonorScene); if (s.Leg == 1) { if (flag2) { return Make(Action.RunPayloadThenReturn, s.PayloadRan ? "landed in the donor scene (payload already ran) — heading home" : "landed in the donor scene — running the payload, then heading home"); } if (flag) { return Make(Action.Ignore, "still in the home scene on the outbound leg — the switch was already requested and is in motion; a resume cannot mean 'it never left' (the watchdog covers a dead outbound)"); } return Make(Action.AbortLoud, "outbound landed in unexpected scene '" + s.ActiveScene + "'"); } if (s.Leg == 2) { if (flag) { if (!s.Restoring) { return Make(Action.Restore, "landed home — restoring player position(s)"); } return Make(Action.Ignore, "home, but a restore is already running — duplicate resume"); } if (flag2) { return Make(Action.Ignore, s.ReturnRequested ? "still in the donor scene — the return was requested but its load has not landed yet" : "still in the donor scene — the return-leg dwell has not fired yet"); } return Make(Action.AbortLoud, "return leg landed in unexpected scene '" + s.ActiveScene + "'"); } return Make(Action.Ignore, $"no leg in flight (leg {s.Leg})"); } private static Decision Make(Action a, string why) { return new Decision { Action = a, Reason = why }; } private static bool SameScene(string a, string b) { if (!string.IsNullOrEmpty(a) && !string.IsNullOrEmpty(b)) { return string.Equals(a, b, StringComparison.OrdinalIgnoreCase); } return false; } } public static class ExpeditionVerb { public enum Kind { Status, Delay, DelayInvalid, Target } public struct Command { public Kind Kind; public float DelaySeconds; public string Target; public bool Force; } public const float MaxDelaySeconds = 30f; public static Command Parse(string[] parts) { if (parts == null || parts.Length < 2) { return new Command { Kind = Kind.Status }; } if (parts.Length >= 3 && string.Equals(parts[1], "delay", StringComparison.OrdinalIgnoreCase)) { if (float.TryParse(parts[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && result >= 0f && result <= 30f) { return new Command { Kind = Kind.Delay, DelaySeconds = result }; } return new Command { Kind = Kind.DelayInvalid }; } if (parts.Length >= 3 && string.Equals(parts[1], "force", StringComparison.OrdinalIgnoreCase)) { return new Command { Kind = Kind.Target, Force = true, Target = string.Join(" ", parts, 2, parts.Length - 2) }; } return new Command { Kind = Kind.Target, Target = string.Join(" ", parts, 1, parts.Length - 1) }; } } public static class FacingPolicy { public static bool ForceEnemyHeading(bool hasCombatTarget, bool planted) { if (hasCombatTarget) { return LookTargetPolicy.Choose(hasCombatTarget, planted, stationActive: true, pointingHold: false, loafActive: false, approachActive: false, settled: false) == LookTarget.Enemy; } return false; } public static bool FaceDriveDestination(bool loafActive, bool approachActive) { return LookTargetPolicy.Choose(hasCombat: false, planted: false, stationActive: false, pointingHold: false, loafActive, approachActive, settled: false) == LookTarget.Travel; } public static bool ForcePointHeading(bool pointingHold) { return LookTargetPolicy.Choose(hasCombat: false, planted: false, stationActive: false, pointingHold, loafActive: false, approachActive: false, settled: false) == LookTarget.FacePoint; } } public enum FollowMode { Hold, Direct, Replace, Agent } public enum HoldReason { None, NoTargetOrAgent, VoidStaging, AgentSuspended } public enum ReplaceDestination { Goal, Owner } public sealed class FollowTuning { public float ZoneReplaceDistance = 50f; public float ReplaceThrottleSeconds = 0.4f; public float LeashWarpThrottleSeconds = 0.75f; public float RoofSanityMeters = 1.5f; public float RepathIntervalSeconds = 0.2f; public float RepathHysteresisSq = 0.16f; public float GoalSampleRadius = 2f; public float OwnerSampleRadius = 2f; public float LoafSampleRadius = 2f; public float PlantHysteresisMeters = 1.5f; public float CantReachSlack = 2f; public float CantReachGraceSeconds = 1.5f; public float LeashWarpSearchRadius = 8f; public float LoafReengageGraceSeconds = 1.5f; public float LoafArriveDistance = 0.6f; public float CatchUpNearMeters = 5f; public float CatchUpRampMeters = 4f; public float FollowAngularSpeed = 240f; public float FollowAcceleration = 12f; public float BrakeWithinMeters = 2.5f; public float CombatAngularSpeed = 540f; public float CombatAcceleration = 40f; public float WarpBehindMeters = 12.5f; public float WarpBehindStepMeters = 5f; public float WarpBehindMaxMeters = 22.5f; public float WarpSideMeters = -0.5f; public float OffscreenMargin = 0.08f; public float ApproachFarMeters = 5f; public float ApproachArriveMeters = 0.9f; public float ApproachRefuseEnterMeters = 1.5f; public float ApproachRefuseExitMeters = 2f; public float TurnSpeed = 0.08f; public float StationArriveMeters = 0.8f; } public struct FollowFacts { public bool HasTargetAndAgent; public bool TargetSane; public bool AgentAbandoned; public float DistToOwner; public bool HasCombatTarget; public bool HasFollowOverride; public bool HasSceneSpot; public bool HasStaySpot; public bool GoalIsOwner; public bool GoalOnMesh; public bool AgentOnMesh; public bool AgentEnabled; public bool AgentSuspendedExternally; public float Now; public float LastReplaceAt; public float LeashGraceUntil; public float FaceUntil; public float BaseSpeed; public float FollowSpeedFloor; public float FollowRefDist; public float CatchUpSpeed; public float LeashDistance; public float CombatLeashDistance; public bool SuppressLeashWarp; } public struct FollowDecision { public FollowMode Mode; public HoldReason Hold; public bool PreReplaceOwner; public bool ReplaceNow; public ReplaceDestination ReplaceDest; public bool ReplaceMeasurable; public bool LoafEligible; public float DriveSpeed; } public struct AgentDriveFacts { public bool HasCombatTarget; public float GoalDist; public float AttackRange; public bool WasPlanted; public float Now; public float FaceUntil; public bool LoafActive; public bool ApproachActive; public bool SceneSpotActive; public bool StaySpotActive; public bool HasPath; public float LastRepathAt; public float DestMovedSq; public float LeashDist; public float LeashGraceUntil; public bool PathPending; public bool PathComplete; public float LastWarpAt; public float CantReachSince; public float StopDistance; public float LeashDistance; public float CombatLeashDistance; public bool SuppressLeashWarp; public bool HasStation; public float StationDist; public float StationArriveMeters; } public enum WarpReason { None, Leash, CantReach } public struct AgentDrivePlan { public bool Planted; public bool HoldForCombat; public bool PointingHold; public bool AgentStopped; public float StoppingDistance; public bool Repath; public bool GraceEnded; public bool LeashWarp; public WarpReason LeashWarpReason; public bool CantReachNow; public bool ClearWarpBlocked; } public static class FollowPolicy { public static bool NeedsGoalSample(in FollowFacts f) { if (f.HasTargetAndAgent && f.TargetSane) { return !f.AgentAbandoned; } return false; } public static bool ShouldReArmAgent(in FollowFacts f) { if (f.GoalOnMesh && !f.AgentEnabled) { return !f.AgentSuspendedExternally; } return false; } public static FollowDecision Decide(in FollowFacts f, FollowTuning t) { FollowDecision result = new FollowDecision { DriveSpeed = FollowSpeed(f.FollowRefDist, f.BaseSpeed, f.FollowSpeedFloor, f.CatchUpSpeed, f.HasCombatTarget, t), LoafEligible = (!f.HasCombatTarget && !f.HasFollowOverride && !f.HasSceneSpot && !f.HasStaySpot && f.Now >= f.FaceUntil + t.LoafReengageGraceSeconds) }; if (!f.HasTargetAndAgent) { result.Mode = FollowMode.Hold; result.Hold = HoldReason.NoTargetOrAgent; return result; } if (!f.TargetSane) { result.Mode = FollowMode.Hold; result.Hold = HoldReason.VoidStaging; return result; } if (f.AgentSuspendedExternally) { result.Mode = FollowMode.Hold; result.Hold = HoldReason.AgentSuspended; return result; } bool flag = f.Now < f.LeashGraceUntil; float val = ((f.HasCombatTarget || flag) ? f.CombatLeashDistance : f.LeashDistance); float num = Math.Max(t.ZoneReplaceDistance, val); bool flag2 = f.Now - f.LastReplaceAt > t.ReplaceThrottleSeconds; if (f.AgentAbandoned) { result.Mode = FollowMode.Direct; bool flag3 = flag; bool flag4 = !f.HasCombatTarget && f.DistToOwner > (flag3 ? f.CombatLeashDistance : f.LeashDistance); result.PreReplaceOwner = !f.SuppressLeashWarp && (f.DistToOwner > num || flag4) && flag2; return result; } if (!f.GoalOnMesh) { float num2 = (f.GoalIsOwner ? num : t.ZoneReplaceDistance); result.Mode = FollowMode.Direct; result.PreReplaceOwner = !f.SuppressLeashWarp && f.DistToOwner > num2 && flag2; return result; } bool flag5 = !f.SuppressLeashWarp && f.DistToOwner > num; if (!f.AgentOnMesh || flag5) { result.Mode = FollowMode.Replace; result.ReplaceNow = flag2; result.ReplaceDest = (flag5 ? ReplaceDestination.Owner : ReplaceDestination.Goal); result.ReplaceMeasurable = ReplaceConvergence.IsMeasurable(flag5, f.GoalIsOwner); return result; } result.Mode = FollowMode.Agent; return result; } public static float FollowSpeed(float distToRef, float baseSpeed, float floor, float catchUp, bool hasCombatTarget, FollowTuning t) { if (hasCombatTarget) { return baseSpeed; } float num = Math.Max(baseSpeed, floor); if (catchUp <= 0f) { return num; } float num2 = Math.Max(floor, catchUp); num = Math.Min(num, num2); if (distToRef <= t.CatchUpNearMeters) { return num; } float num3 = Math.Max(t.CatchUpRampMeters, 0.001f); float num4 = Math.Min(1f, (distToRef - t.CatchUpNearMeters) / num3); return num + (num2 - num) * num4; } public static bool ApproachDestination(float ownerX, float ownerZ, bool hasDir, float dirX, float dirZ, float distToOwner, float behindMeters, FollowTuning t, out float x, out float z) { return ApproachDestination(ownerX, ownerZ, hasDir, dirX, dirZ, distToOwner, behindMeters, float.NaN, float.NaN, wasApproaching: false, t, out x, out z); } public static bool ApproachDestination(float ownerX, float ownerZ, bool hasDir, float dirX, float dirZ, float distToOwner, float behindMeters, float petX, float petZ, bool wasApproaching, FollowTuning t, out float x, out float z) { bool refused; return ApproachDestination(ownerX, ownerZ, hasDir, dirX, dirZ, distToOwner, behindMeters, petX, petZ, wasApproaching, wasRefused: false, t, out x, out z, out refused); } public static bool ApproachDestination(float ownerX, float ownerZ, bool hasDir, float dirX, float dirZ, float distToOwner, float behindMeters, float petX, float petZ, bool wasApproaching, bool wasRefused, FollowTuning t, out float x, out float z, out bool refused) { x = ownerX; z = ownerZ; refused = false; if (!hasDir || behindMeters <= 0f) { return false; } float num = (float)Math.Sqrt(dirX * dirX + dirZ * dirZ); if (num < 1E-05f) { return false; } x = ownerX - dirX / num * behindMeters; z = ownerZ - dirZ / num * behindMeters; if (!float.IsNaN(petX) && !float.IsNaN(petZ)) { float num2 = x - petX; float num3 = z - petZ; float num4 = ownerX - petX; float num5 = ownerZ - petZ; float num6 = num2 * num2 + num3 * num3; if (num6 > 1E-06f) { float num7 = (num4 * num2 + num5 * num3) / num6; float num8 = petX + num2 * num7; float num9 = petZ + num3 * num7; float num10 = (ownerX - num8) * (ownerX - num8) + (ownerZ - num9) * (ownerZ - num9); float num11 = (wasRefused ? t.ApproachRefuseExitMeters : t.ApproachRefuseEnterMeters); if (num7 > 0f && num7 < 1f && num10 < num11 * num11) { refused = true; return false; } } if (wasApproaching) { float num12 = x - petX; float num13 = z - petZ; return num12 * num12 + num13 * num13 > t.ApproachArriveMeters * t.ApproachArriveMeters; } } return distToOwner > t.ApproachFarMeters; } public static float OwnerScaledSpeed(float speed, float ownerMultiplier) { if (float.IsNaN(ownerMultiplier) || float.IsInfinity(ownerMultiplier) || ownerMultiplier < 1f) { ownerMultiplier = 1f; } return speed * ownerMultiplier; } public static void AgentDynamics(bool hasCombatTarget, FollowTuning t, out float angularSpeed, out float acceleration) { AgentDynamics(hasCombatTarget, 0f, t, out angularSpeed, out acceleration); } public static void AgentDynamics(bool hasCombatTarget, float driveSpeed, FollowTuning t, out float angularSpeed, out float acceleration) { angularSpeed = (hasCombatTarget ? t.CombatAngularSpeed : t.FollowAngularSpeed); acceleration = (hasCombatTarget ? t.CombatAcceleration : t.FollowAcceleration); if (!hasCombatTarget && driveSpeed > 0f && t.BrakeWithinMeters > 0f) { acceleration = Math.Max(acceleration, driveSpeed * driveSpeed / (2f * t.BrakeWithinMeters)); } } public static void WarpOffset(float fx, float fz, float rx, float rz, FollowTuning t, out float ox, out float oz) { WarpOffset(fx, fz, rx, rz, t.WarpBehindMeters, t, out ox, out oz); } public static int WarpBehindLadder(FollowTuning t, float[] into) { int num = 0; for (float num2 = t.WarpBehindMeters; num2 <= t.WarpBehindMaxMeters + 0.001f; num2 += Math.Max(t.WarpBehindStepMeters, 0.5f)) { if (num >= into.Length) { break; } into[num++] = num2; } return num; } public static bool IsOffscreen(float vx, float vy, float vz, FollowTuning t) { if (!(vz <= 0f) && !(vx < 0f - t.OffscreenMargin) && !(vx > 1f + t.OffscreenMargin) && !(vy < 0f - t.OffscreenMargin)) { return vy > 1f + t.OffscreenMargin; } return true; } public static void WarpOffset(float fx, float fz, float rx, float rz, float behind, FollowTuning t, out float ox, out float oz) { ox = (oz = 0f); float num = (float)Math.Sqrt(fx * fx + fz * fz); if (!(num < 0.0001f)) { fx /= num; fz /= num; float num2 = (float)Math.Sqrt(rx * rx + rz * rz); if (num2 < 0.0001f) { rx = fz; rz = 0f - fx; num2 = 1f; } rx /= num2; rz /= num2; ox = (0f - fx) * behind + rx * t.WarpSideMeters; oz = (0f - fz) * behind + rz * t.WarpSideMeters; } } public static AgentDrivePlan PlanAgentDrive(in AgentDriveFacts f, FollowTuning t) { AgentDrivePlan result = new AgentDrivePlan { Planted = f.WasPlanted }; if (f.HasCombatTarget && f.HasStation) { float num = ((f.StationArriveMeters > 0f) ? f.StationArriveMeters : t.StationArriveMeters); if (f.StationDist <= num) { result.Planted = true; } else if (f.StationDist > num + t.PlantHysteresisMeters) { result.Planted = false; } } else if (f.HasCombatTarget && f.GoalDist <= f.AttackRange) { result.Planted = true; } else if (!f.HasCombatTarget || f.GoalDist > f.AttackRange + t.PlantHysteresisMeters) { result.Planted = false; } result.HoldForCombat = f.HasCombatTarget && result.Planted; result.PointingHold = !f.HasCombatTarget && f.Now < f.FaceUntil; result.AgentStopped = result.HoldForCombat || result.PointingHold; result.StoppingDistance = ((f.LoafActive || f.SceneSpotActive || f.ApproachActive || f.StaySpotActive || (f.HasCombatTarget && f.HasStation)) ? t.LoafArriveDistance : f.StopDistance); result.Repath = !result.HoldForCombat && f.Now - f.LastRepathAt > t.RepathIntervalSeconds && (!f.HasPath || f.DestMovedSq > t.RepathHysteresisSq); bool flag = !f.HasCombatTarget && f.Now < f.LeashGraceUntil; if (flag && f.LeashDist <= f.LeashDistance) { result.GraceEnded = true; flag = false; } float num2 = ((f.HasCombatTarget || flag) ? f.CombatLeashDistance : f.LeashDistance); bool flag2 = (result.CantReachNow = !f.PathPending && !f.PathComplete); bool flag3 = flag2 && f.CantReachSince >= 0f && f.Now - f.CantReachSince >= t.CantReachGraceSeconds; bool flag4 = f.LeashDist > num2; bool flag5 = !f.HasCombatTarget && !flag && !f.StaySpotActive && flag3 && f.LeashDist > f.StopDistance + t.CantReachSlack; bool flag6 = (flag4 || flag5) && f.Now - f.LastWarpAt > t.LeashWarpThrottleSeconds; result.LeashWarp = flag6 && !f.SuppressLeashWarp; result.LeashWarpReason = (result.LeashWarp ? (flag4 ? WarpReason.Leash : WarpReason.CantReach) : WarpReason.None); result.ClearWarpBlocked = !flag6 && f.LeashDist <= num2 && !flag2; return result; } } public enum GuardEndResult { Closed, AlreadyClosed, StaleToken } public sealed class GuardWindow { private int _nextToken; public double ExpirySeconds { get; } public int Owner { get; private set; } public double ExpireAt { get; private set; } public GuardWindow(double expirySeconds) { ExpirySeconds = expirySeconds; } public bool Active(double now) { if (Owner != 0) { return now < ExpireAt; } return false; } public bool Expired(double now) { if (Owner != 0) { return now >= ExpireAt; } return false; } public int Begin(double now) { Owner = ++_nextToken; ExpireAt = now + ExpirySeconds; return Owner; } public bool Refresh(int token, double now) { if (token == 0 || token != Owner) { return false; } ExpireAt = now + ExpirySeconds; return true; } public GuardEndResult End(int token) { if (Owner == 0) { return GuardEndResult.AlreadyClosed; } if (token != Owner) { return GuardEndResult.StaleToken; } Owner = 0; return GuardEndResult.Closed; } } public sealed class HarvestPacing { public const float DefaultRetryCooldownSeconds = 60f; public const int DefaultMaxCyclesPerSession = 9; public const int DefaultMaxCooldownRearmsPerEpisode = 3; private float _nextAt; private string _scene; private int _rearms; private bool _parkWarned; public float RetryCooldownSeconds { get; } public int MaxCyclesPerSession { get; } public int MaxCooldownRearmsPerEpisode { get; } public bool HasAttempted => _scene != null; public string LastScene => _scene; public HarvestPacing(float retryCooldownSeconds = 60f, int maxCyclesPerSession = 9, int maxCooldownRearmsPerEpisode = 3) { RetryCooldownSeconds = retryCooldownSeconds; MaxCyclesPerSession = maxCyclesPerSession; MaxCooldownRearmsPerEpisode = maxCooldownRearmsPerEpisode; } public bool TryEarlyRearm(string sceneNow, float now) { if (_scene == null || SameScene(sceneNow, _scene) || now >= _nextAt) { return false; } _nextAt = 0f; _rearms = 0; _parkWarned = false; return true; } public bool IsCooldownRearm(string sceneNow) { if (_scene != null) { return SameScene(sceneNow, _scene); } return false; } private static bool SameScene(string a, string b) { return string.Equals(a, b, StringComparison.OrdinalIgnoreCase); } public bool Allowed(int cyclesThisSession, string sceneNow) { if (cyclesThisSession < MaxCyclesPerSession) { if (IsCooldownRearm(sceneNow)) { return _rearms < MaxCooldownRearmsPerEpisode; } return true; } return false; } public bool TryPark(bool allowed, bool wantsBody, float now) { if (allowed || _parkWarned || !wantsBody || now < _nextAt) { return false; } _parkWarned = true; _nextAt = float.MaxValue; return true; } public bool ReadyToAttempt(bool allowed, bool wantsBody, float now) { if (allowed && wantsBody) { return now >= _nextAt; } return false; } public void NoteAttempt(string sceneNow, float now) { if (IsCooldownRearm(sceneNow)) { _rearms++; } _nextAt = now + RetryCooldownSeconds; _scene = sceneNow; } } public static class HostilityRepair { public const float DefaultCooldownSeconds = 5f; public const float CheckerSilentSeconds = 2f; public static bool IsStale(bool exists, bool alive) { if (exists) { return !alive; } return true; } public static bool CheckerWedged(bool coroutineLive, int engagedCount, float now, float lastSignalAt, float silentSeconds) { if (!coroutineLive) { return false; } if (engagedCount <= 0) { return false; } if (lastSignalAt < 0f) { return false; } if (now < lastSignalAt) { return false; } return now - lastSignalAt >= silentSeconds; } public static int OverhangToTrim(int parallelCount, int checkingCount) { if (parallelCount <= checkingCount) { return 0; } return parallelCount - checkingCount; } public static bool RepairDue(float now, float lastRepairAt, float cooldownSeconds) { if (lastRepairAt < 0f) { return true; } if (cooldownSeconds <= 0f) { return true; } if (now < lastRepairAt) { return true; } return now - lastRepairAt >= cooldownSeconds; } } public struct IdleFacingDecision { public bool ReAim; public float DirX; public float DirZ; public float TurnRateDegPerSec; } public sealed class IdleFacingPolicy { public const float DefaultEngageAngleDeg = 40f; public const float DefaultReleaseAngleDeg = 8f; public const float DefaultDeadZone = 1f; public const float DefaultTurnRateDegPerSec = 120f; private bool _reAiming; public bool ReAiming => _reAiming; public void Reset() { _reAiming = false; } public IdleFacingDecision Decide(float heldX, float heldZ, float toOwnerX, float toOwnerZ, float engageAngleDeg = 40f, float releaseAngleDeg = 8f, float deadZone = 1f, float turnRateDegPerSec = 120f) { double num = Math.Sqrt((double)toOwnerX * (double)toOwnerX + (double)toOwnerZ * (double)toOwnerZ); if (num < (double)deadZone || num < 1E-06) { _reAiming = false; return default(IdleFacingDecision); } float num2 = (float)((double)toOwnerX / num); float num3 = (float)((double)toOwnerZ / num); double num4 = Math.Sqrt((double)heldX * (double)heldX + (double)heldZ * (double)heldZ); if (num4 < 1E-06) { _reAiming = true; return new IdleFacingDecision { ReAim = true, DirX = num2, DirZ = num3, TurnRateDegPerSec = turnRateDegPerSec }; } float num5 = AngleBetweenDeg((float)((double)heldX / num4), (float)((double)heldZ / num4), num2, num3); if (_reAiming) { if (num5 <= releaseAngleDeg) { _reAiming = false; } } else if (num5 >= engageAngleDeg) { _reAiming = true; } return new IdleFacingDecision { ReAim = _reAiming, DirX = num2, DirZ = num3, TurnRateDegPerSec = turnRateDegPerSec }; } public static float AngleBetweenDeg(float ax, float az, float bx, float bz) { double num = (double)ax * (double)bx + (double)az * (double)bz; if (num > 1.0) { num = 1.0; } else if (num < -1.0) { num = -1.0; } return (float)(Math.Acos(num) * 180.0 / Math.PI); } } public readonly struct ItemKey { public string Key { get; } public int? ItemId { get; } public ItemKey(string key, int? itemId) { Key = key; ItemId = itemId; } public static bool TryRead(object? jsonValue, out ItemKey key) { if (jsonValue is double num) { if (double.IsNaN(num) || num > 2147483647.0 || num < -2147483648.0) { key = default(ItemKey); return false; } int value = (int)num; key = new ItemKey(value.ToString(CultureInfo.InvariantCulture), value); return true; } if (jsonValue is string text) { string text2 = text.Trim(); if (text2.Length > 0) { int? itemId = null; if (int.TryParse(text2, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { itemId = result; } key = new ItemKey(text2, itemId); return true; } } key = default(ItemKey); return false; } public bool Matches(int liveId, string? liveName) { if (ItemId.HasValue) { return ItemId.Value == liveId; } if (!string.IsNullOrEmpty(liveName)) { return string.Equals(Key, liveName.Trim(), StringComparison.OrdinalIgnoreCase); } return false; } } public static class Json { private const int MaxDepth = 64; public static object Parse(string text, Action onDuplicateKey = null) { if (text == null) { throw new FormatException("json: input is null."); } int pos = 0; object result = ParseValue(text, ref pos, 0, onDuplicateKey); SkipWhitespace(text, ref pos); if (pos != text.Length) { throw Error(text, pos, "trailing content after the document"); } return result; } private static object ParseValue(string s, ref int pos, int depth, Action onDup) { SkipWhitespace(s, ref pos); if (pos >= s.Length) { throw Error(s, pos, "unexpected end of input"); } char c = s[pos]; switch (c) { case '{': return ParseObject(s, ref pos, depth, onDup); case '[': return ParseArray(s, ref pos, depth, onDup); case '"': return ParseString(s, ref pos); case 't': return ParseLiteral(s, ref pos, "true", true); case 'f': return ParseLiteral(s, ref pos, "false", false); case 'n': return ParseLiteral(s, ref pos, "null", null); default: switch (c) { case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return ParseNumber(s, ref pos); default: throw Error(s, pos, $"unexpected character '{c}'"); } } } private static Dictionary ParseObject(string s, ref int pos, int depth, Action onDup) { if (depth >= 64) { throw Error(s, pos, $"maximum nesting depth ({64}) exceeded"); } pos++; Dictionary dictionary = new Dictionary(StringComparer.Ordinal); SkipWhitespace(s, ref pos); if (pos < s.Length && s[pos] == '}') { pos++; return dictionary; } while (true) { SkipWhitespace(s, ref pos); if (pos >= s.Length || s[pos] != '"') { throw Error(s, pos, "expected a quoted object key"); } string text = ParseString(s, ref pos); SkipWhitespace(s, ref pos); if (pos >= s.Length || s[pos] != ':') { throw Error(s, pos, "expected ':' after object key"); } pos++; if (onDup != null && dictionary.ContainsKey(text)) { onDup(text); } dictionary[text] = ParseValue(s, ref pos, depth + 1, onDup); SkipWhitespace(s, ref pos); if (pos >= s.Length) { throw Error(s, pos, "unterminated object"); } if (s[pos] != ',') { break; } pos++; } if (s[pos] == '}') { pos++; return dictionary; } throw Error(s, pos, "expected ',' or '}' in object"); } private static List ParseArray(string s, ref int pos, int depth, Action onDup) { if (depth >= 64) { throw Error(s, pos, $"maximum nesting depth ({64}) exceeded"); } pos++; List list = new List(); SkipWhitespace(s, ref pos); if (pos < s.Length && s[pos] == ']') { pos++; return list; } while (true) { list.Add(ParseValue(s, ref pos, depth + 1, onDup)); SkipWhitespace(s, ref pos); if (pos >= s.Length) { throw Error(s, pos, "unterminated array"); } if (s[pos] != ',') { break; } pos++; } if (s[pos] == ']') { pos++; return list; } throw Error(s, pos, "expected ',' or ']' in array"); } private static string ParseString(string s, ref int pos) { pos++; StringBuilder stringBuilder = new StringBuilder(); while (pos < s.Length) { char c = s[pos++]; switch (c) { case '"': return stringBuilder.ToString(); default: stringBuilder.Append(c); break; case '\\': { if (pos >= s.Length) { throw Error(s, pos, "unterminated escape"); } char c2 = s[pos++]; switch (c2) { case '"': stringBuilder.Append('"'); break; case '\\': stringBuilder.Append('\\'); break; case '/': stringBuilder.Append('/'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; case 'u': { if (pos + 4 > s.Length) { throw Error(s, pos, "truncated \\u escape"); } if (!ushort.TryParse(s.Substring(pos, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result)) { throw Error(s, pos, "invalid \\u escape"); } stringBuilder.Append((char)result); pos += 4; break; } default: throw Error(s, pos - 1, $"invalid escape '\\{c2}'"); } break; } } } throw Error(s, pos, "unterminated string"); } private static object ParseNumber(string s, ref int pos) { int num = pos; if (pos < s.Length && s[pos] == '-') { pos++; } while (pos < s.Length && s[pos] >= '0' && s[pos] <= '9') { pos++; } if (pos < s.Length && s[pos] == '.') { pos++; while (pos < s.Length && s[pos] >= '0' && s[pos] <= '9') { pos++; } } if (pos < s.Length && (s[pos] == 'e' || s[pos] == 'E')) { pos++; if (pos < s.Length && (s[pos] == '+' || s[pos] == '-')) { pos++; } while (pos < s.Length && s[pos] >= '0' && s[pos] <= '9') { pos++; } } string text = s.Substring(num, pos - num); if (!double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { throw Error(s, num, "invalid number '" + text + "'"); } return result; } private static object ParseLiteral(string s, ref int pos, string literal, object value) { if (pos + literal.Length > s.Length || string.CompareOrdinal(s, pos, literal, 0, literal.Length) != 0) { throw Error(s, pos, "expected '" + literal + "'"); } pos += literal.Length; return value; } private static void SkipWhitespace(string s, ref int pos) { while (pos < s.Length) { switch (s[pos]) { case '\t': case '\n': case '\r': case ' ': pos++; break; case '/': if (pos + 1 < s.Length && s[pos + 1] == '/') { while (pos < s.Length && s[pos] != '\n') { pos++; } break; } return; default: return; } } } private static FormatException Error(string s, int pos, string message) { int num = 1; int num2 = 1; for (int i = 0; i < pos && i < s.Length; i++) { if (s[i] == '\n') { num++; num2 = 1; } else { num2++; } } return new FormatException($"json: {message} (line {num}, col {num2})."); } } public struct LoafPoint { public bool Active; public float X; public float Z; } public sealed class LoafTracker { public const float DefaultMinDistance = 2f; public const float DefaultMaxDistance = 4f; public const float DefaultRepickDistance = 3f; public const float EngageSpeed = 0.3f; public const float DisengageSpeed = 1f; public const float EngageDelaySeconds = 0.75f; public const float SpeedSmoothingSeconds = 0.25f; private bool _has; private float _anchorX; private float _anchorZ; private float _pointX; private float _pointZ; private int _seq; private bool _engaged; private bool _hasPrev; private float _prevX; private float _prevZ; private float _smoothedSpeed; private float _dirX; private float _dirZ; private bool _hasDir; public const float MotionDirSpeed = 1f; private float _stillFor; public bool HasMotionDir => _hasDir; public float MotionDirX => _dirX; public float MotionDirZ => _dirZ; public bool HasPoint => _has; public bool Engaged => _engaged; public void Clear() { _has = false; _seq = 0; _engaged = false; _hasPrev = false; _smoothedSpeed = 0f; _stillFor = 0f; _hasDir = false; } public void Reject() { _has = false; } public LoafPoint Resolve(float ownerX, float ownerZ, float ownerHeadingX, float ownerHeadingZ, float dtSeconds, float minDist, float maxDist, float repickDistance) { return Resolve(ownerX, ownerZ, ownerHeadingX, ownerHeadingZ, dtSeconds, minDist, maxDist, repickDistance, float.NaN, float.NaN); } public LoafPoint Resolve(float ownerX, float ownerZ, float ownerHeadingX, float ownerHeadingZ, float dtSeconds, float minDist, float maxDist, float repickDistance, float petX, float petZ) { if (maxDist <= 0f) { Clear(); return default(LoafPoint); } float num = ((minDist < 0f) ? 0f : minDist); float num2 = ((maxDist < num) ? num : maxDist); float num3 = ((repickDistance <= 0f) ? 3f : repickDistance); if (dtSeconds > 0f) { if (_hasPrev) { float num4 = ownerX - _prevX; float num5 = ownerZ - _prevZ; float num6 = (float)Math.Sqrt(num4 * num4 + num5 * num5); float num7 = num6 / dtSeconds; float num8 = dtSeconds / (dtSeconds + 0.25f); _smoothedSpeed += (num7 - _smoothedSpeed) * num8; if (num7 >= 1f && num6 > 1E-05f) { _dirX = num4 / num6; _dirZ = num5 / num6; _hasDir = true; } } _prevX = ownerX; _prevZ = ownerZ; _hasPrev = true; if (_smoothedSpeed <= 0.3f) { _stillFor += dtSeconds; if (!_engaged && _stillFor >= 0.75f) { _engaged = true; } } else { _stillFor = 0f; if (_engaged && _smoothedSpeed >= 1f) { _engaged = false; _has = false; } } } if (!_engaged) { return default(LoafPoint); } bool flag = !_has; if (_has) { float num9 = ownerX - _anchorX; float num10 = ownerZ - _anchorZ; if (num9 * num9 + num10 * num10 > num3 * num3) { flag = true; } } if (flag) { float num11 = petX - ownerX; float num12 = petZ - ownerZ; float num13 = num11 * num11 + num12 * num12; if (!float.IsNaN(petX) && !float.IsNaN(petZ) && num13 >= num * num && num13 <= num2 * num2) { _pointX = petX; _pointZ = petZ; } else { Pick(ownerX, ownerZ, ownerHeadingX, ownerHeadingZ, _seq, num, num2, out _pointX, out _pointZ); } _seq++; _anchorX = ownerX; _anchorZ = ownerZ; _has = true; } return new LoafPoint { Active = true, X = _pointX, Z = _pointZ }; } public static void Pick(float ownerX, float ownerZ, float ownerHeadingX, float ownerHeadingZ, int seq, float minDist, float maxDist, out float x, out float z) { uint num = Hash((uint)seq); float num2 = (float)(num & 0xFFFF) / 65535f; float num3 = (float)((num >> 16) & 0xFFFF) / 65535f; float num4 = minDist + (maxDist - minDist) * num3; double num5 = Math.Sqrt((double)ownerHeadingX * (double)ownerHeadingX + (double)ownerHeadingZ * (double)ownerHeadingZ); double num9; double num10; if (num5 > 1E-06) { double num6 = Math.Atan2((double)(0f - ownerHeadingZ) / num5, (double)(0f - ownerHeadingX) / num5); double num7 = ((double)num2 - 0.5) * Math.PI; double num8 = num6 + num7; num9 = Math.Cos(num8); num10 = Math.Sin(num8); } else { double num11 = (double)num2 * 2.0 * Math.PI; num9 = Math.Cos(num11); num10 = Math.Sin(num11); } x = ownerX + (float)(num9 * (double)num4); z = ownerZ + (float)(num10 * (double)num4); } private static uint Hash(uint v) { v ^= 0xA3C59AC3u; v *= 2654435769u; v ^= v >> 16; v *= 2654435769u; v ^= v >> 16; v *= 2654435769u; return v; } } public static class LocoFrame { public struct Blend { public float Forward; public float Side; public Blend(float forward, float side) { Forward = forward; Side = side; } } public const float DefaultLerpPerSec = 1.5f; public const float DefaultMovePerSec = 0.5f; public static Blend Project(float vx, float vz, float fx, float fz, bool moving) { if (!moving) { return new Blend(0f, 0f); } double num = Math.Sqrt((double)fx * (double)fx + (double)fz * (double)fz); if (num < 1E-06) { return new Blend(0f, 0f); } float num2 = (float)((double)fx / num); float num3 = (float)((double)fz / num); float v = vx * num2 + vz * num3; float v2 = vx * num3 - vz * num2; return new Blend(Clamp(v), Clamp(v2)); } public static Blend ProjectDisplacement(float dx, float dz, float dt, float fx, float fz, bool moving) { if (dt <= 1E-05f) { return new Blend(0f, 0f); } return Project(dx / dt, dz / dt, fx, fz, moving); } public static float SmoothToward(float current, float target, float dt, float lerpPerSec, float movePerSec) { if (dt <= 0f) { return current; } float num = lerpPerSec * dt; if (num > 1f) { num = 1f; } if (num < 0f) { num = 0f; } float num2 = current + (target - current) * num; float num3 = movePerSec * dt; if (num3 < 0f) { num3 = 0f; } float num4 = target - num2; if (num4 > num3) { return num2 + num3; } if (num4 < 0f - num3) { return num2 - num3; } return target; } private static float Clamp(float v) { float num = 8f; if (v > num) { return num; } if (v < 0f - num) { return 0f - num; } return v; } } public enum LookTarget { Travel, Owner, Enemy, FacePoint, OwnerHeading } public static class LookTargetPolicy { public static LookTarget Choose(bool hasCombat, bool planted, bool stationActive, bool pointingHold, bool loafActive, bool approachActive, bool settled) { if (hasCombat) { if (planted) { return LookTarget.Enemy; } if (stationActive) { return LookTarget.Travel; } return LookTarget.Enemy; } if (pointingHold) { return LookTarget.FacePoint; } if (settled) { return LookTarget.OwnerHeading; } if (loafActive || approachActive) { return LookTarget.Travel; } return LookTarget.Owner; } public static bool HoldLastFacing(LookTarget target, bool moving) { if (target == LookTarget.Travel) { return !moving; } return false; } } public static class Scenes { public static bool IsGameplay(string sceneName) { if (!string.IsNullOrEmpty(sceneName) && !sceneName.StartsWith("MainMenu", StringComparison.OrdinalIgnoreCase) && sceneName.IndexOf("Transition", StringComparison.OrdinalIgnoreCase) < 0) { return sceneName.IndexOf("LowMemory", StringComparison.OrdinalIgnoreCase) < 0; } return false; } public static bool IsMainMenu(string sceneName) { if (!string.IsNullOrEmpty(sceneName)) { return sceneName.StartsWith("MainMenu", StringComparison.OrdinalIgnoreCase); } return false; } } public static class SaveNaming { public static string SanitizeKey(string uid) { string text = new string((uid ?? "").Where(char.IsLetterOrDigit).ToArray()); if (text.Length != 0) { return text; } return "global"; } public static string FileName(string prefix, string uid) { return prefix + SanitizeKey(uid) + ".txt"; } } public static class Species { public static bool NameMatches(string creatureName, string filter) { if (string.IsNullOrEmpty(filter)) { return true; } if (string.IsNullOrEmpty(creatureName)) { return false; } return CultureInfo.InvariantCulture.CompareInfo.IndexOf(creatureName, filter, CompareOptions.IgnoreCase) >= 0; } public static bool NameEquals(string creatureName, string speciesId) { if (!string.IsNullOrEmpty(creatureName) && !string.IsNullOrEmpty(speciesId)) { return string.Equals(creatureName, speciesId, StringComparison.OrdinalIgnoreCase); } return false; } public static int MatchRank(string creatureName, string filter) { if (!NameMatches(creatureName, filter)) { return 0; } if (!NameEquals(creatureName, filter)) { return 1; } return 2; } } public sealed class MovementGate { public float Tau = 0.2f; public float EnterSpeed = 0.1f; public float ExitSpeed = 0.04f; public float TeleportDistance = 2f; private float _emaSpeed; private bool _moving; public float SmoothedSpeed => _emaSpeed; public bool DisplacingBelief => _moving; public bool Evaluate(float dx, float dz, float dt, bool claimedMoving) { if (dt > 0.0001f) { float num = (float)Math.Sqrt((double)dx * (double)dx + (double)dz * (double)dz); if (num > TeleportDistance) { _emaSpeed = 0f; _moving = false; } else { float num2 = num / dt; float num3 = dt / ((Tau > 0.001f) ? Tau : 0.001f); if (num3 > 1f) { num3 = 1f; } _emaSpeed += (num2 - _emaSpeed) * num3; } } if (_moving) { if (_emaSpeed < ExitSpeed) { _moving = false; } } else if (_emaSpeed >= EnterSpeed) { _moving = true; } if (claimedMoving) { return _moving; } return false; } public void Reset() { _emaSpeed = 0f; _moving = false; } } public static class NetProtocol { public enum SetHealthKind { Full, Drain, Set } public enum PosParseError { None, Unparseable, Insane } public const int Version = 1; private const string VersionPrefix = "v"; public const int TierMin = 0; public const int TierMax = 8; public const float TauntSecondsCap = 60f; public const float DrainAmountCap = 1000f; public const float MaxHealthCeiling = 100000f; public const string ReleasePayload = ""; public const int StatusListMax = 32; public const string BandageAckOk = "ok"; public const string BandageAckNoAnchor = "no-anchor"; public const string BandageAckDisabled = "disabled"; public const string BandageAckUnresolved = "unresolved"; public const string SetHealthFullPayload = "full"; private const string DrainPrefix = "drain "; public const string SetHealthSetPrefix = "set "; public const string StancePassivePayload = "passive"; public const string StanceEngagedPayload = "engaged"; public const int SwingTypeMax = 9; public const string ProxyPetFxActive = "1"; public const string ProxyPetFxInactive = "0"; public static string Sanitize(string s) { if (!string.IsNullOrEmpty(s)) { return s.Replace("|", "/").Replace(";", "/").Replace("=", "/"); } return ""; } public static string BuildHandshake(IReadOnlyDictionary fragments) { List list = new List(fragments.Keys); list.Sort(StringComparer.Ordinal); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("v").Append(1).Append('|'); for (int i = 0; i < list.Count; i++) { if (i > 0) { stringBuilder.Append(';'); } stringBuilder.Append(Sanitize(list[i])).Append('=').Append(Sanitize(fragments[list[i]])); } return stringBuilder.ToString(); } public static bool TryParseHandshake(string payload, out int protocol, out Dictionary fragments) { protocol = 0; fragments = null; if (string.IsNullOrEmpty(payload)) { return false; } int num = payload.IndexOf('|'); if (num <= "v".Length || !payload.StartsWith("v", StringComparison.Ordinal)) { return false; } if (!int.TryParse(payload.Substring("v".Length, num - "v".Length), out protocol) || protocol <= 0) { return false; } Dictionary dictionary = new Dictionary(); string text = payload.Substring(num + 1); if (text.Length > 0) { string[] array = text.Split(new char[1] { ';' }); foreach (string text2 in array) { int num2 = text2.IndexOf('='); if (num2 <= 0) { return false; } dictionary[text2.Substring(0, num2)] = text2.Substring(num2 + 1); } } fragments = dictionary; return true; } public static List Diff(IReadOnlyDictionary local, IReadOnlyDictionary remote, int localProtocol = 1, int remoteProtocol = 1) { List list = new List(); if (localProtocol != remoteProtocol) { list.Add($"protocol v{localProtocol} (local) vs v{remoteProtocol} (remote)"); } SortedSet sortedSet = new SortedSet(StringComparer.Ordinal); foreach (string key in local.Keys) { sortedSet.Add(key); } foreach (string key2 in remote.Keys) { sortedSet.Add(key2); } foreach (string item in sortedSet) { string value; bool num = local.TryGetValue(item, out value); string value2; bool flag = remote.TryGetValue(item, out value2); if (!num) { list.Add(item + ": (absent locally) vs '" + value2 + "'"); } else if (!flag) { list.Add(item + ": '" + value + "' vs (absent remotely)"); } else if (!string.Equals(value, value2, StringComparison.Ordinal)) { list.Add(item + ": '" + value + "' vs '" + value2 + "'"); } } return list; } private static string SanitizeField(string s) { if (!string.IsNullOrEmpty(s)) { return s.Replace('\t', ' '); } return ""; } public static string BuildAnnounce(string species, int tier) { return SanitizeField(species) + "\t" + ClampTier(tier).ToString(CultureInfo.InvariantCulture); } public static string BuildAnnounce(string species, int tier, double healthFraction) { return BuildAnnounce(species, tier) + "\t" + ClampFraction(healthFraction).ToString("0.###", CultureInfo.InvariantCulture); } public static string BuildAnnounce(string species, int tier, double healthFraction, string displayName) { string text = BuildAnnounce(species, tier, healthFraction); string text2 = SanitizeField(displayName); if (text2.Length != 0) { return text + "\t" + text2; } return text; } public static void ParseAnnounce(string payload, out string species, out int tier) { ParseAnnounce(payload, out species, out tier, out var _); } public static void ParseAnnounce(string payload, out string species, out int tier, out double healthFraction, out string displayName) { ParseAnnounce(payload, out species, out tier, out healthFraction); displayName = ""; if (!string.IsNullOrEmpty(payload)) { string[] array = payload.Split(new char[1] { '\t' }); if (array.Length > 3) { displayName = array[3]; } } } public static void ParseAnnounce(string payload, out string species, out int tier, out double healthFraction) { species = ""; tier = 0; healthFraction = 1.0; if (!string.IsNullOrEmpty(payload)) { string[] array = payload.Split(new char[1] { '\t' }); species = array[0]; if (array.Length > 1 && int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { tier = ClampTier(result); } if (array.Length > 2 && double.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && !double.IsNaN(result2) && !double.IsInfinity(result2)) { healthFraction = ClampFraction(result2); } } } private static double ClampFraction(double f) { if (!double.IsNaN(f) && !double.IsInfinity(f)) { if (!(f < 0.0)) { if (!(f > 1.0)) { return f; } return 1.0; } return 0.0; } return 1.0; } public static int ClampTier(int tier) { if (tier >= 0) { if (tier <= 8) { return tier; } return 8; } return 0; } public static string BuildStats(float maxHealth, string flattenedAttributes) { return maxHealth.ToString("R", CultureInfo.InvariantCulture) + "\t" + flattenedAttributes; } public static bool TryParseStats(string payload, out float maxHealth, out string flattened) { maxHealth = 0f; flattened = ""; if (string.IsNullOrEmpty(payload)) { return false; } int num = payload.IndexOf('\t'); if (num < 0) { return false; } if (!TryParseFinite(payload.Substring(0, num), out maxHealth) || maxHealth < 0f) { return false; } if (maxHealth > 100000f) { maxHealth = 100000f; } flattened = payload.Substring(num + 1); return true; } public static string BuildHealth(float current, float max) { return current.ToString("R", CultureInfo.InvariantCulture) + "\t" + max.ToString("R", CultureInfo.InvariantCulture); } public static bool TryParseHealth(string payload, out float current, out float max) { current = 0f; max = 0f; if (string.IsNullOrEmpty(payload)) { return false; } int num = payload.IndexOf('\t'); if (num < 0) { return false; } if (!TryParseFinite(payload.Substring(0, num), out current) || current < 0f) { current = 0f; return false; } if (!TryParseFinite(payload.Substring(num + 1), out max) || max < 0f) { max = 0f; return false; } if (max > 100000f) { max = 100000f; } if (current > max) { current = max; } return true; } public static string BuildStatusList(IEnumerable names) { if (names == null) { return ""; } List list = new List(); foreach (string name in names) { string text = SanitizeField(name); if (text.Length > 0) { list.Add(text); } } list.Sort(StringComparer.Ordinal); if (list.Count > 32) { list.RemoveRange(32, list.Count - 32); } return string.Join("\t", list.ToArray()); } public static List ParseStatusList(string payload) { List list = new List(); if (string.IsNullOrEmpty(payload)) { return list; } string[] array = payload.Split(new char[1] { '\t' }); foreach (string text in array) { if (text.Length != 0) { list.Add(text); if (list.Count >= 32) { break; } } } return list; } public static string BuildStatusRequest(string targetUid, string status, float buildupPercent) { return SanitizeField(targetUid) + "\t" + SanitizeField(status) + "\t" + buildupPercent.ToString("R", CultureInfo.InvariantCulture); } public static bool TryParseStatusRequest(string payload, out string targetUid, out string status, out float buildupPercent) { targetUid = ""; status = ""; buildupPercent = 0f; string[] array = (payload ?? "").Split(new char[1] { '\t' }); if (array.Length != 3 || array[0].Length == 0 || array[1].Length == 0) { return false; } if (!TryParseFinite(array[2], out buildupPercent) || buildupPercent < 0f) { buildupPercent = 0f; return false; } if (buildupPercent > 100f) { buildupPercent = 100f; } targetUid = array[0]; status = array[1]; return true; } public static string BuildWardRequest(string status) { return SanitizeField(status); } public static bool TryParseWardRequest(string payload, out string status) { status = ""; if (string.IsNullOrEmpty(payload) || payload.IndexOf('\t') >= 0) { return false; } status = payload; return true; } public static string BuildBandageRequest(string token) { return SanitizeField(token); } public static bool TryParseBandageRequest(string payload, out string token) { token = ""; if (string.IsNullOrEmpty(payload) || payload.IndexOf('\t') >= 0) { return false; } token = payload; return true; } public static string BuildBandageAck(string token, string result) { return SanitizeField(token) + "\t" + SanitizeField(result); } public static bool TryParseBandageAck(string payload, out string token, out string result) { token = ""; result = ""; string[] array = (payload ?? "").Split(new char[1] { '\t' }); if (array.Length != 2 || array[0].Length == 0) { return false; } if (array[1] != "ok" && array[1] != "no-anchor" && array[1] != "disabled" && array[1] != "unresolved") { return false; } token = array[0]; result = array[1]; return true; } public static string BuildTaunt(string enemyUid, float seconds) { return SanitizeField(enemyUid) + "\t" + seconds.ToString("R", CultureInfo.InvariantCulture); } public static bool TryParseTaunt(string payload, out string enemyUid, out float seconds) { enemyUid = ""; seconds = 0f; string[] array = (payload ?? "").Split(new char[1] { '\t' }); if (array.Length != 2 || array[0].Length == 0) { return false; } if (!TryParseFinite(array[1], out seconds) || seconds <= 0f) { return false; } if (seconds > 60f) { seconds = 60f; } enemyUid = array[0]; return true; } public static string BuildDrain(float amount) { return "drain " + amount.ToString("R", CultureInfo.InvariantCulture); } public static string BuildSetHealthValue(float value) { return "set " + value.ToString("R", CultureInfo.InvariantCulture); } public static string BuildSetHealthPercent(float percent) { return "set " + percent.ToString("R", CultureInfo.InvariantCulture) + "%"; } public static bool TryParseSetHealth(string payload, out bool full, out float drainAmount) { full = false; drainAmount = 0f; if (!TryParseSetHealth(payload, out var kind, out var value, out var _)) { return false; } if (kind == SetHealthKind.Set) { return false; } full = kind == SetHealthKind.Full; if (kind == SetHealthKind.Drain) { drainAmount = value; } return true; } public static bool TryParseSetHealth(string payload, out SetHealthKind kind, out float value, out bool isPercent) { kind = SetHealthKind.Full; value = 0f; isPercent = false; if (string.IsNullOrEmpty(payload)) { return false; } if (payload == "full") { return true; } if (payload.StartsWith("drain ", StringComparison.Ordinal)) { if (!TryParseFinite(payload.Substring("drain ".Length), out value) || value <= 0f) { value = 0f; return false; } if (value > 1000f) { value = 1000f; } kind = SetHealthKind.Drain; return true; } if (payload.StartsWith("set ", StringComparison.Ordinal)) { string text = payload.Substring("set ".Length); bool flag = text.EndsWith("%", StringComparison.Ordinal); if (flag) { text = text.Substring(0, text.Length - 1); } if (!TryParseFinite(text, out value) || value < 0f) { value = 0f; return false; } if (flag) { if (value > 100f) { value = 100f; } } else if (value > 100000f) { value = 100000f; } kind = SetHealthKind.Set; isPercent = flag; return true; } return false; } public static string BuildEffigySet(string species, int tier) { return BuildAnnounce(species, tier); } public static string BuildEffigySet(string species, int tier, string extension) { if (!string.IsNullOrEmpty(extension)) { return BuildAnnounce(species, tier) + "\t" + extension; } return BuildAnnounce(species, tier); } public static void ParseEffigySet(string payload, out string species, out int tier) { ParseAnnounce(payload, out species, out tier); } public static void ParseEffigySet(string payload, out string species, out int tier, out string extension) { ParseAnnounce(payload, out species, out tier); extension = ""; if (string.IsNullOrEmpty(payload)) { return; } int num = payload.IndexOf('\t'); if (num >= 0) { int num2 = payload.IndexOf('\t', num + 1); if (num2 >= 0) { extension = payload.Substring(num2 + 1); } } } public static string BuildEffigyClear(string reason) { return SanitizeField(reason); } public static string BuildStance(bool passive) { if (!passive) { return "engaged"; } return "passive"; } public static bool ParseStancePassive(string payload) { return string.Equals(payload, "passive", StringComparison.Ordinal); } public static string ParseEffigyClear(string payload) { return payload ?? ""; } public static string BuildSwing(int type) { return ((type > 9) ? 9 : type).ToString(CultureInfo.InvariantCulture); } public static bool TryParseSwingType(string payload, out int type) { type = 0; if (string.IsNullOrEmpty(payload)) { return false; } if (!int.TryParse(payload, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return false; } if (result < 0) { return false; } type = ((result > 9) ? 9 : result); return true; } public static string BuildPos(float x, float y, float z, float yawDeg) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; return x.ToString("R", invariantCulture) + "\t" + y.ToString("R", invariantCulture) + "\t" + z.ToString("R", invariantCulture) + "\t" + yawDeg.ToString("R", invariantCulture); } public static bool TryParsePos(string payload, out float x, out float y, out float z, out float yawDeg) { PosParseError error; return TryParsePos(payload, out x, out y, out z, out yawDeg, out error); } public static bool TryParsePos(string payload, out float x, out float y, out float z, out float yawDeg, out PosParseError error) { x = 0f; y = 0f; z = 0f; yawDeg = 0f; error = PosParseError.Unparseable; string[] array = (payload ?? "").Split(new char[1] { '\t' }); if (array.Length != 4) { return false; } if (!TryParseFinite(array[0], out x) || !TryParseFinite(array[1], out y) || !TryParseFinite(array[2], out z) || !TryParseFinite(array[3], out yawDeg)) { x = 0f; y = 0f; z = 0f; yawDeg = 0f; return false; } if (!WorldPosition.IsSane(x, y, z)) { error = PosParseError.Insane; x = 0f; y = 0f; z = 0f; yawDeg = 0f; return false; } error = PosParseError.None; return true; } public static string BuildPetFxSet(string spellKey) { return SanitizeField(spellKey); } public static bool TryParsePetFxSet(string payload, out string spellKey) { spellKey = ""; if (string.IsNullOrEmpty(payload)) { return false; } int num = payload.IndexOf('\t'); string text = ((num < 0) ? payload : payload.Substring(0, num)); if (text.Length == 0) { return false; } spellKey = text; return true; } public static string BuildPetFxCast(string spellKey, int castItemId = 0) { if (castItemId <= 0) { return BuildPetFxSet(spellKey); } return SanitizeField(spellKey) + "\t" + castItemId.ToString(CultureInfo.InvariantCulture); } public static bool TryParsePetFxCast(string payload, out string spellKey, out int castItemId) { castItemId = 0; if (!TryParsePetFxSet(payload, out spellKey)) { return false; } int num = payload.IndexOf('\t'); if (num < 0) { return true; } string text = payload.Substring(num + 1); int num2 = text.IndexOf('\t'); if (num2 >= 0) { text = text.Substring(0, num2); } if (int.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out var result) && result > 0) { castItemId = result; } return true; } public static string BuildProxyPetFx(string spellKey, int slot, bool active) { return SanitizeField(spellKey) + "\t" + ((slot >= 0) ? slot : 0).ToString(CultureInfo.InvariantCulture) + "\t" + (active ? "1" : "0"); } public static bool TryParseProxyPetFx(string payload, out string spellKey, out int slot, out bool active) { spellKey = ""; slot = 0; active = false; string[] array = (payload ?? "").Split(new char[1] { '\t' }); if (array.Length != 3 || array[0].Length == 0) { return false; } if (!int.TryParse(array[1], NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result < 0) { return false; } if (array[2] != "1" && array[2] != "0") { return false; } spellKey = array[0]; slot = result; active = array[2] == "1"; return true; } public static string BuildProxyPetFxCast(string spellKey, int castItemId = 0) { return BuildPetFxCast(spellKey, castItemId); } public static bool TryParseProxyPetFxCast(string payload, out string spellKey, out int castItemId) { return TryParsePetFxCast(payload, out spellKey, out castItemId); } private static bool TryParseFinite(string s, out float value) { if (float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out value) && !float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } public static string HashTable(IEnumerable lines) { List list = new List(lines ?? Array.Empty()); list.Sort(StringComparer.Ordinal); uint num = 2166136261u; foreach (string item in list) { if (item != null) { string text = item; foreach (char c in text) { num ^= c; num *= 16777619; } num ^= 0xA; num *= 16777619; } } return num.ToString("x8"); } } public enum FxApplyDecision { None, Attach, Rebuild, Detach } public sealed class PetFxRow { public string OwnerUid { get; internal set; } public int Slot { get; internal set; } public string Signature { get; internal set; } public double MaxSeconds { get; internal set; } public double SetAt { get; internal set; } public double AgeAt(double now) { return now - SetAt; } } public sealed class PetFxLedger { private readonly Dictionary _rows = new Dictionary(StringComparer.Ordinal); public int Count => _rows.Count; public FxApplyDecision Set(string ownerUid, int slot, string signature, double maxSeconds, double now) { if (string.IsNullOrEmpty(ownerUid) || string.IsNullOrEmpty(signature) || slot < 0) { return FxApplyDecision.None; } string key = Key(ownerUid, slot); if (!_rows.TryGetValue(key, out PetFxRow value)) { _rows[key] = new PetFxRow { OwnerUid = ownerUid, Slot = slot, Signature = signature, MaxSeconds = maxSeconds, SetAt = now }; return FxApplyDecision.Attach; } bool num = !string.Equals(value.Signature, signature, StringComparison.Ordinal); value.Signature = signature; value.MaxSeconds = maxSeconds; value.SetAt = now; if (!num) { return FxApplyDecision.None; } return FxApplyDecision.Rebuild; } public bool Detach(string ownerUid, int slot) { return _rows.Remove(Key(ownerUid ?? "", slot)); } public IReadOnlyList<(string OwnerUid, int Slot)> Expired(double now) { List<(string, int)> list = null; foreach (KeyValuePair row in _rows) { PetFxRow value = row.Value; if (value.MaxSeconds > 0.0 && now - value.SetAt >= value.MaxSeconds) { (list ?? (list = new List<(string, int)>())).Add((value.OwnerUid, value.Slot)); } } IReadOnlyList<(string, int)> readOnlyList = list; return readOnlyList ?? Array.Empty<(string, int)>(); } public bool TryGet(string ownerUid, int slot, out PetFxRow row) { return _rows.TryGetValue(Key(ownerUid ?? "", slot), out row); } public int ClearOwner(string ownerUid) { if (string.IsNullOrEmpty(ownerUid)) { return 0; } List list = new List(); foreach (KeyValuePair row in _rows) { if (string.Equals(row.Value.OwnerUid, ownerUid, StringComparison.Ordinal)) { list.Add(row.Key); } } foreach (string item in list) { _rows.Remove(item); } return list.Count; } public int ClearAll() { int count = _rows.Count; _rows.Clear(); return count; } public List RowsSnapshot() { List list = new List(_rows.Count); foreach (KeyValuePair row in _rows) { list.Add(row.Value); } return list; } private static string Key(string ownerUid, int slot) { return ownerUid + ":" + slot.ToString(CultureInfo.InvariantCulture); } } public enum PetFxReportStep { None, Send } public sealed class PetFxReportBook { private sealed class Entry { public string Payload; public double LandedAt; } public const double DefaultPeriodSeconds = 30.0; private readonly Dictionary _landed = new Dictionary(StringComparer.Ordinal); public double PeriodSeconds { get; set; } = 30.0; public int Count => _landed.Count; public PetFxReportStep DecideActive(string key, string payload, double now) { if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(payload)) { return PetFxReportStep.None; } if (!_landed.TryGetValue(key, out Entry value)) { return PetFxReportStep.Send; } if (!string.Equals(value.Payload, payload, StringComparison.Ordinal)) { return PetFxReportStep.Send; } if (!(now - value.LandedAt >= PeriodSeconds)) { return PetFxReportStep.None; } return PetFxReportStep.Send; } public PetFxReportStep DecideInactive(string key) { if (string.IsNullOrEmpty(key) || !_landed.ContainsKey(key)) { return PetFxReportStep.None; } return PetFxReportStep.Send; } public void OnLandedActive(string key, string payload, double now) { if (!string.IsNullOrEmpty(key)) { _landed[key] = new Entry { Payload = (payload ?? ""), LandedAt = now }; } } public void OnLandedInactive(string key) { if (!string.IsNullOrEmpty(key)) { _landed.Remove(key); } } public void Reset() { _landed.Clear(); } } public enum PlaceRefusal { None, NoReference, ReferenceStaging, SpotInsane } public static class PlacementGate { public static PlaceRefusal Check(bool hasReference, float refX, float refY, float refZ, float spotX, float spotY, float spotZ) { if (!hasReference) { return PlaceRefusal.NoReference; } if (!WorldPosition.IsSane(refX, refY, refZ)) { return PlaceRefusal.ReferenceStaging; } if (!WorldPosition.IsSane(spotX, spotY, spotZ)) { return PlaceRefusal.SpotInsane; } return PlaceRefusal.None; } public static PlaceRefusal CheckSpot(float spotX, float spotY, float spotZ) { if (!WorldPosition.IsSane(spotX, spotY, spotZ)) { return PlaceRefusal.SpotInsane; } return PlaceRefusal.None; } public static bool Allows(PlaceRefusal r) { return r == PlaceRefusal.None; } public static string Describe(PlaceRefusal r) { return r switch { PlaceRefusal.NoReference => "no owner/anchor reference", PlaceRefusal.ReferenceStaging => "owner/anchor reads as void/staging (mid-load)", PlaceRefusal.SpotInsane => "spot reads as void/staging or is numerically absurd", _ => "allowed", }; } } public sealed class ProxyPosPacing { public const float DefaultMinIntervalSeconds = 0.2f; public const float ResendSeconds = 4f; public const float PosQuantumMeters = 0.25f; public const float YawQuantumDegrees = 5f; private double _lastAttemptAt = double.NegativeInfinity; private bool _hasKeyed; private float _kx; private float _ky; private float _kz; private float _kyaw; private long _keySeq; public bool IntervalElapsed(double now, float minIntervalSeconds = 0.2f) { return now - _lastAttemptAt >= (double)minIntervalSeconds; } public void OnAttempt(double now) { _lastAttemptAt = now; } public string DeltaKey(float x, float y, float z, float yawDeg, float posQuantum = 0.25f, float yawQuantum = 5f) { if (!_hasKeyed || Math.Abs(x - _kx) >= posQuantum || Math.Abs(y - _ky) >= posQuantum || Math.Abs(z - _kz) >= posQuantum || CircularDeltaDegrees(yawDeg, _kyaw) >= yawQuantum) { _hasKeyed = true; _kx = x; _ky = y; _kz = z; _kyaw = yawDeg; _keySeq++; } return _keySeq.ToString(CultureInfo.InvariantCulture); } public static float CircularDeltaDegrees(float a, float b) { float num = Math.Abs(a - b) % 360f; if (!(num > 180f)) { return num; } return 360f - num; } public static float YawDegrees(float dirX, float dirZ) { if (dirX * dirX + dirZ * dirZ < 1E-12f) { return 0f; } return (float)(Math.Atan2(dirX, dirZ) * (180.0 / Math.PI)); } } public static class ProxyDriveState { public enum Status { None, Live, Stale } public const float DefaultStaleSeconds = 10f; public static Status Decide(double now, double lastReceivedAt, float staleSeconds) { if (lastReceivedAt <= 0.0) { return Status.None; } if (!(now - lastReceivedAt <= (double)staleSeconds)) { return Status.Stale; } return Status.Live; } } public static class ProxyDriveMotion { public struct Step { public float X; public float Y; public float Z; public bool Snapped; } public const float SnapDistance = 3f; public const float DefaultSpeedCap = 15f; public static Step Toward(float curX, float curY, float curZ, float tgtX, float tgtY, float tgtZ, float dt, float speedCap = 15f, float snapDistance = 3f) { float num = tgtX - curX; float num2 = tgtY - curY; float num3 = tgtZ - curZ; double num4 = Math.Sqrt((double)num * (double)num + (double)num2 * (double)num2 + (double)num3 * (double)num3); if (num4 > (double)snapDistance) { return new Step { X = tgtX, Y = tgtY, Z = tgtZ, Snapped = true }; } double num5 = (double)speedCap * (double)dt; if (num4 <= num5 || num4 < 1E-09) { return new Step { X = tgtX, Y = tgtY, Z = tgtZ, Snapped = false }; } double num6 = num5 / num4; return new Step { X = curX + (float)((double)num * num6), Y = curY + (float)((double)num2 * num6), Z = curZ + (float)((double)num3 * num6), Snapped = false }; } } public static class RangedSpecial { public readonly struct ShooterCandidate { public readonly string ProjectileName; public readonly string Path; public ShooterCandidate(string projectileName, string path) { ProjectileName = projectileName; Path = path; } } public enum ImpactOutcome { Resolve, AlreadyResolved, Miss } public enum VolleyState { Pending, Hit, Miss } public const float DefaultTimeoutSeconds = 6f; public const float WindupStaleMarginSeconds = 2f; public const float MuzzleClearanceMeters = 3.5f; public const float MuzzleClearanceLateralMeters = 0.8f; public const float MuzzleClearanceLiftMeters = 0.4f; public static int ChooseShooter(IReadOnlyList candidates, string filter) { if (candidates == null || candidates.Count == 0) { return -1; } if (!string.IsNullOrEmpty(filter)) { for (int i = 0; i < candidates.Count; i++) { if (!string.IsNullOrEmpty(candidates[i].ProjectileName) && (Contains(candidates[i].ProjectileName, filter) || Contains(candidates[i].Path, filter))) { return i; } } } for (int j = 0; j < candidates.Count; j++) { if (!string.IsNullOrEmpty(candidates[j].ProjectileName)) { return j; } } return -1; } public static ImpactOutcome ResolveImpact(bool alreadyResolved, bool hitIsCharacter, bool hitIsValidEnemy) { if (alreadyResolved) { return ImpactOutcome.AlreadyResolved; } if (hitIsCharacter && hitIsValidEnemy) { return ImpactOutcome.Resolve; } return ImpactOutcome.Miss; } public static VolleyState AssessVolley(bool resolved, int explodedCount, int firedCount) { if (resolved) { return VolleyState.Hit; } if (firedCount > 0 && explodedCount >= firedCount) { return VolleyState.Miss; } return VolleyState.Pending; } public static float TimeoutSeconds(float cfgSeconds, float projectileLifespan) { bool flag = cfgSeconds > 0f; bool flag2 = projectileLifespan > 0f; if (flag && flag2) { return Math.Min(cfgSeconds, projectileLifespan); } if (flag) { return cfgSeconds; } if (flag2) { return projectileLifespan; } return 6f; } public static bool WasShot(float shootTimeBefore, float shootTimeAfter) { return shootTimeAfter > shootTimeBefore; } public static bool WindupStale(float startedAt, float windupSeconds, float now, float marginSeconds = 2f) { if (windupSeconds < 0f) { windupSeconds = 0f; } if (marginSeconds < 0f) { marginSeconds = 0f; } return now > startedAt + windupSeconds + marginSeconds; } public static bool MuzzleBlocked(bool firstHitIsFriendly, float hitDistance, float clearanceMeters = 3.5f) { if (firstHitIsFriendly && hitDistance >= 0f) { return hitDistance <= clearanceMeters; } return false; } public static void MuzzleClearanceOffset(float aimX, float aimY, float aimZ, float lateralMeters, float liftMeters, out float offX, out float offY, out float offZ) { offY = liftMeters; if (aimX * aimX + aimY * aimY + aimZ * aimZ <= 1E-08f) { offX = 0f; offZ = 0f; return; } float num = aimZ; float num2 = 0f - aimX; float num3 = (float)Math.Sqrt(num * num + num2 * num2); if (num3 > 0.0001f) { num /= num3; num2 /= num3; } else { num = 1f; num2 = 0f; } offX = num * lateralMeters; offZ = num2 * lateralMeters; } private static bool Contains(string haystack, string needle) { if (haystack != null) { return haystack.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } } public static class ReformFlow { public enum SceneAction { IgnoreAdditive, Gameplay, SessionEnd, None } public static SceneAction Classify(bool additive, string sceneName) { if (additive) { return SceneAction.IgnoreAdditive; } if (Scenes.IsGameplay(sceneName)) { return SceneAction.Gameplay; } if (Scenes.IsMainMenu(sceneName)) { return SceneAction.SessionEnd; } return SceneAction.None; } public static bool OwnerMismatch(string liveOwnerUid, string playerUid) { if (!string.IsNullOrEmpty(liveOwnerUid) && !string.IsNullOrEmpty(playerUid)) { return liveOwnerUid != playerUid; } return false; } public static bool ShouldKickMaintain(bool hasLive, bool hasBody, bool maintainActive) { if (hasLive && !hasBody) { return !maintainActive; } return false; } public static bool WantsBodyUpgrade(bool hasBody, bool isGhost) { return !hasBody || isGhost; } public static bool StandInGate(bool hasBody, bool triedStandIn) { if (!hasBody) { return !triedStandIn; } return false; } public static string Uid4(string uid) { if (!string.IsNullOrEmpty(uid)) { if (uid.Length > 4) { return uid.Substring(0, 4); } return uid; } return "none"; } } public sealed class ReplaceConvergence { public float MinProgress = 0.5f; public int Threshold = 8; private float _lastDist = float.NaN; private int _stuck; private bool _latched; public bool Latched => _latched; public int ConsecutiveStuck => _stuck; public static bool IsMeasurable(bool ownerKeyedTrigger, bool goalIsOwner) { return ownerKeyedTrigger || goalIsOwner; } public bool RecordReplace(float dist) { if (_latched) { return false; } bool flag = !float.IsNaN(_lastDist) && Math.Abs(dist - _lastDist) < MinProgress; _lastDist = dist; _stuck = (flag ? (_stuck + 1) : 0); if (_stuck >= Threshold) { _latched = true; return true; } return false; } public void RecordConverged() { if (!_latched) { _stuck = 0; _lastDist = float.NaN; } } } public static class RigSurgery { public static HashSet RootChain(T root, Func parent) where T : class { HashSet hashSet = new HashSet(); for (T val = root; val != null; val = parent(val)) { hashSet.Add(val); } return hashSet; } public static T FindGraftRoot(T bone, HashSet donorRootChain, Func parent) where T : class { if (bone == null || donorRootChain == null || donorRootChain.Contains(bone)) { return null; } T result = bone; T val = parent(bone); while (val != null && !donorRootChain.Contains(val)) { result = val; val = parent(val); } return result; } public static List DedupeGraftRoots(IEnumerable roots, Func parent) where T : class { List list = new List(); HashSet hashSet = new HashSet(); foreach (T item in roots ?? Array.Empty()) { if (item != null && hashSet.Add(item)) { list.Add(item); } } List list2 = new List(); foreach (T item2 in list) { bool flag = false; T val = parent(item2); while (val != null && !flag) { if (hashSet.Contains(val)) { flag = true; } val = parent(val); } if (!flag) { list2.Add(item2); } } return list2; } public static string Summarize(int smrs, int bones, int internalBones, int external, int nullBones, int rootBonesExternal, int jointsExternal, int cloth) { return $"{smrs} SMR(s), {bones} bone slot(s) — internal={internalBones} external={external} " + $"null={nullBones} rootBoneExt={rootBonesExternal} jointsExt={jointsExternal} cloth={cloth}"; } public static bool IsHealthy(int external, int nullBones, int rootBonesExternal, int jointsExternal) { if (external == 0 && nullBones == 0 && rootBonesExternal == 0) { return jointsExternal == 0; } return false; } } public static class SlopeMath { public const float DefaultTauSeconds = 0.15f; public const float DefaultRateCapDegPerSec = 120f; public const float DefaultMaxPitchDeg = 30f; public const float DefaultMaxRollDeg = 15f; public const float DefaultDeadbandDeg = 5f; public const float DefaultProbeHz = 20f; public const float MinFacetUp = 0.34f; public const float DeadbandHysteresisDeg = 0.5f; public static bool AcceptFacet(float ny) { return ny >= 0.34f; } public static float EmaAlpha(float dt, float tau) { if (dt <= 0f) { return 0f; } if (tau <= 0f) { return 1f; } float num = 1f - (float)Math.Exp((0f - dt) / tau); if (!(num < 0f)) { if (!(num > 1f)) { return num; } return 1f; } return 0f; } public static bool NormalFromTwoPoints(float fx, float fy, float fz, float bx, float by, float bz, float hx, float hz, out float nx, out float ny, out float nz) { nx = 0f; ny = 1f; nz = 0f; float num = (float)Math.Sqrt(hx * hx + hz * hz); if (num < 1E-06f) { return false; } float num2 = fx - bx; float num3 = fy - by; float num4 = fz - bz; if ((float)Math.Sqrt(num2 * num2 + num4 * num4) < 0.0001f) { return false; } float num5 = hz / num; float num6 = (0f - hx) / num; float num7 = num3 * num6; float num8 = num4 * num5 - num2 * num6; float num9 = (0f - num3) * num5; float num10 = (float)Math.Sqrt(num7 * num7 + num8 * num8 + num9 * num9); if (num10 < 1E-06f) { return false; } if (num8 < 0f) { num7 = 0f - num7; num8 = 0f - num8; num9 = 0f - num9; } nx = num7 / num10; ny = num8 / num10; nz = num9 / num10; return ny > 0.001f; } public static float TiltDeg(float nx, float ny, float nz) { return (float)(Math.Acos((ny < -1f) ? (-1f) : ((ny > 1f) ? 1f : ny)) * 180.0 / Math.PI); } public static bool WithinDeadband(float nx, float ny, float nz, float degrees) { return TiltDeg(nx, ny, nz) < degrees; } public static void PitchRollDeg(float nx, float ny, float nz, float hx, float hz, out float pitchDeg, out float rollDeg) { pitchDeg = 0f; rollDeg = 0f; float num = (float)Math.Sqrt(hx * hx + hz * hz); if (!(num < 1E-06f)) { float num2 = hx / num; float num3 = hz / num; float num4 = num3; float num5 = 0f - num2; float num6 = nx * num2 + nz * num3; float num7 = nx * num4 + nz * num5; num6 = ((num6 < -1f) ? (-1f) : ((num6 > 1f) ? 1f : num6)); num7 = ((num7 < -1f) ? (-1f) : ((num7 > 1f) ? 1f : num7)); pitchDeg = (float)(Math.Asin(0f - num6) * 180.0 / Math.PI); rollDeg = (float)(Math.Asin(num7) * 180.0 / Math.PI); } } public static bool DeadbandFlat(bool wasFlat, float tiltDeg, float deadbandDeg) { if (!wasFlat) { return tiltDeg < deadbandDeg - 0.5f; } return tiltDeg <= deadbandDeg + 0.5f; } public static void SmoothStep(float cx, float cy, float cz, float sx, float sy, float sz, float dt, float tauSeconds, float rateCapDegPerSec, out float ox, out float oy, out float oz) { float t = EmaAlpha(dt, tauSeconds); ArcTowards(cx, cy, cz, sx, sy, sz, t, float.MaxValue, out var ox2, out var oy2, out var oz2); float maxRad = ((rateCapDegPerSec < 0f) ? 0f : rateCapDegPerSec) * (MathF.PI / 180f) * ((dt > 0f) ? dt : 0f); ArcTowards(cx, cy, cz, ox2, oy2, oz2, 1f, maxRad, out ox, out oy, out oz); } private static void ArcTowards(float ax, float ay, float az, float bx, float by, float bz, float t, float maxRad, out float ox, out float oy, out float oz) { float num = ax * bx + ay * by + az * bz; num = ((num < -1f) ? (-1f) : ((num > 1f) ? 1f : num)); float num2 = (float)Math.Acos(num); float num3 = num2 * ((t < 0f) ? 0f : ((t > 1f) ? 1f : t)); if (num3 > maxRad) { num3 = maxRad; } if (num2 < 1E-05f || num3 >= num2 - 1E-06f) { ox = bx; oy = by; oz = bz; Renorm(ref ox, ref oy, ref oz); return; } float num4 = (float)Math.Sin(num2); if (num4 < 1E-05f) { ox = bx; oy = by; oz = bz; Renorm(ref ox, ref oy, ref oz); } else { float num5 = (float)Math.Sin(num2 - num3) / num4; float num6 = (float)Math.Sin(num3) / num4; ox = num5 * ax + num6 * bx; oy = num5 * ay + num6 * by; oz = num5 * az + num6 * bz; Renorm(ref ox, ref oy, ref oz); } } private static void Renorm(ref float x, ref float y, ref float z) { float num = (float)Math.Sqrt(x * x + y * y + z * z); if (num < 1E-06f) { x = 0f; y = 1f; z = 0f; } else { x /= num; y /= num; z /= num; } } public static void ClampTilt(float nx, float ny, float nz, float hx, float hz, float maxPitchDeg, float maxRollDeg, out float cx, out float cy, out float cz) { cx = nx; cy = ny; cz = nz; float num = (float)Math.Sqrt(hx * hx + hz * hz); if (!(num < 1E-06f)) { PitchRollDeg(nx, ny, nz, hx, hz, out var pitchDeg, out var rollDeg); if (!(Math.Abs(pitchDeg) <= maxPitchDeg) || !(Math.Abs(rollDeg) <= maxRollDeg)) { float num2 = ((pitchDeg > maxPitchDeg) ? maxPitchDeg : ((pitchDeg < 0f - maxPitchDeg) ? (0f - maxPitchDeg) : pitchDeg)); float num3 = ((rollDeg > maxRollDeg) ? maxRollDeg : ((rollDeg < 0f - maxRollDeg) ? (0f - maxRollDeg) : rollDeg)); float num4 = hx / num; float num5 = hz / num; float num6 = num5; float num7 = 0f - num4; float num8 = (float)Math.Sin((double)(0f - num2) * Math.PI / 180.0); float num9 = (float)Math.Sin((double)num3 * Math.PI / 180.0); float num10 = 1f - num8 * num8 - num9 * num9; float num11 = (float)Math.Sqrt((num10 < 0.0001f) ? 0.0001f : num10); float num12 = num8 * num4 + num9 * num6; float num13 = num11; float num14 = num8 * num5 + num9 * num7; float num15 = (float)Math.Sqrt(num12 * num12 + num13 * num13 + num14 * num14); cx = num12 / num15; cy = num13 / num15; cz = num14 / num15; } } } } public static class SpeciesResolve { public static int MatchIndex(string? speciesId, IReadOnlyList? keys) { if (string.IsNullOrEmpty(speciesId) || keys == null) { return -1; } for (int i = 0; i < keys.Count; i++) { string text = keys[i]; if (!string.IsNullOrEmpty(text) && Species.NameMatches(speciesId, text)) { return i; } } return -1; } public static bool IsStandInBodyName(string? name) { if (string.IsNullOrWhiteSpace(name)) { return false; } string text = name.Trim(); if (!text.Equals("Spirit", StringComparison.OrdinalIgnoreCase) && !text.Equals("Summoned Ghost", StringComparison.OrdinalIgnoreCase)) { return text.StartsWith("NewGhost", StringComparison.OrdinalIgnoreCase); } return true; } } public static class SpeciesTable { public readonly struct TableLine { public readonly string Key; public readonly string[] Fields; public readonly string Raw; public TableLine(string key, string[] fields, string raw) { Key = key; Fields = fields; Raw = raw; } } public static IEnumerable Lines(string text, char lineSeparator = '\n') { string[] array = (text ?? "").Split(new char[1] { lineSeparator }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2[0] == '#') { continue; } int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); if (text3.Length != 0) { yield return new TableLine(text3, text2.Substring(num + 1).Split(new char[1] { ',' }), text2); } } } } public static float FloatOr(string[] fields, int index, float fallback, Action warn, string key) { if (index >= fields.Length) { return fallback; } string text = fields[index].Trim(); if (text.Length == 0) { return fallback; } if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } warn?.Invoke($"'{key}': unparsable number '{text}' — using {fallback}."); return fallback; } public static double DoubleOr(string[] fields, int index, double fallback, Action warn, string key) { if (index >= fields.Length) { return fallback; } string text = fields[index].Trim(); if (text.Length == 0) { return fallback; } if (double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } warn?.Invoke($"'{key}': unparsable number '{text}' — using {fallback}."); return fallback; } public static int IntOr(string[] fields, int index, int fallback, Action warn, string key) { if (index >= fields.Length) { return fallback; } string text = fields[index].Trim(); if (text.Length == 0) { return fallback; } if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return result; } warn?.Invoke($"'{key}': unparsable integer '{text}' — using {fallback}."); return fallback; } public static Dictionary Merge(Dictionary builtIn, Dictionary overrides) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (builtIn != null) { foreach (KeyValuePair item in builtIn) { dictionary[item.Key] = item.Value; } } if (overrides != null) { foreach (KeyValuePair @override in overrides) { dictionary[@override.Key] = @override.Value; } } return dictionary; } public static bool TryResolve(Dictionary table, string speciesId, out T value, string defaultKey = null) { string key; return TryResolveKey(table, speciesId, out key, out value, defaultKey); } public static bool TryResolveKey(Dictionary table, string speciesId, out string key, out T value, string defaultKey = null) { if (table != null && !string.IsNullOrEmpty(speciesId)) { if (table.TryGetValue(speciesId, out value)) { key = speciesId; return true; } string text = null; foreach (KeyValuePair item in table) { if (!string.IsNullOrEmpty(item.Key) && (defaultKey == null || !string.Equals(item.Key, defaultKey, StringComparison.OrdinalIgnoreCase)) && Species.NameMatches(speciesId, item.Key) && (text == null || item.Key.Length > text.Length)) { text = item.Key; } } if (text != null) { key = text; value = table[text]; return true; } } if (table != null && defaultKey != null && table.TryGetValue(defaultKey, out value)) { key = defaultKey; return true; } key = null; value = default(T); return false; } } public struct SpeedEstimate { public const float WindowSeconds = 0.1f; public const float WarpMetersPerSecond = 30f; public float Speed; public float LastX; public float LastZ; public float LastAt; public bool Armed; public float Current { get { if (!Armed) { return float.NaN; } return Speed; } } public float Update(float x, float z, float now) { if (!Armed) { Armed = true; LastX = x; LastZ = z; LastAt = now; Speed = float.NaN; return Speed; } float num = now - LastAt; if (num < 0.099f) { return Speed; } float num2 = x - LastX; float num3 = z - LastZ; float num4 = (float)Math.Sqrt(num2 * num2 + num3 * num3) / num; LastX = x; LastZ = z; LastAt = now; if (num4 > 30f) { return Speed; } Speed = (float.IsNaN(Speed) ? num4 : (0.5f * Speed + 0.5f * num4)); return Speed; } public void Reset() { this = default(SpeedEstimate); } } public static class ProxyStance { public static bool ShouldCalm(bool stancePassive, bool anchorFighting, bool instructedLockActive) { if (anchorFighting) { if (!stancePassive) { return !instructedLockActive; } return true; } return false; } public static bool ShouldRecall(bool stancePassive, bool welded, bool anchorFighting) { return stancePassive && !welded && anchorFighting; } } public sealed class AttackedThrottle { public const float DefaultIntervalSeconds = 3f; private readonly Dictionary _lastSentAt = new Dictionary(); public bool ShouldSend(string attackerUid, double now, float intervalSeconds) { if (string.IsNullOrEmpty(attackerUid)) { return false; } if (_lastSentAt.TryGetValue(attackerUid, out var value) && now - value < (double)intervalSeconds) { return false; } _lastSentAt[attackerUid] = now; return true; } } public static class AttackedReport { public static bool SuppressedByInstruction(string attackerUid, string instructedUid) { if (!string.IsNullOrEmpty(instructedUid)) { return string.Equals(attackerUid, instructedUid, StringComparison.Ordinal); } return false; } } public static class RemoteDefend { public const float DefaultHoldSeconds = 10f; public static bool Active(double reportedAt, double now, float holdSeconds) { if (reportedAt > 0.0) { return now - reportedAt <= (double)holdSeconds; } return false; } } public static class OwnerFocus { public const float DefaultHoldSeconds = 8f; public static bool Active(double struckAt, double now, float holdSeconds) { if (struckAt > 0.0) { return now - struckAt <= (double)holdSeconds; } return false; } } public enum EffigyFollowTarget { Anchor, OwnerReplica } public static class EffigyFollow { public static EffigyFollowTarget Choose(bool stancePassive, bool ownerReplicaResolved) { if (!(stancePassive && ownerReplicaResolved)) { return EffigyFollowTarget.Anchor; } return EffigyFollowTarget.OwnerReplica; } } public static class StatAdoption { public static bool ShouldAdopt(string savedSpeciesId, string bodySpeciesId, bool hasCapture, bool alreadyHasAttributes) { if (!hasCapture || alreadyHasAttributes) { return false; } return IsSameSpecies(savedSpeciesId, bodySpeciesId); } public static bool IsSameSpecies(string a, string b) { if (!string.IsNullOrWhiteSpace(a) && !string.IsNullOrWhiteSpace(b)) { return string.Equals(a.Trim(), b.Trim(), StringComparison.OrdinalIgnoreCase); } return false; } } public enum StatApplyGate { Unknown, Applied, Unchanged, Cleared, NoAnchor, NoStats, ZeroBaseline, NoAttributes } public static class ZeroBaselinePolicy { public enum Action { Retry, Accept } public const int DefaultRetryLimit = 8; public const double DefaultMinSeconds = 1.0; public static Action Decide(int consecutiveZeroReads, double secondsSinceFirstZeroRead, int retryLimit = 8, double minSeconds = 1.0) { if (retryLimit < 2) { retryLimit = 2; } if (minSeconds < 0.0) { minSeconds = 0.0; } if (consecutiveZeroReads < retryLimit || !(secondsSinceFirstZeroRead >= minSeconds)) { return Action.Retry; } return Action.Accept; } public static bool ShouldResnapshotAfterAccept(bool acceptedZeroBaseline, bool baselineNowNonZero) { return acceptedZeroBaseline && baselineNowNonZero; } } public static class PendingApplyPolicy { public const int DefaultMaxRetries = 240; public static bool IsPending(StatApplyGate gate) { if (gate != StatApplyGate.NoAnchor && gate != StatApplyGate.NoStats) { return gate == StatApplyGate.ZeroBaseline; } return true; } public static bool IsPending(StatApplyGate gate, bool localAnchorPossible) { return gate switch { StatApplyGate.NoStats => true, StatApplyGate.NoAnchor => localAnchorPossible, _ => gate == StatApplyGate.ZeroBaseline, }; } public static bool IsAnythingPending(StatApplyGate gate, bool vitalsPending, bool localAnchorPossible) { if (!IsPending(gate, localAnchorPossible)) { return vitalsPending && localAnchorPossible; } return true; } public static bool ShouldRetry(StatApplyGate gate, bool vitalsPending, int attempts, int maxRetries = 240) { return ShouldRetry(gate, vitalsPending, attempts, localAnchorPossible: true, maxRetries); } public static bool ShouldRetry(StatApplyGate gate, bool vitalsPending, int attempts, bool localAnchorPossible, int maxRetries = 240) { if (!IsAnythingPending(gate, vitalsPending, localAnchorPossible)) { return false; } if (maxRetries < 0) { maxRetries = 0; } return attempts < maxRetries; } public static string DescribeHeld(StatApplyGate gate, bool vitalsPending, bool localAnchorPossible) { bool flag = IsPending(gate, localAnchorPossible); bool flag2 = vitalsPending && localAnchorPossible; if (flag && flag2) { return "gate " + Describe(gate) + " AND vitals (max health not established)"; } if (flag2) { return "vitals only (max health not established; the gate itself reads " + Describe(gate) + ")"; } if (flag) { return "gate " + Describe(gate); } return "nothing (gate " + Describe(gate) + ") — settled; a give-up citing this is a caller bug"; } public static string Describe(StatApplyGate gate) { return gate switch { StatApplyGate.Applied => "applied", StatApplyGate.Unchanged => "unchanged (already applied)", StatApplyGate.Cleared => "cleared (no effective attributes)", StatApplyGate.NoAnchor => "no-anchor (bodiless/downed/not spawned yet)", StatApplyGate.NoStats => "no-stats (the anchor's CharacterStats is not built yet)", StatApplyGate.ZeroBaseline => "zero-baseline (the anchor's stat BaseValues all read 0 — not initialised)", StatApplyGate.NoAttributes => "eff-null (no capture on the bond, or species stats are switched off)", _ => "none yet", }; } } public struct StationPlantEdge { public bool Prev; public void CarryFromLastFrame(bool plantedLastFrame) { Prev = plantedLastFrame; } public bool Fires(bool plantedNow) { if (plantedNow) { return !Prev; } return false; } } public static class StatusResistPolicy { public const float Ceiling = 95f; public const float Floor = -100f; public static float Cap(float v) { if (!(v > 95f)) { if (!(v < -100f)) { return v; } return -100f; } return 95f; } } public static class StrafeCapability { public struct Verdict { public bool FeedSide; public bool ForceFaceToTravel; } public const float ForceEnterDeg = 60f; public const float ForceExitDeg = 25f; public static bool Exempt(LookTarget look, bool planted) { if (look != LookTarget.FacePoint) { return look == LookTarget.Enemy && planted; } return true; } public static Verdict Decide(bool hasSideParam, bool speciesAllowsBackpedal, float travelVsFaceDeg, bool wasForcing) { Verdict result = default(Verdict); result.FeedSide = hasSideParam && speciesAllowsBackpedal; if (result.FeedSide || float.IsNaN(travelVsFaceDeg)) { result.ForceFaceToTravel = false; return result; } float num = ((travelVsFaceDeg < 0f) ? (0f - travelVsFaceDeg) : travelVsFaceDeg); result.ForceFaceToTravel = (wasForcing ? (num > 25f) : (num > 60f)); return result; } } public enum StrikeOutcome { Pending, Landed, Blocked, Dodged, OutOfRange, OutOfAngle, Missed, TimedOut } public struct JudgeInputs { public bool TargetAlive; public float TargetDiedSecondsAgo; public float DistanceMeters; public float MaxRangeMeters; public float AngleFromAttackerForward; public float ConeDegrees; public bool HitboxesInactive; public bool Dodging; public bool Blocking; public float AngleFromTargetForward; public bool TargetHasShield; } public static class StrikeJudge { public const float BlockAngleDegrees = 60f; public const float ShieldBlockAngleDegrees = 80f; public const float JustDiedSeconds = 1f; public static bool JustDied(in JudgeInputs a) { if (a.TargetDiedSecondsAgo > 0f) { return a.TargetDiedSecondsAgo <= 1f; } return false; } public static StrikeOutcome Judge(in JudgeInputs a) { if (!a.TargetAlive && !JustDied(in a)) { return StrikeOutcome.Missed; } if (a.MaxRangeMeters > 0f && a.DistanceMeters > a.MaxRangeMeters) { return StrikeOutcome.OutOfRange; } if (a.ConeDegrees > 0f && a.ConeDegrees < 360f && a.AngleFromAttackerForward > a.ConeDegrees * 0.5f) { return StrikeOutcome.OutOfAngle; } if (a.HitboxesInactive || a.Dodging) { return StrikeOutcome.Dodged; } if (a.Blocking && a.AngleFromTargetForward < (a.TargetHasShield ? 80f : 60f)) { return StrikeOutcome.Blocked; } return StrikeOutcome.Landed; } } public struct StuckObservation { public float Dt; public bool AgentUnusable; public float GoalDist; public float BodyAgeSeconds; public bool WorldReady; } public sealed class StuckBodyPolicy { public float TripSeconds = 20f; public float StuckDistanceMeters = 25f; public float MinProgressMeters = 2f; public float MinBodyAgeSeconds = 20f; public const float DefaultTripSeconds = 20f; public const float DefaultStuckDistanceMeters = 25f; private float _elapsed; private float _best = float.PositiveInfinity; private float _anchor; private float _last; private bool _tripped; private bool _inEpisode; public float Elapsed => _elapsed; public bool Tripped => _tripped; public float BestGoalDist => _best; public float LastGoalDist => _last; public void Reset() { _elapsed = 0f; _best = float.PositiveInfinity; _anchor = float.PositiveInfinity; _last = 0f; _tripped = false; _inEpisode = false; } public bool Observe(in StuckObservation o) { if (o.Dt <= 0f || !o.WorldReady || !o.AgentUnusable || o.BodyAgeSeconds < MinBodyAgeSeconds || o.GoalDist <= StuckDistanceMeters) { Reset(); return false; } if (!_inEpisode || o.GoalDist < _anchor - MinProgressMeters) { Reset(); _inEpisode = true; _anchor = (_best = (_last = o.GoalDist)); return false; } _last = o.GoalDist; if (o.GoalDist < _best) { _best = o.GoalDist; } _elapsed += o.Dt; if (!_tripped && _elapsed >= TripSeconds) { _tripped = true; return true; } return false; } public static bool NearOrigin(float x, float y, float z, float radius = 5f) { return x * x + y * y + z * z <= radius * radius; } } public static class SwingVocabulary { public static bool IsSpellShaped(int type) { return type >= 2; } public static string TriggerFor(int type) { if (type != 0) { return "Attack2"; } return "Attack1"; } } public static class TargetPick { public struct Candidate { public float X; public float Z; } public static int PickFrontTarget(float px, float pz, float fwdX, float fwdZ, float maxRange, float coneDegrees, IList candidates) { if (candidates == null || candidates.Count == 0 || maxRange <= 0f) { return -1; } double num = Math.Sqrt((double)fwdX * (double)fwdX + (double)fwdZ * (double)fwdZ); if (num < 1E-06) { return -1; } double num2 = (double)fwdX / num; double num3 = (double)fwdZ / num; double num4 = Math.Cos(Math.Max(0.0, Math.Min(360.0, coneDegrees)) * 0.5 * Math.PI / 180.0); int result = -1; double num5 = maxRange; for (int i = 0; i < candidates.Count; i++) { double num6 = candidates[i].X - px; double num7 = candidates[i].Z - pz; double num8 = Math.Sqrt(num6 * num6 + num7 * num7); if (!(num8 > num5) && (!(num8 > 1E-06) || !(num6 / num8 * num2 + num7 / num8 * num3 < num4))) { num5 = num8; result = i; } } return result; } } public static class TemplateClearVerb { public const string PrebuiltOrigin = "prebuilt"; public static bool WantsAll(string[] parts) { if (parts == null) { return false; } for (int i = 1; i < parts.Length; i++) { if (string.Equals(parts[i], "all", StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public static bool ShouldKeep(string origin, bool all) { if (!all) { return string.Equals(origin, "prebuilt", StringComparison.OrdinalIgnoreCase); } return false; } public static string Summary(int cleared, int kept, bool all) { if (!all) { return $"[TEMPLATE] cleared {cleared} harvested body template(s), kept {kept} prebuilt ('templateclear all' clears those too)."; } return $"[TEMPLATE] cleared {cleared} cached body template(s) (ALL — prebuilt bundle bodies included; 'bundlebody reload' re-registers them)."; } } public enum UnloadAssetsMode { Off, EveryN, Always } public static class TerrainMaintenance { public static bool ShouldUnload(UnloadAssetsMode mode, int everyN, int harvestCount) { switch (mode) { case UnloadAssetsMode.Off: return false; case UnloadAssetsMode.Always: return true; case UnloadAssetsMode.EveryN: if (harvestCount <= 0) { return false; } if (everyN <= 1) { return true; } return harvestCount % everyN == 0; default: return true; } } } public static class TurnLaw { public const float FloorDeg = 15f; public const float MaxLookPitch = 0.7f; private const float RadToDeg = 57.29578f; public static float StepDeg(float angleDeg, float turnSpeed, float capDegPerSec, float dt) { if (dt <= 0f || angleDeg <= 0f || float.IsNaN(angleDeg)) { return 0f; } float num = (angleDeg + 15f) * turnSpeed * 57.29578f * dt; if (num < 0f) { num = 0f; } float num2 = capDegPerSec * dt; if (num2 >= 0f && num > num2) { num = num2; } if (!(num > angleDeg)) { return num; } return angleDeg; } public static bool Allowed(float normalizedDirY) { return Math.Abs(normalizedDirY) < 0.7f; } } public static class WorldPosition { public const float VoidFloorY = -3000f; public const float MaxSqrMagnitude = 100000000f; public static bool IsSane(float x, float y, float z) { if (y > -3000f) { return x * x + y * y + z * z < 100000000f; } return false; } } }