Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of The Salvager v0.8.2
plugins/SalvagerMod.dll
Decompiled 3 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using EntityStates; using EntityStates.AI.Walker; using EntityStates.Captain.Weapon; using EntityStates.Commando; using EntityStates.Commando.CommandoWeapon; using EntityStates.Croco; using EntityStates.Drone; using EntityStates.Drone.DroneWeapon; using EntityStates.GolemMonster; using EntityStates.Loader; using EntityStates.Toolbot; using Grumpy; using HG; using HG.BlendableTypes; using HG.Reflection; using KinematicCharacterController; using On.RoR2; using On.RoR2.UI; using R2API; using R2API.Utils; using RoR2; using RoR2.Achievements; using RoR2.Audio; using RoR2.CharacterAI; using RoR2.ContentManagement; using RoR2.Orbs; using RoR2.Projectile; using RoR2.Skills; using RoR2.UI; using SalvagerMod.Art; using SalvagerMod.Combat; using SalvagerMod.DevTools; using SalvagerMod.Modules; using SalvagerMod.Modules.Characters; using SalvagerMod.Network; using SalvagerMod.Progression; using SalvagerMod.Survivors.Salvager; using SalvagerMod.Survivors.Salvager.Components; using SalvagerMod.Survivors.Salvager.SkillStates; using SalvagerMod.Survivors.Salvager.SkillStates.Harpoon; using SalvagerMod.Survivors.Salvager.SkillStates.RailSpike; using SalvagerMod.Survivors.Salvager.SkillStates.Weapons; using SalvagerMod.UI; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.Networking; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: OptIn] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SalvagerMod")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.8.2.0")] [assembly: AssemblyInformationalVersion("0.8.2+e3c42bfefc83073176a8d983f309d150e8a94382")] [assembly: AssemblyProduct("SalvagerMod")] [assembly: AssemblyTitle("SalvagerMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.8.2.0")] [module: UnverifiableCode] namespace SalvagerMod { internal static class Log { internal static ManualLogSource _logSource; internal static void Init(ManualLogSource logSource) { _logSource = logSource; } internal static void Debug(object data) { _logSource.LogDebug(data); } internal static void Error(object data) { _logSource.LogError(data); } internal static void ErrorAssetBundle(string assetName, string bundleName) { Error("failed to load asset, " + assetName + ", because it does not exist in asset bundle, " + bundleName); } internal static void Fatal(object data) { _logSource.LogFatal(data); } internal static void Info(object data) { _logSource.LogInfo(data); } internal static void Message(object data) { _logSource.LogMessage(data); } internal static void Warning(object data) { _logSource.LogWarning(data); } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.lukem.salvager", "Salvager", "0.8.2")] public class SalvagerPlugin : BaseUnityPlugin { public const string MODUID = "com.lukem.salvager"; public const string MODNAME = "Salvager"; public const string MODVERSION = "0.8.2"; public const string DEVELOPER_PREFIX = "LUKEM"; public static SalvagerPlugin instance; public static ConfigEntry<bool> VerboseLogging; private void Awake() { instance = this; Log.Init(((BaseUnityPlugin)this).Logger); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseLogging", false, "Development diagnostics (catalog registration, skin/model checks, per-shot debug lines, stack traces for warnings). Multiplayer reports (Scrap awards, upgrades, SALVAGER REPORT blocks) are always written to the log."); SalvagerConfig.Init(((BaseUnityPlugin)this).Config); DevLogTrace.Init(); Language.Init(); new SalvagerSurvivor().Initialize(); SalvageAwarder.Init(); ReclamationMarks.Init(); ArcChains.Init(); SalvagerCombatHooks.Init(); RailSpikeImpale.Init(); SalvagerReport.Init(); SelectorInputGuards.Init(); new ContentPacks().Initialize(); Log.Info("Salvager 0.8.2 Awake complete (public alpha)."); } } } namespace SalvagerMod.UI { public sealed class EvolutionHudRows { public bool isMax; public string topLeft; public string topRight; public float fill; public string bottomLabel; public string bottomValue; public string bottomRight; } public sealed class EvolutionBanner { public string header; public string tierLabel; public string name; public string more; public bool showCheck; public string description; } public static class EvolutionHudModel { public static EvolutionHudRows Build(int total, int tier, int[] thresholds, bool showNext, string nextOptionName, string maxTierName) { if (thresholds == null || thresholds.Length < 4) { thresholds = EvolutionSettings.DefaultThresholds; } total = Math.Max(0, total); tier = Math.Max(0, Math.Min(4, tier)); if (tier >= 4) { return new EvolutionHudRows { isMax = true, topLeft = (string.IsNullOrEmpty(maxTierName) ? "APEX RECLAIMER" : maxTierName), topRight = $"{total} SCRAP", fill = 1f, bottomLabel = "MAX EVOLUTION", bottomValue = null, bottomRight = null }; } int num = EvolutionRules.ThresholdForTier(tier, thresholds); int num2 = EvolutionRules.ThresholdForTier(tier + 1, thresholds); float num3 = ((num2 > num) ? ((float)(total - num) / (float)(num2 - num)) : 1f); EvolutionHudRows evolutionHudRows = new EvolutionHudRows { isMax = false, topLeft = $"SCRAP {total} / {num2}", topRight = $"TIER {tier:00}", fill = ((num3 < 0f) ? 0f : ((num3 > 1f) ? 1f : num3)) }; if (showNext) { evolutionHudRows.bottomLabel = "NEXT:"; evolutionHudRows.bottomValue = (string.IsNullOrEmpty(nextOptionName) ? "waiting for loadout..." : nextOptionName); evolutionHudRows.bottomRight = num2.ToString(); } return evolutionHudRows; } public static EvolutionBanner InstalledBanner(int tier, string optionName, int installedCount, string description = null) { int num = Math.Max(0, installedCount - 1); return new EvolutionBanner { description = PlainText(description), header = "UPGRADE INSTALLED", tierLabel = $"TIER {Math.Max(0, tier):00}", name = (string.IsNullOrEmpty(optionName) ? "UNKNOWN UPGRADE" : optionName.ToUpperInvariant()), more = ((num > 0) ? $"+{num} MORE" : null), showCheck = true }; } public static string PlainText(string text) { if (string.IsNullOrEmpty(text)) { return null; } string input = Regex.Replace(text, "<[^>]*>", ""); input = Regex.Replace(input, "\\s+", " ").Trim(); if (input.Length != 0) { return input; } return null; } public static float HoldSeconds(EvolutionBanner b) { if (b == null || string.IsNullOrEmpty(b.description)) { return 3.6f; } return 8f; } public static EvolutionBanner ReadyBanner(int tier, string toggleKey) { return new EvolutionBanner { header = "EVOLUTION READY", tierLabel = $"TIER {Math.Max(0, tier):00}", name = "PRESS " + toggleKey + " TO CHOOSE", more = null, showCheck = false }; } } public enum SelectorEvent { None, Opened, Closed, NothingPending, Highlighted, NeedHighlight, Busy, RequestSent, Installed, Advanced, AllResolved, NoConfirmation } public sealed class EvolutionSelectorModel { private float awaitStartedAt; public bool IsOpen { get; private set; } public int ShownTier { get; private set; } public int Highlight { get; private set; } = -1; public bool AwaitingServer { get; private set; } public int AwaitTier { get; private set; } public int AwaitOption { get; private set; } = -1; public SelectorEvent Toggle(int oldestPendingTier) { if (IsOpen) { return Close(); } if (oldestPendingTier <= 0) { return SelectorEvent.NothingPending; } IsOpen = true; ShownTier = oldestPendingTier; Highlight = -1; return SelectorEvent.Opened; } public SelectorEvent Close() { if (!IsOpen) { return SelectorEvent.None; } IsOpen = false; Highlight = -1; return SelectorEvent.Closed; } public SelectorEvent SetHighlight(int option) { if (!IsOpen || AwaitingServer || option < 0 || option > 2) { return SelectorEvent.None; } Highlight = option; return SelectorEvent.Highlighted; } public SelectorEvent Confirm(float now) { if (!IsOpen) { return SelectorEvent.None; } if (AwaitingServer) { return SelectorEvent.Busy; } if (Highlight < 0) { return SelectorEvent.NeedHighlight; } AwaitingServer = true; AwaitTier = ShownTier; AwaitOption = Highlight; awaitStartedAt = now; return SelectorEvent.RequestSent; } public void CancelAwait() { AwaitingServer = false; } public SelectorEvent OnSnapshot(int oldestPendingTier, int chosenForAwaitTier) { SelectorEvent selectorEvent = SelectorEvent.None; if (AwaitingServer && chosenForAwaitTier >= 0) { AwaitingServer = false; selectorEvent = SelectorEvent.Installed; } if (oldestPendingTier <= 0) { if (IsOpen) { IsOpen = false; Highlight = -1; ShownTier = 0; } return selectorEvent switch { SelectorEvent.None => SelectorEvent.AllResolved, SelectorEvent.Installed => SelectorEvent.Installed, _ => selectorEvent, }; } if (ShownTier != oldestPendingTier) { ShownTier = oldestPendingTier; Highlight = -1; if (selectorEvent == SelectorEvent.None && IsOpen) { selectorEvent = SelectorEvent.Advanced; } } return selectorEvent; } public SelectorEvent Tick(float now, float confirmTimeout) { if (AwaitingServer && now - awaitStartedAt > confirmTimeout) { AwaitingServer = false; return SelectorEvent.NoConfirmation; } return SelectorEvent.None; } } public class SalvagerHud : MonoBehaviour { private const float ConfirmTimeout = 3f; private SalvagerEvolutionNetworker net; private CharacterBody body; private readonly EvolutionSelectorModel selector = new EvolutionSelectorModel(); private string statusText; private float statusUntil; private EvolutionBanner banner; private float bannerStart = -100f; private float displayedFill; private int displayedTier = -1; private Texture2D white; private Texture2D checkIcon; private GUIStyle styleLabel; private GUIStyle styleLabelRight; private GUIStyle styleSmall; private GUIStyle styleTitle; private GUIStyle styleCardTitle; private GUIStyle styleCardBody; private GUIStyle styleFooter; private GUIStyle styleTop; private GUIStyle styleTopRight; private GUIStyle styleTopRightMuted; private GUIStyle styleBottomLabel; private GUIStyle styleBottomRight; private GUIStyle styleMaxEvolution; private GUIStyle styleBannerHeader; private GUIStyle styleBannerTier; private GUIStyle styleBannerName; private GUIStyle styleBannerMore; private GUIStyle styleBannerDesc; private GUIStyle styleTopMax; private static readonly Color Amber = new Color(0.918f, 0.702f, 0.302f); private static readonly Color Orange = new Color(0.78f, 0.427f, 0.145f); private static readonly Color Gunmetal = new Color(0.275f, 0.294f, 0.302f); private static readonly Color Panel = new Color(0.055f, 0.06f, 0.075f, 0.8f); private static readonly Color Track = new Color(1f, 1f, 1f, 0.1f); private static readonly Color Muted = new Color(0.62f, 0.61f, 0.58f); private static readonly Color TextWhite = new Color(0.95f, 0.95f, 0.93f); private const float PanelWidth = 440f; private const float PadX = 16f; private const float PadY = 10f; private const float AccentWidth = 3f; private const float TopRowHeight = 24f; private const float BarHeight = 5f; private const float BottomRowHeight = 18f; private const float RowGap = 7f; private const float ColumnGap = 16f; private const float BannerHeaderHeight = 16f; private const float BannerNameHeight = 28f; private const float BannerRowGap = 3f; private const float BannerPadY = 9f; private const float StackGap = 8f; private const float ScreenMargin = 16f; private const float BottomOffset = 150f; private const int MinFontSize = 10; private const float BannerFadeIn = 0.18f; private const float BannerFadeOut = 0.6f; private const float BannerSlide = 6f; private const float BannerDescMaxHeight = 72f; private const float BarEaseRate = 10f; private readonly Dictionary<GUIStyle, GUIStyle> fitStyles = new Dictionary<GUIStyle, GUIStyle>(); private bool IsLocalOwner { get { if (Object.op_Implicit((Object)(object)body)) { return body.hasEffectiveAuthority; } return false; } } public static float BannerHeight => 65f; private void Start() { net = ((Component)this).GetComponent<SalvagerEvolutionNetworker>(); body = ((Component)this).GetComponent<CharacterBody>(); if (!Object.op_Implicit((Object)(object)net) || !Object.op_Implicit((Object)(object)body)) { ((Behaviour)this).enabled = false; } else { net.onSnapshotChanged += OnSnapshotChanged; } } private void OnDestroy() { if (Object.op_Implicit((Object)(object)net)) { net.onSnapshotChanged -= OnSnapshotChanged; } if (selector.IsOpen) { SelectorInputGuards.SetSelectorOpen(open: false); } if (Object.op_Implicit((Object)(object)white)) { Object.Destroy((Object)(object)white); } if (Object.op_Implicit((Object)(object)checkIcon)) { Object.Destroy((Object)(object)checkIcon); } } private void SetStatus(string text, float seconds = 4f) { statusText = text; statusUntil = Time.unscaledTime + seconds; } private void Sound(string evt) { Util.PlaySound(evt, ((Component)this).gameObject); } private void OnSnapshotChanged(int previousTier) { if (IsLocalOwner) { int num = (selector.AwaitingServer ? net.GetChoice(selector.AwaitTier) : (-1)); if (selector.OnSnapshot(net.mirror.OldestPendingTier, num) == SelectorEvent.Installed) { SetStatus("INSTALLED: " + OptionName(selector.AwaitTier, num)); Sound("Play_UI_menuClick"); } if (previousTier >= 0 && net.reachedTier > previousTier) { int reachedTier = net.reachedTier; banner = (net.autoInstall ? EvolutionHudModel.InstalledBanner(reachedTier, OptionName(reachedTier, net.GetChoice(reachedTier)), reachedTier - previousTier, OptionDescription(reachedTier, net.GetChoice(reachedTier))) : EvolutionHudModel.ReadyBanner(reachedTier, KeyName(SalvagerConfig.SelectorToggleKey))); bannerStart = Time.unscaledTime; Sound("Play_UI_skill_unlock"); } if (net.autoInstall && selector.IsOpen) { selector.Close(); } SelectorInputGuards.SetSelectorOpen(selector.IsOpen); } } private static bool Pressed(ConfigEntry<KeyboardShortcut> key, KeyCode alternate = (KeyCode)0) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = key.Value; if (!((KeyboardShortcut)(ref value)).IsDown()) { if ((int)alternate != 0) { return Input.GetKeyDown(alternate); } return false; } return true; } private static bool InputBlocked() { if (!Object.op_Implicit((Object)(object)ConsoleWindow.instance) && !ChatBox.IsActive) { return PauseManager.isPaused; } return true; } private void Update() { if (!IsLocalOwner || !net.hasSnapshot) { return; } float unscaledTime = Time.unscaledTime; EaseProgressBar(); if (selector.Tick(unscaledTime, 3f) == SelectorEvent.NoConfirmation) { SetStatus("Not confirmed by the server - nothing was installed. Try again."); Sound("Play_UI_insufficient_funds"); } if (net.autoInstall || InputBlocked()) { return; } if (Pressed(SalvagerConfig.SelectorToggleKey, (KeyCode)0)) { switch (selector.Toggle(net.mirror.OldestPendingTier)) { case SelectorEvent.Opened: case SelectorEvent.Closed: Sound("Play_UI_menuClick"); break; case SelectorEvent.NothingPending: SetStatus("No evolution ready yet"); break; } } else if (selector.IsOpen) { if (Pressed(SalvagerConfig.SelectorCloseKey, (KeyCode)0)) { SelectorInputGuards.NoteCloseKeyUsed(); selector.Close(); Sound("Play_UI_menuClick"); } else { int num = ((!Pressed(SalvagerConfig.SelectorOption1Key, (KeyCode)257)) ? (Pressed(SalvagerConfig.SelectorOption2Key, (KeyCode)258) ? 1 : (Pressed(SalvagerConfig.SelectorOption3Key, (KeyCode)259) ? 2 : (-1))) : 0); if (num >= 0 && selector.SetHighlight(num) == SelectorEvent.Highlighted) { Sound("Play_UI_menuHover"); } if (Pressed(SalvagerConfig.SelectorConfirmKey, (KeyCode)271)) { SelectorInputGuards.NoteConfirmKeyUsed(); switch (selector.Confirm(unscaledTime)) { case SelectorEvent.RequestSent: if (net.RequestUpgrade(selector.AwaitTier, selector.AwaitOption)) { Sound("Play_UI_menuClick"); break; } selector.CancelAwait(); SetStatus("Could not send the request"); break; case SelectorEvent.NeedHighlight: SetStatus("Highlight an upgrade first (" + KeyName(SalvagerConfig.SelectorOption1Key) + "/" + KeyName(SalvagerConfig.SelectorOption2Key) + "/" + KeyName(SalvagerConfig.SelectorOption3Key) + ")"); Sound("Play_UI_insufficient_funds"); break; } } } } SelectorInputGuards.SetSelectorOpen(selector.IsOpen); } private void OnGUI() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) if (IsLocalOwner && Object.op_Implicit((Object)(object)net) && net.hasSnapshot && SalvagerConfig.ShowHud.Value && !PauseManager.isPaused) { EnsureStyles(); float num = (float)Screen.height / 1080f * Mathf.Clamp(SalvagerConfig.HudScale.Value, 0.5f, 3f); Matrix4x4 matrix = GUI.matrix; GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f)); float vw = (float)Screen.width / num; float vh = (float)Screen.height / num; float counterTop = DrawCounter(vw, vh); if (selector.IsOpen) { DrawSelector(vw, counterTop); } else { DrawBanner(vw, counterTop); } GUI.matrix = matrix; } } private static float SharedWidth(float vw) { return Mathf.Min(440f, vw - 32f); } public static float CounterHeight(int extraLines) { return 71f + (float)extraLines * 18f + 10f; } private void EaseProgressBar() { EvolutionHudRows evolutionHudRows = EvolutionHudModel.Build(net.salvageTotal, net.reachedTier, net.thresholds, showNext: false, null, null); if (displayedTier != net.reachedTier) { displayedFill = ((displayedTier < 0) ? evolutionHudRows.fill : (evolutionHudRows.isMax ? displayedFill : 0f)); displayedTier = net.reachedTier; } displayedFill = Mathf.Lerp(displayedFill, evolutionHudRows.fill, 1f - Mathf.Exp(-10f * Time.unscaledDeltaTime)); if (Mathf.Abs(displayedFill - evolutionHudRows.fill) < 0.001f) { displayedFill = evolutionHudRows.fill; } } private float DrawCounter(float vw, float vh) { //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) int reachedTier = net.reachedTier; string nextOptionName = null; if (net.autoInstall && reachedTier < 4) { int planned = net.GetPlanned(reachedTier + 1); if (planned >= 0) { nextOptionName = OptionName(reachedTier + 1, planned); } } EvolutionHudRows evolutionHudRows = EvolutionHudModel.Build(net.salvageTotal, reachedTier, net.thresholds, net.autoInstall, nextOptionName, TierName(4)); int num = ((!net.autoInstall) ? net.mirror.PendingCount : 0); bool flag = !string.IsNullOrEmpty(statusText) && Time.unscaledTime < statusUntil; string text = null; if (num > 0) { bool flag2 = !selector.IsOpen && Mathf.Repeat(Time.unscaledTime, 1f) < 0.6f; text = "<color=#" + (flag2 ? "EAB34D" : "C76D25") + ">EVOLUTION READY</color>" + ((num > 1) ? $" x{num}" : "") + (selector.IsOpen ? "" : (" [" + KeyName(SalvagerConfig.SelectorToggleKey) + "] to choose")); } int extraLines = ((text != null && evolutionHudRows.bottomLabel != null) ? 1 : 0) + (flag ? 1 : 0); float num2 = SharedWidth(vw); float num3 = CounterHeight(extraLines); float num4 = (vw - num2) * 0.5f; float num5 = vh - 150f - num3; float num6 = num2 - 32f; DrawPanel(new Rect(num4, num5, num2, num3), 1f); float num7 = num5 + 10f; DrawRow(new Rect(num4 + 16f, num7, num6, 24f), evolutionHudRows.topLeft, evolutionHudRows.isMax ? styleTopMax : styleTop, evolutionHudRows.topRight, evolutionHudRows.isMax ? styleTopRightMuted : styleTopRight); num7 += 31f; Rect r = default(Rect); ((Rect)(ref r))..ctor(num4 + 16f, num7, num6, 5f); Fill(r, Track); Fill(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width * Mathf.Clamp01(displayedFill), ((Rect)(ref r)).height), evolutionHudRows.isMax ? Orange : Amber); num7 += 12f; Rect val = default(Rect); ((Rect)(ref val))..ctor(num4 + 16f, num7, num6, 18f); if (evolutionHudRows.isMax) { FitLabel(val, evolutionHudRows.bottomLabel, styleMaxEvolution); } else if (evolutionHudRows.bottomLabel != null) { string left = evolutionHudRows.bottomLabel + " <color=#EAB34D>" + evolutionHudRows.bottomValue + "</color>"; DrawRow(val, left, styleBottomLabel, evolutionHudRows.bottomRight, styleBottomRight); } else if (text != null) { FitLabel(val, text, styleBottomLabel); text = null; } num7 += 18f; if (text != null) { FitLabel(new Rect(num4 + 16f, num7, num6, 18f), text, styleBottomLabel); num7 += 18f; } if (flag) { FitLabel(new Rect(num4 + 16f, num7, num6, 18f), statusText, styleBottomLabel); } return num5; } private void DrawBanner(float vw, float counterTop) { //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) if (banner == null) { return; } float num = Time.unscaledTime - bannerStart; float num2 = EvolutionHudModel.HoldSeconds(banner); float num3 = 0.18f + num2 + 0.6f; if (!(num < 0f) && !(num > num3)) { float num4 = ((num < 0.18f) ? (num / 0.18f) : ((num > 0.18f + num2) ? (1f - (num - 0.18f - num2) / 0.6f) : 1f)); num4 = Mathf.SmoothStep(0f, 1f, Mathf.Clamp01(num4)); float num5 = ((num < 0.18f) ? ((1f - num / 0.18f) * 6f) : 0f); float num6 = SharedWidth(vw); float num7 = num6 - 32f; float num8 = 0f; GUIContent val = (string.IsNullOrEmpty(banner.description) ? ((GUIContent)null) : new GUIContent(banner.description)); if (val != null) { num8 = Mathf.Min(72f, styleBannerDesc.CalcHeight(val, num7)) + 3f; } float num9 = BannerHeight + num8; float num10 = (vw - num6) * 0.5f; float num11 = counterTop - 8f - num9 + num5; Color color = GUI.color; GUI.color = new Color(1f, 1f, 1f, num4); DrawPanel(new Rect(num10, num11, num6, num9), num4); float num12 = num11 + 9f; Rect row = default(Rect); ((Rect)(ref row))..ctor(num10 + 16f, num12, num7, 16f); if (banner.showCheck && Object.op_Implicit((Object)(object)checkIcon)) { float num13 = 12f; GUI.DrawTexture(new Rect(((Rect)(ref row)).x, ((Rect)(ref row)).y + 2f, num13, num13), (Texture)(object)checkIcon); ((Rect)(ref row))..ctor(((Rect)(ref row)).x + num13 + 6f, ((Rect)(ref row)).y, ((Rect)(ref row)).width - num13 - 6f, ((Rect)(ref row)).height); } DrawRow(row, banner.header, styleBannerHeader, banner.tierLabel, styleBannerTier); num12 += 19f; DrawRow(new Rect(num10 + 16f, num12, num7, 28f), banner.name, styleBannerName, banner.more, styleBannerMore); if (val != null) { num12 += 31f; GUI.Label(new Rect(num10 + 16f, num12, num7, num8 - 3f), val, styleBannerDesc); } GUI.color = color; } } private void DrawPanel(Rect r, float alpha) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) Fill(r, new Color(Panel.r, Panel.g, Panel.b, Panel.a * alpha)); Fill(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, 3f, ((Rect)(ref r)).height), new Color(Orange.r, Orange.g, Orange.b, alpha)); } private void DrawRow(Rect row, string left, GUIStyle leftStyle, string right, GUIStyle rightStyle) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) float num = (string.IsNullOrEmpty(right) ? 0f : Mathf.Min(rightStyle.CalcSize(new GUIContent(right)).x, ((Rect)(ref row)).width * 0.45f)); float num2 = ((num > 0f) ? 16f : 0f); FitLabel(new Rect(((Rect)(ref row)).x, ((Rect)(ref row)).y, Mathf.Max(0f, ((Rect)(ref row)).width - num - num2), ((Rect)(ref row)).height), left, leftStyle); if (num > 0f) { FitLabel(new Rect(((Rect)(ref row)).xMax - num, ((Rect)(ref row)).y, num, ((Rect)(ref row)).height), right, rightStyle); } } private void FitLabel(Rect r, string text, GUIStyle baseStyle) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrEmpty(text) && !(((Rect)(ref r)).width <= 0f)) { if (!fitStyles.TryGetValue(baseStyle, out var value)) { value = new GUIStyle(baseStyle) { wordWrap = false, clipping = (TextClipping)1 }; fitStyles[baseStyle] = value; } GUIContent val = new GUIContent(text); int num = (value.fontSize = baseStyle.fontSize); while (num > 10 && value.CalcSize(val).x > ((Rect)(ref r)).width) { num = (value.fontSize = num - 1); } GUI.Label(r, val, value); } } private void DrawSelector(float vw, float counterTop) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) int shownTier = selector.ShownTier; if (shownTier <= 0) { return; } float num = (vw - 760f) * 0.5f; float num2 = counterTop - 176f - 8f; Fill(new Rect(num, num2, 760f, 176f), Panel); Fill(new Rect(num, num2, 760f, 3f), Orange); int pendingCount = net.mirror.PendingCount; string text = string.Format("TIER {0:00} {1} - {2}", shownTier, TierName(shownTier), Language.GetString("LUKEM_SALVAGER_TIER_" + shownTier + "_PROMPT")) + ((pendingCount > 1) ? $" <color=#85827A>({pendingCount - 1} more queued)</color>" : ""); GUI.Label(new Rect(num + 14f, num2 + 8f, 732f, 26f), text, styleTitle); float num3 = 237.33333f; Rect r = default(Rect); for (int i = 0; i < 3; i++) { bool flag = selector.Highlight == i; ((Rect)(ref r))..ctor(num + 14f + (float)i * (num3 + 10f), num2 + 40f, num3, 100f); Fill(r, flag ? new Color(0.2f, 0.17f, 0.11f, 0.97f) : new Color(0.12f, 0.125f, 0.14f, 0.95f)); Fill(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width, flag ? 3f : 1f), flag ? Amber : Gunmetal); if (flag) { Fill(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + ((Rect)(ref r)).height - 3f, ((Rect)(ref r)).width, 3f), Amber); } string text2 = KeyName((ConfigEntry<KeyboardShortcut>)(i switch { 1 => SalvagerConfig.SelectorOption2Key, 0 => SalvagerConfig.SelectorOption1Key, _ => SalvagerConfig.SelectorOption3Key, })); GUI.Label(new Rect(((Rect)(ref r)).x + 8f, ((Rect)(ref r)).y + 6f, ((Rect)(ref r)).width - 16f, 24f), (flag ? "<color=#EAB34D>> </color>" : "") + "[" + text2 + "] " + OptionName(shownTier, i), styleCardTitle); GUI.Label(new Rect(((Rect)(ref r)).x + 8f, ((Rect)(ref r)).y + 30f, ((Rect)(ref r)).width - 16f, ((Rect)(ref r)).height - 34f), OptionDescription(shownTier, i), styleCardBody); } string text3 = (selector.AwaitingServer ? "<color=#EAB34D>Installing... waiting for server confirmation</color>" : ("[1/2/3] highlight [" + KeyName(SalvagerConfig.SelectorConfirmKey) + "] install [" + KeyName(SalvagerConfig.SelectorCloseKey) + "] close - game keeps running")); GUI.Label(new Rect(num + 14f, num2 + 176f - 30f, 732f, 24f), text3, styleFooter); } private static string KeyName(ConfigEntry<KeyboardShortcut> key) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) return ((object)key.Value/*cast due to .constrained prefix*/).ToString().Replace("Alpha", "").Replace("Return", "Enter"); } private static string TierName(int tier) { return Language.GetString("LUKEM_SALVAGER_TIER_" + EvolutionRules.TierIds[Mathf.Clamp(tier, 0, 4)] + "_NAME"); } private static string OptionName(int tier, int option) { if (!EvolutionRules.IsValidOption(tier, option)) { return "?"; } return Language.GetString("LUKEM_SALVAGER_OPT_" + EvolutionRules.OptionIds[tier][option] + "_NAME"); } private static string OptionDescription(int tier, int option) { if (!EvolutionRules.IsValidOption(tier, option)) { return ""; } return Language.GetString("LUKEM_SALVAGER_OPT_" + EvolutionRules.OptionIds[tier][option] + "_DESC"); } private void Fill(Rect r, Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = new Color(c.r, c.g, c.b, c.a * color.a); GUI.DrawTexture(r, (Texture)(object)white); GUI.color = color; } private void EnsureStyles() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected O, but got Unknown //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) if (styleLabel == null) { white = new Texture2D(1, 1); white.SetPixel(0, 0, Color.white); white.Apply(); styleLabel = new GUIStyle(GUI.skin.label) { fontSize = 20, fontStyle = (FontStyle)1, richText = true }; styleLabel.normal.textColor = Color.white; styleLabelRight = new GUIStyle(styleLabel) { alignment = (TextAnchor)2 }; styleLabelRight.normal.textColor = Amber; styleSmall = new GUIStyle(GUI.skin.label) { fontSize = 15, richText = true }; styleSmall.normal.textColor = new Color(0.85f, 0.85f, 0.85f); styleTitle = new GUIStyle(styleLabel) { fontSize = 18 }; styleTitle.normal.textColor = Amber; styleCardTitle = new GUIStyle(styleLabel) { fontSize = 16 }; styleCardBody = new GUIStyle(GUI.skin.label) { fontSize = 13, wordWrap = true, richText = true }; styleCardBody.normal.textColor = new Color(0.86f, 0.86f, 0.86f); styleFooter = new GUIStyle(styleSmall) { alignment = (TextAnchor)4, fontSize = 14 }; styleTop = Style(19, (FontStyle)1, TextWhite, (TextAnchor)3); styleTopMax = Style(19, (FontStyle)1, Amber, (TextAnchor)3); styleTopRight = Style(16, (FontStyle)1, Amber, (TextAnchor)5); styleTopRightMuted = Style(16, (FontStyle)1, TextWhite, (TextAnchor)5); styleBottomLabel = Style(14, (FontStyle)0, Muted, (TextAnchor)3); styleBottomRight = Style(14, (FontStyle)1, Muted, (TextAnchor)5); styleMaxEvolution = Style(14, (FontStyle)1, Orange, (TextAnchor)3); styleBannerHeader = Style(12, (FontStyle)1, Amber, (TextAnchor)3); styleBannerTier = Style(12, (FontStyle)1, Muted, (TextAnchor)5); styleBannerName = Style(22, (FontStyle)1, TextWhite, (TextAnchor)3); styleBannerMore = Style(12, (FontStyle)1, Amber, (TextAnchor)5); styleBannerDesc = Style(14, (FontStyle)0, new Color(0.86f, 0.86f, 0.84f), (TextAnchor)0); styleBannerDesc.wordWrap = true; styleBannerDesc.richText = false; styleBannerDesc.clipping = (TextClipping)1; checkIcon = MakeCheckIcon(32, Amber); } } private static GUIStyle Style(int size, FontStyle fontStyle, Color color, TextAnchor anchor) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = size, fontStyle = fontStyle, alignment = anchor, richText = true, wordWrap = false }; val.normal.textColor = color; return val; } private static Texture2D MakeCheckIcon(int size, Color color) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(size, size, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)1 }; Vector2 a = new Vector2(0.16f, 0.52f) * (float)size; Vector2 val2 = new Vector2(0.4f, 0.26f) * (float)size; Vector2 b = new Vector2(0.86f, 0.78f) * (float)size; float num = (float)size * 0.09f; Vector2 p = default(Vector2); for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { ((Vector2)(ref p))..ctor((float)j + 0.5f, (float)i + 0.5f); float num2 = Mathf.Min(SegmentDistance(p, a, val2), SegmentDistance(p, val2, b)); float num3 = Mathf.Clamp01(num - num2 + 0.5f); val.SetPixel(j, i, new Color(color.r, color.g, color.b, num3)); } } val.Apply(); return val; } private static float SegmentDistance(Vector2 p, Vector2 a, Vector2 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Vector2 val = b - a; float num = Mathf.Clamp01(Vector2.Dot(p - a, val) / ((Vector2)(ref val)).sqrMagnitude); return Vector2.Distance(p, a + val * num); } } internal static class SelectorInputGuards { private static bool installed; private static bool selectorOpen; private static float lastConfirmKeyAt = -10f; private static float lastCloseKeyAt = -10f; private const float Window = 0.25f; internal static void Init() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown if (!installed) { installed = true; ChatBox.Update += new hook_Update(ChatBox_Update); PauseManager.CCTogglePause += new hook_CCTogglePause(PauseManager_CCTogglePause); } } internal static void SetSelectorOpen(bool open) { selectorOpen = open; } internal static void NoteConfirmKeyUsed() { lastConfirmKeyAt = Time.unscaledTime; } internal static void NoteCloseKeyUsed() { lastCloseKeyAt = Time.unscaledTime; } private static bool ConfirmIsEnter() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 KeyboardShortcut value = SalvagerConfig.SelectorConfirmKey.Value; KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey; if ((int)mainKey != 13) { return (int)mainKey == 271; } return true; } private static bool CloseIsEscape() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 KeyboardShortcut value = SalvagerConfig.SelectorCloseKey.Value; return (int)((KeyboardShortcut)(ref value)).MainKey == 27; } private static void ChatBox_Update(orig_Update orig, ChatBox self) { bool num = Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271); bool flag = (selectorOpen || Time.unscaledTime - lastConfirmKeyAt < 0.25f) && ConfirmIsEnter(); if (!(num && flag) || ChatBox.IsActive) { orig.Invoke(self); } } private static void PauseManager_CCTogglePause(orig_CCTogglePause orig, ConCommandArgs args) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) bool flag = (selectorOpen || Time.unscaledTime - lastCloseKeyAt < 0.25f) && CloseIsEscape(); if (!(!PauseManager.isPaused && flag) || !Input.GetKey((KeyCode)27)) { orig.Invoke(args); } } } } namespace SalvagerMod.Progression { public sealed class AbilitySettings { public float barrierPerSalvage = 0.02f; public float barrierMaxPerAward = 0.1f; public float armorPerTier = 3f; public float hoverMaxFallSpeed = 4f; public bool hoverEnabled = true; public int thrusterCharges = 1; public float thrusterCooldown = 6f; public float thrusterBurnSeconds = 1.8f; public float thrusterMinBurnSeconds = 0.25f; public float jetClimbSpeed = 8f; public float jetLevelClimbSpeed = 4f; public float jetHoldPitch = -35f; public float jetVerticalAcceleration = 50f; public float jetSpeedMultiplier = 1.8f; public float jetHorizontalAcceleration = 40f; public float thrusterRamDamage = 2.5f; public float thrusterRamProc = 1f; public float thrusterRamRadius = 2.6f; public float thrusterRamStunSeconds = 1f; public float thrusterRamForce = 1800f; public float thrusterExitSpeed = 1.8f; public float harpoonSpeed = 260f; public float harpoonRetractSpeed = 380f; public float harpoonRadius = 0.5f; public float harpoonKillRefund = 0.5f; public float pulledStunSeconds = 1f; public float grappleSlingshotUp = 9f; public float ignitionDamage = 6f; public float ignitionRadius = 15f; public float ignitionProc = 1f; public float ignitionForce = 2200f; public float overdriveMoveSpeedBonus = 0.2f; public float overdriveAuxFrequencyBonus = 0.35f; public float accelFeedDelay = 0.8f; public float accelFeedBonus = 0.2f; public float accelFeedGap = 0.6f; public float fragDamage = 1.5f; public float fragRadius = 5f; public float fragCooldown = 2f; public float overchargeThreshold = 7f; public float overchargeDamage = 2.5f; public float overchargeRadius = 4f; public float overchargeCooldown = 2.5f; public float overchargeExpireSeconds = 4f; public float overchargeSlowSeconds = 2f; public float rotaryInterval = 0.16f; public int rotaryBurstRounds = 10; public float rotaryBurstRecovery = 2f; public float rotaryDamagePerGun = 0.2f; public float rotaryProc = 0.08f; public float rotaryRange = 75f; public float turretYawSpeed = 600f; public float turretPitchSpeed = 400f; public float turretAimTolerance = 6f; public float missileInterval = 5f; public int missileCount = 3; public float missileDamage = 1.4f; public float missileProc = 0.2f; public float missileRange = 95f; public float missileConeHalfAngle = 85f; public float missileLockSeconds = 0.35f; public float teslaInterval = 3f; public int teslaTargets = 4; public float teslaRange = 35f; public float teslaDamage = 1.2f; public float teslaProc = 0.1f; public float weaponsFreePrimaryRate = 0.4f; public float weaponsFreeAuxFrequency = 0.4f; public float siegeProtocolArmor = 30f; public float siegeProtocolMoveMultiplier = 0.8f; public float siegeProtocolExplosionDamage = 1.8f; public float siegeProtocolExplosionInterval = 1.5f; public float siegeProtocolExplosionRadius = 5f; public int assimilationMaxDrones = 5; public float assimilationDroneSeconds = 20f; public float railSpikeChargeSeconds = 0.6f; public float railSpikeDamage = 6.5f; public float railSpikeCooldown = 9f; public float railSpikeRange = 80f; public int railSpikePierce = 3; public float railSpikeProcFirst = 1f; public float railSpikeProcRest = 0.5f; public float impaleSeconds = 4f; public float impaleDamage = 2f; public float impaleRadius = 5f; public float pistonCooldown = 7f; public float pistonCrouchSeconds = 0.12f; public float pistonLaunchSpeed = 25f; public float pistonHorizontalSpeed = 9f; public float pistonSlamSpeed = 55f; public float pistonDamage = 4f; public float pistonMaxDamage = 8f; public float pistonFullDamageFallSpeed = 50f; public float pistonRadius = 9f; public float pistonProc = 1f; public float pistonForce = 2400f; } public static class AbilityRules { public const int ModAcceleratedFeed = 0; public const int ModFragmentation = 1; public const int ModOvercharged = 2; public const int ShoulderRotary = 0; public const int ShoulderMissiles = 1; public const int ShoulderTesla = 2; public const int ApexWeaponsFree = 0; public const int ApexSiegeProtocol = 1; public const int ApexAssimilation = 2; public static float AcceleratedFeedMultiplier(float continuousFiringSeconds, AbilitySettings s) { if (!(continuousFiringSeconds >= s.accelFeedDelay)) { return 1f; } return 1f + s.accelFeedBonus; } public static float PrimaryRateMultiplier(bool accelFeedInstalled, float continuousFiringSeconds, bool overdrive, bool weaponsFreeInstalled, AbilitySettings s) { float num = (accelFeedInstalled ? AcceleratedFeedMultiplier(continuousFiringSeconds, s) : 1f); if (overdrive && weaponsFreeInstalled) { num *= 1f + s.weaponsFreePrimaryRate; } return num; } public static float AuxInterval(float baseInterval, bool overdrive, bool weaponsFreeInstalled, AbilitySettings s) { float num = 1f; if (overdrive) { num += s.overdriveAuxFrequencyBonus; } if (overdrive && weaponsFreeInstalled) { num += s.weaponsFreeAuxFrequency; } return baseInterval / num; } public static bool OverchargeAdd(ref float charge, ref float lastHitTime, float now, float coefficient, AbilitySettings s) { if (now - lastHitTime > s.overchargeExpireSeconds) { charge = 0f; } lastHitTime = now; charge += Math.Max(0f, coefficient); if (charge + 0.0001f < s.overchargeThreshold) { return false; } charge = 0f; return true; } public static float HoverVerticalSpeed(float vy, bool jumpHeld, bool grounded, AbilitySettings s) { if (!s.hoverEnabled || !jumpHeld || grounded) { return vy; } if (!(vy < 0f - s.hoverMaxFallSpeed)) { return vy; } return 0f - s.hoverMaxFallSpeed; } public static float BarrierForSalvage(int points, float fullHealth, AbilitySettings s) { if (points <= 0 || fullHealth <= 0f) { return 0f; } return Math.Min((float)points * s.barrierPerSalvage, s.barrierMaxPerAward) * fullHealth; } public static float TierArmor(int reachedTier, AbilitySettings s) { return (float)Math.Max(0, Math.Min(4, reachedTier)) * s.armorPerTier; } public static void StatBonuses(int reachedTier, bool overdrive, bool siegeProtocolInstalled, AbilitySettings s, out float armor, out float moveMultiplier) { armor = TierArmor(reachedTier, s); moveMultiplier = 1f; if (overdrive) { moveMultiplier += s.overdriveMoveSpeedBonus; if (siegeProtocolInstalled) { armor += s.siegeProtocolArmor; moveMultiplier *= s.siegeProtocolMoveMultiplier; } } } public static float HarpoonStep(float travelled, float speed, float dt, float range, out bool reachedMax) { float num = Math.Max(0f, speed * dt); if (travelled + num >= range) { reachedMax = true; return Math.Max(0f, range - travelled); } reachedMax = false; return num; } public static float HarpoonMissSeconds(float range, AbilitySettings s) { return range / Math.Max(1f, s.harpoonSpeed) + range / Math.Max(1f, s.harpoonRetractSpeed); } public static float RailSpikeChargeSeconds(float attackSpeed, AbilitySettings s) { return Math.Max(0.2f, s.railSpikeChargeSeconds / Math.Max(0.01f, attackSpeed)); } public static float RailSpikeProc(int enemyIndex, AbilitySettings s) { if (enemyIndex < 0 || enemyIndex >= Math.Max(1, s.railSpikePierce)) { return -1f; } if (enemyIndex != 0) { return s.railSpikeProcRest; } return s.railSpikeProcFirst; } public static float PistonLandingDamage(float fallSpeed, AbilitySettings s) { float num = ((s.pistonFullDamageFallSpeed > 0f) ? Math.Max(0f, Math.Min(1f, fallSpeed / s.pistonFullDamageFallSpeed)) : 1f); return s.pistonDamage + (s.pistonMaxDamage - s.pistonDamage) * num; } public static float JetClimbTarget(float aimPitchDegrees, AbilitySettings s) { if (aimPitchDegrees >= 30f) { return s.jetClimbSpeed; } if (aimPitchDegrees >= 0f) { return s.jetLevelClimbSpeed + (s.jetClimbSpeed - s.jetLevelClimbSpeed) * aimPitchDegrees / 30f; } float num = Math.Min(-1f, s.jetHoldPitch); if (aimPitchDegrees <= num) { return 0f; } return s.jetLevelClimbSpeed * (1f - aimPitchDegrees / num); } public static float JetFlySpeed(float moveSpeed, float baseMoveSpeed, AbilitySettings s, float maxScale = 1.5f) { float num = Math.Max(0.1f, baseMoveSpeed); float num2 = Math.Max(0.5f * num, Math.Min(maxScale * num, moveSpeed)); return s.jetSpeedMultiplier * num2; } public static void JetStep(ref float vx, ref float vy, ref float vz, float moveX, float moveZ, float flySpeed, float climbTarget, float gravityStep, float dt, AbilitySettings s) { float num = (float)Math.Sqrt(moveX * moveX + moveZ * moveZ); if (num > 0.1f) { float num2 = Math.Min(1f, num) / num; float num3 = moveX * num2 * flySpeed; float num4 = moveZ * num2 * flySpeed; float num5 = num3 - vx; float num6 = num4 - vz; float num7 = (float)Math.Sqrt(num5 * num5 + num6 * num6); float num8 = s.jetHorizontalAcceleration * dt; if (num7 <= num8) { vx = num3; vz = num4; } else { vx += num5 / num7 * num8; vz += num6 / num7 * num8; } } float num9 = climbTarget + gravityStep; float num10 = s.jetVerticalAcceleration * dt; vy = ((Math.Abs(num9 - vy) <= num10) ? num9 : (vy + (float)Math.Sign(num9 - vy) * num10)); } } public enum HarpoonOutcome : byte { Miss, Enemy, Terrain, InvalidTarget } public sealed class HarpoonSettings { public float damageCoefficient = 4f; public float procCoefficient = 1f; public float cooldownSeconds = 8f; public float rangeMeters = 125f; public float missRetryDelaySeconds = 0.3f; public float markDurationSeconds = 4f; public float markDirectPrimaryBonus = 0.2f; public int markKillBonusNormal = 1; public int markKillBonusElite = 2; public int markKillBonusBoss = 3; public float overdriveMarkBonusDamageCoefficient = 1.2f; public float lightPullStopDistance = 5f; public float lightPullSpeed = 60f; public float lightPullMaxSeconds = 2.2f; public float heavySelfPullMaxMeters = 25f; public float heavySelfPullSpeed = 36f; public float heavySelfPullHoldCheckSeconds = 0.12f; public bool largeHullIsHeavy = true; public float heavyMassThreshold = 250f; public float grappleMaxTetherSeconds = 4f; public float grappleReelSpeed = 40f; public float grappleReelAcceleration = 140f; public float grappleMaxSpeed = 50f; public float grappleArriveDistance = 2.5f; public float grappleClickPullSeconds = 0.5f; public float grappleReelCloseBoost = 0.2f; public float grappleGravityCompensation = 1f; public float grappleSidewaysDamping = 4f; } public static class CombatRules { public static bool HarpoonConsumesCooldown(HarpoonOutcome outcome) { if (outcome != HarpoonOutcome.Enemy) { return outcome == HarpoonOutcome.Terrain; } return true; } public static bool IsHeavy(bool isChampion, bool isBoss, bool ungrabbable, bool unmovable, bool hasMovableMotor, bool largeHull, float mass, bool largeHullIsHeavy, float heavyMassThreshold) { if (!(isChampion || isBoss || ungrabbable || unmovable) && hasMovableMotor && !(largeHullIsHeavy && largeHull)) { if (heavyMassThreshold > 0f) { return mass >= heavyMassThreshold; } return false; } return true; } public static int MarkKillBonus(SalvageCategory category, HarpoonSettings s) { switch (category) { case SalvageCategory.Boss: return s.markKillBonusBoss; case SalvageCategory.Elite: case SalvageCategory.Large: return s.markKillBonusElite; default: return s.markKillBonusNormal; } } public static float PullStepSpeed(float distance, float stopDistance, float maxSpeed, float deltaTime) { if (distance <= stopDistance || deltaTime <= 0f) { return 0f; } return Math.Min(maxSpeed, (distance - stopDistance) / deltaTime); } public static void GrappleVelocity(ref float vx, ref float vy, ref float vz, float dx, float dy, float dz, float reelSpeed, float accel, float maxSpeed, float dt) { float num = (float)Math.Sqrt(dx * dx + dy * dy + dz * dz); if (!(num < 0.0001f)) { dx /= num; dy /= num; dz /= num; float num2 = vx * dx + vy * dy + vz * dz; if (num2 < 0f) { vx -= dx * num2; vy -= dy * num2; vz -= dz * num2; num2 = 0f; } if (num2 < reelSpeed) { float num3 = Math.Min(accel * dt, reelSpeed - num2); vx += dx * num3; vy += dy * num3; vz += dz * num3; } float num4 = (float)Math.Sqrt(vx * vx + vy * vy + vz * vz); if (num4 > maxSpeed) { float num5 = maxSpeed / num4; vx *= num5; vy *= num5; vz *= num5; } } } public static void ReelVelocity(ref float vx, ref float vy, ref float vz, float dx, float dy, float dz, float attachDistance, float arriveDistance, float reelSpeed, float closeBoost, float accel, float maxSpeed, float sidewaysDamping, float gravityStep, float dt) { float num = (float)Math.Sqrt(dx * dx + dy * dy + dz * dz); if (num < 0.0001f) { return; } dx /= num; dy /= num; dz /= num; vy += gravityStep; float num2 = vx * dx + vy * dy + vz * dz; float num3 = vx - dx * num2; float num4 = vy - dy * num2; float num5 = vz - dz * num2; if (num2 < 0f) { num2 = 0f; } float num6 = Math.Max(attachDistance - arriveDistance, 0.001f); float num7 = Math.Min(1f, Math.Max(0f, (attachDistance - num) / num6)); float num8 = reelSpeed * (1f + closeBoost * num7); if (maxSpeed > 0f) { num8 = Math.Min(num8, maxSpeed); } if (num2 < num8) { num2 = Math.Min(num8, num2 + accel * dt); } float num9 = (float)Math.Exp((0f - Math.Max(0f, sidewaysDamping)) * dt); num3 *= num9; num4 *= num9; num5 *= num9; if (maxSpeed > 0f) { float num10 = (float)Math.Sqrt(num3 * num3 + num4 * num4 + num5 * num5); float num11 = (float)Math.Sqrt(Math.Max(0f, maxSpeed * maxSpeed - num2 * num2)); if (num10 > num11 && num10 > 1E-05f) { float num12 = num11 / num10; num3 *= num12; num4 *= num12; num5 *= num12; } } vx = dx * num2 + num3; vy = dy * num2 + num4; vz = dz * num2 + num5; } public static void ClampSpeed(ref float vx, ref float vy, ref float vz, float maxSpeed) { float num = (float)Math.Sqrt(vx * vx + vy * vy + vz * vz); if (maxSpeed > 0f && num > maxSpeed) { float num2 = maxSpeed / num; vx *= num2; vy *= num2; vz *= num2; } } public static void ScrapcasterVolley(bool overdrive, bool twinPortsEnabled, float baseDamage, float baseProc, int overdriveShots, float overdriveDamage, float overdriveProc, out int shots, out float damage, out float proc) { if (overdrive && twinPortsEnabled) { shots = Math.Max(1, overdriveShots); damage = overdriveDamage; proc = overdriveProc; } else { shots = 1; damage = baseDamage; proc = baseProc; } } } public enum GrappleInputEvent : byte { None, Pressed, Released, AutoPullEnded } public sealed class GrappleReelInput { private const float Epsilon = 0.0001f; private bool wasHeld; private float autoPullLeft; private bool autoPull; public bool Reeling { get; private set; } public bool AutoPulling => autoPull; public int ReelPresses { get; private set; } public float AutoPulledSeconds { get; private set; } public GrappleReelInput(bool heldAtAttach, float clickPullSeconds) { wasHeld = heldAtAttach; if (heldAtAttach) { Reeling = true; ReelPresses = 1; } else if (clickPullSeconds > 0f) { Reeling = true; autoPull = true; autoPullLeft = clickPullSeconds; } } public GrappleInputEvent Step(bool held, float dt) { GrappleInputEvent grappleInputEvent = GrappleInputEvent.None; if (held && !wasHeld) { Reeling = true; ReelPresses++; autoPull = false; grappleInputEvent = GrappleInputEvent.Pressed; } else if (!held && wasHeld) { Reeling = false; grappleInputEvent = GrappleInputEvent.Released; } else if (!held && autoPull) { if (autoPullLeft <= 0.0001f) { autoPull = false; Reeling = false; grappleInputEvent = GrappleInputEvent.AutoPullEnded; } else { autoPullLeft -= dt; AutoPulledSeconds += dt; } } else if (!held && !Reeling && grappleInputEvent == GrappleInputEvent.None) { grappleInputEvent = GrappleInputEvent.AutoPullEnded; } wasHeld = held; return grappleInputEvent; } } public enum SalvageCategory : byte { Normal, Elite, Large, Boss } public enum ChoiceResult : byte { Accepted, InvalidTier, TierNotReached, AlreadyChosen, NotOldestPending, InvalidOption, ManualPickDisabled } public sealed class EvolutionSettings { public static readonly int[] DefaultThresholds = new int[4] { 70, 190, 390, 640 }; public int[] tierThresholds = (int[])DefaultThresholds.Clone(); public int rewardNormal = 1; public int rewardElite = 4; public int rewardLarge = 8; public int rewardBoss = 20; public float radiusMeters = 70f; public float refundSecondsPerPoint = 0.15f; public float refundCapFractionPerCycle = 0.5f; public static bool TryParseThresholds(string text, out int[] thresholds, out string error) { thresholds = null; error = null; if (string.IsNullOrWhiteSpace(text)) { error = "empty"; return false; } string[] array = text.Split(','); if (array.Length != 4) { error = $"need exactly {4} values"; return false; } int[] array2 = new int[array.Length]; for (int i = 0; i < array.Length; i++) { if (!int.TryParse(array[i].Trim(), out array2[i])) { error = "'" + array[i].Trim() + "' is not an integer"; return false; } if (array2[i] <= 0) { error = "values must be positive"; return false; } if (i > 0 && array2[i] <= array2[i - 1]) { error = "values must be strictly increasing"; return false; } } thresholds = array2; return true; } } public static class EvolutionRules { public const int MaxTier = 4; public const int OptionsPerTier = 3; public static readonly string[][] OptionIds = new string[5][] { new string[0], new string[3] { "PRIMARY_SHREDDER", "PRIMARY_SIEGE", "PRIMARY_ARC" }, new string[3] { "MOD_ACCELERATED_FEED", "MOD_FRAGMENTATION", "MOD_OVERCHARGED" }, new string[3] { "SHOULDER_ROTARY", "SHOULDER_MISSILES", "SHOULDER_TESLA" }, new string[3] { "OVERDRIVE_WEAPONS_FREE", "OVERDRIVE_SIEGE_PROTOCOL", "OVERDRIVE_ASSIMILATION" } }; public static readonly string[] TierIds = new string[5] { "SCAVENGER", "RETROFIT", "HEAVY_RETROFIT", "WARFRAME", "APEX_RECLAIMER" }; public static bool IsSalvageableVictim(bool hasMaster, bool isProjectile, string bodyName) { if (!hasMaster || isProjectile) { return false; } if (bodyName != null) { return bodyName.IndexOf("BodyProjectile", StringComparison.Ordinal) < 0; } return true; } public static SalvageCategory Classify(bool isBoss, bool isChampion, bool isElite) { if (isBoss) { return SalvageCategory.Boss; } if (isChampion) { return SalvageCategory.Large; } if (isElite) { return SalvageCategory.Elite; } return SalvageCategory.Normal; } public static int Reward(SalvageCategory category, EvolutionSettings s) { return category switch { SalvageCategory.Boss => s.rewardBoss, SalvageCategory.Large => s.rewardLarge, SalvageCategory.Elite => s.rewardElite, _ => s.rewardNormal, }; } public static int TierForTotal(int total, int[] thresholds) { int result = 0; for (int i = 0; i < thresholds.Length && i < 4 && total >= thresholds[i]; i++) { result = i + 1; } return result; } public static int ThresholdForTier(int tier, int[] thresholds) { if (tier <= 0) { return 0; } return thresholds[Math.Min(tier, 4) - 1]; } public static int PointsAboveApex(int oldTotal, int newTotal, int[] thresholds) { int num = thresholds[3]; if (newTotal <= num) { return 0; } return newTotal - Math.Max(oldTotal, num); } public static bool IsValidOption(int tier, int option) { if (tier >= 1 && tier <= 4 && option >= 0) { return option < 3; } return false; } public static int SanitizeLoadoutOption(long rawVariantIndex, out bool wasValid) { wasValid = rawVariantIndex >= 0 && rawVariantIndex < 3; if (!wasValid) { return 0; } return (int)rawVariantIndex; } } public sealed class EvolutionState { private readonly int[] choices = new int[5] { -1, -1, -1, -1, -1 }; private readonly int[] plan = new int[5] { -1, -1, -1, -1, -1 }; public int SalvageTotal { get; private set; } public int ReachedTier { get; private set; } public bool PlanLocked { get; private set; } public int PendingCount { get { int num = 0; for (int i = 1; i <= ReachedTier; i++) { if (choices[i] < 0) { num++; } } return num; } } public int OldestPendingTier { get { for (int i = 1; i <= ReachedTier; i++) { if (choices[i] < 0) { return i; } } return 0; } } public int GetChoice(int tier) { if (tier < 1 || tier > 4) { return -1; } return choices[tier]; } public int GetPlanned(int tier) { if (tier < 1 || tier > 4) { return -1; } return plan[tier]; } public bool LockPlan(int[] planByTier, out int corrected) { corrected = 0; if (PlanLocked) { return false; } for (int i = 1; i <= 4; i++) { long rawVariantIndex = ((planByTier != null && i < planByTier.Length) ? planByTier[i] : (-1)); plan[i] = EvolutionRules.SanitizeLoadoutOption(rawVariantIndex, out var wasValid); if (!wasValid) { corrected++; } } PlanLocked = true; return true; } public List<int> InstallPlanned() { List<int> list = new List<int>(); if (!PlanLocked) { return list; } int oldestPendingTier; while ((oldestPendingTier = OldestPendingTier) != 0 && TryChoose(oldestPendingTier, plan[oldestPendingTier]) == ChoiceResult.Accepted) { list.Add(oldestPendingTier); } return list; } public List<int> AddSalvage(int amount, int[] thresholds) { List<int> list = new List<int>(); if (amount <= 0) { return list; } long num = (long)SalvageTotal + (long)amount; SalvageTotal = (int)((num > int.MaxValue) ? int.MaxValue : num); int num2 = EvolutionRules.TierForTotal(SalvageTotal, thresholds); for (int i = ReachedTier + 1; i <= num2; i++) { list.Add(i); } if (num2 > ReachedTier) { ReachedTier = num2; } return list; } public ChoiceResult CanChoose(int tier, int option) { if (tier < 1 || tier > 4) { return ChoiceResult.InvalidTier; } if (tier > ReachedTier) { return ChoiceResult.TierNotReached; } if (choices[tier] >= 0) { return ChoiceResult.AlreadyChosen; } if (tier != OldestPendingTier) { return ChoiceResult.NotOldestPending; } if (option < 0 || option >= 3) { return ChoiceResult.InvalidOption; } return ChoiceResult.Accepted; } public ChoiceResult TryChoose(int tier, int option) { ChoiceResult num = CanChoose(tier, option); if (num == ChoiceResult.Accepted) { choices[tier] = option; } return num; } public bool DevReplaceChoice(int tier, int option) { if (tier < 1 || tier > ReachedTier || option < -1 || option >= 3) { return false; } choices[tier] = option; return true; } public void Reset() { SalvageTotal = 0; ReachedTier = 0; for (int i = 0; i < choices.Length; i++) { choices[i] = -1; } } public void ApplySnapshot(int total, int reachedTier, int[] tierChoices, int[] plannedChoices = null) { SalvageTotal = Math.Max(0, total); ReachedTier = Math.Max(0, Math.Min(4, reachedTier)); bool flag = false; for (int i = 1; i <= 4; i++) { int num = ((tierChoices != null && i < tierChoices.Length) ? tierChoices[i] : (-1)); choices[i] = ((num >= 0 && num < 3) ? num : (-1)); int num2 = ((plannedChoices != null && i < plannedChoices.Length) ? plannedChoices[i] : (-1)); plan[i] = ((num2 >= 0 && num2 < 3) ? num2 : (-1)); flag |= plan[i] >= 0; } PlanLocked = flag; } } public sealed class OverdriveRefundLedger { public float GrantedThisCycle { get; private set; } public void BeginCycle() { GrantedThisCycle = 0f; } public float Grant(int points, float baseCooldownSeconds, EvolutionSettings s) { if (points <= 0 || baseCooldownSeconds <= 0f) { return 0f; } float num = baseCooldownSeconds * s.refundCapFractionPerCycle; float val = Math.Max(0f, num - GrantedThisCycle); float num2 = Math.Min((float)points * s.refundSecondsPerPoint, val); GrantedThisCycle += num2; return num2; } } public static class SalvageAwarder { private static bool subscribed; private static readonly HashSet<int> awardedVictimsThisStage = new HashSet<int>(); public static void Init() { if (!subscribed) { subscribed = true; GlobalEventManager.onCharacterDeathGlobal += OnCharacterDeathGlobal; Stage.onServerStageBegin += delegate { awardedVictimsThisStage.Clear(); }; Run.onRunDestroyGlobal += delegate { awardedVictimsThisStage.Clear(); }; } } private static bool IsHostileToPlayers(TeamIndex team) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if ((int)team != 2 && (int)team != 4) { return (int)team == 3; } return true; } private static void OnCharacterDeathGlobal(DamageReport report) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) if (!NetworkServer.active || report == null) { return; } CharacterBody victimBody = report.victimBody; if (!Object.op_Implicit((Object)(object)victimBody) || victimBody.isPlayerControlled || !IsHostileToPlayers(report.victimTeamIndex)) { return; } if (!EvolutionRules.IsSalvageableVictim(Object.op_Implicit((Object)(object)report.victimMaster), Object.op_Implicit((Object)(object)((Component)victimBody).GetComponent<ProjectileController>()), ((Object)victimBody).name)) { if (SalvagerPlugin.VerboseLogging.Value) { Log.Debug("[Salvager] no salvage for " + ((Object)victimBody).name + " (projectile or masterless body)"); } } else { if (!awardedVictimsThisStage.Add(((Object)victimBody).GetInstanceID())) { return; } SalvageCategory salvageCategory = EvolutionRules.Classify(report.victimIsBoss, report.victimIsChampion || victimBody.isChampion, report.victimIsElite); ReclamationMarks.ServerOnVictimDeath(victimBody, salvageCategory); if (SalvagerEvolutionNetworker.serverInstances.Count == 0) { return; } EvolutionSettings evolution = SalvagerConfig.Evolution; int num = EvolutionRules.Reward(salvageCategory, evolution); if (num <= 0) { return; } Vector3 corePosition = victimBody.corePosition; float num2 = evolution.radiusMeters * evolution.radiusMeters; foreach (SalvagerEvolutionNetworker item in new List<SalvagerEvolutionNetworker>(SalvagerEvolutionNetworker.serverInstances)) { if (!Object.op_Implicit((Object)(object)item) || !Object.op_Implicit((Object)(object)item.body)) { continue; } HealthComponent healthComponent = item.body.healthComponent; if (Object.op_Implicit((Object)(object)healthComponent) && healthComponent.alive) { Vector3 val = item.body.corePosition - corePosition; if (!(((Vector3)(ref val)).sqrMagnitude > num2)) { item.ServerAward(num, $"{salvageCategory} {((Object)victimBody).name}"); } } } } } } public class SalvagerMasterProgress : MonoBehaviour { public readonly EvolutionState state = new EvolutionState(); public CharacterMaster master { get; private set; } public bool AutoInstall => !SalvagerConfig.ManualEvolutionPicker.Value; public event Action onChanged; public event Action<List<int>> onTiersReached; public static string Who(CharacterMaster master) { if (!Object.op_Implicit((Object)(object)master)) { return "'?'"; } NetworkUser val = (Object.op_Implicit((Object)(object)master.playerCharacterMasterController) ? master.playerCharacterMasterController.networkUser : null); if (!Object.op_Implicit((Object)(object)val)) { return "'" + ((Object)master).name + "'"; } return "'" + SalvagerReport.PlayerName(val) + "' (" + ((Object)master).name + ")"; } public static SalvagerMasterProgress GetOrCreate(CharacterMaster master) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (!NetworkServer.active || !Object.op_Implicit((Object)(object)master)) { return null; } SalvagerMasterProgress salvagerMasterProgress = ((Component)master).GetComponent<SalvagerMasterProgress>(); if (!Object.op_Implicit((Object)(object)salvagerMasterProgress)) { salvagerMasterProgress = ((Component)master).gameObject.AddComponent<SalvagerMasterProgress>(); salvagerMasterProgress.master = master; Log.Info($"[Salvager] progression record created for {Who(master)} (netId {((NetworkBehaviour)master).netId})"); } return salvagerMasterProgress; } public bool LockLoadout(int[] planByTier, string source) { if (!NetworkServer.active) { return false; } if (!state.LockPlan(planByTier, out var corrected)) { return false; } Log.Info("[Salvager] evolution loadout locked for " + Who(master) + " (" + source + "): " + SalvagerEvolutionLoadout.Describe(state) + (AutoInstall ? "" : " [Debug.ManualEvolutionPicker = true: plan ignored, picker active]")); if (corrected > 0) { Log.Warning($"[Salvager] {corrected} invalid loadout value(s) for {Who(master)} replaced by the slot default"); } InstallPlanned("loadout lock"); this.onChanged?.Invoke(); return true; } public void Award(int points, string reason) { if (NetworkServer.active && points > 0) { List<int> list = state.AddSalvage(points, SalvagerConfig.Evolution.tierThresholds); Log.Info($"[Salvager] +{points} scrap for {Who(master)} ({reason}) -> {state.SalvageTotal}, tier {state.ReachedTier}"); if (list.Count > 0) { Log.Info(string.Format("[Salvager] {0} reached tier(s) {1} at {2} salvage", Who(master), string.Join(",", list), state.SalvageTotal)); InstallPlanned("milestone"); this.onTiersReached?.Invoke(list); } this.onChanged?.Invoke(); } } private void InstallPlanned(string source) { if (!AutoInstall) { return; } if (!state.PlanLocked) { if (state.PendingCount > 0) { Log.Warning("[Salvager] " + Who(master) + ": tier reached before the loadout was locked; will install on lock"); } return; } foreach (int item in state.InstallPlanned()) { int choice = state.GetChoice(item); Log.Info($"[Salvager] auto-installed tier {item} -> {EvolutionRules.OptionIds[item][choice]} for {Who(master)} ({source}, {state.SalvageTotal} salvage)"); } } public ChoiceResult TryChoose(int tier, int option, string source) { if (!NetworkServer.active) { return ChoiceResult.InvalidTier; } ChoiceResult choiceResult = state.TryChoose(tier, option); string text = (EvolutionRules.IsValidOption(tier, option) ? EvolutionRules.OptionIds[tier][option] : "?"); Log.Info($"[Salvager] choice request from {source}: tier {tier} option {option} ({text}) -> {choiceResult}"); if (choiceResult == ChoiceResult.Accepted) { this.onChanged?.Invoke(); } return choiceResult; } public bool DevReplaceChoice(int tier, int option, string source) { if (!NetworkServer.active || !state.DevReplaceChoice(tier, option)) { return false; } Log.Info($"[Salvager] DEV: tier {tier} replaced with option {option} for {Who(master)} ({source})"); this.onChanged?.Invoke(); return true; } public void ResetProgress(string source) { if (NetworkServer.active) { state.Reset(); Log.Info("[Salvager] progression reset for " + Who(master) + " (" + source + "); loadout plan kept: " + SalvagerEvolutionLoadout.Describe(state)); this.onChanged?.Invoke(); } } } public static class ShoulderMountData { public const int YawStepDegrees = 15; public static readonly ShoulderMount[] All = new ShoulderMount[8] { new ShoulderMount("MissileL", 1, "MissileYawL", "MissilePitchL", "MissileMuzzleL", new float[3] { -0.35273f, 1.83853f, -0.06033f }, new float[3] { -0.34877f, 1.96345f, -0.06238f }, new float[3] { -0.34869f, 1.96608f, 0.0976f }, new float[3] { 0.03162f, 0.99937f, -0.01641f }, new float[3] { 0.9995f, -0.03162f, 0f }, new float[3] { 0.00052f, 0.0164f, 0.99987f }, new float[24] { -25f, -25f, -10f, 5f, 5f, 5f, 5f, -5f, 0f, 25f, -20f, -25f, -25f, -25f, -25f, -25f, -25f, -25f, -25f, -25f, -25f, -25f, -25f, -25f }, new float[24] { 35f, 35f, 35f, 35f, 35f, 35f, 35f, 35f, 15f, 35f, 35f, 35f, 35f, 35f, 35f, 35f, 35f, 35f, 35f, 35f, 35f, 35f, 35f, 35f }, null), new ShoulderMount("MissileR", 1, "MissileYawR", "MissilePitchR", "MissileMuzzleR", new float[3] { 0.38359f, 1.73878f, 0.04586f }, new float[3] { 0.38182f, 1.86369f, 0.05038f }, new float[3] { 0.3819f, 1.85789f, 0.21028f }, new float[3] { -0.01419f, 0.99924f, 0.03621f }, new float[3] { 0.9999f, 0.0142f, 0f }, new float[3] { 0.00051f, -0.03621f, 0.99934f }, new float[24] { -5f, -15f, -25f, -25f, -25f, -25f, -25f, -20f, -15f, -15f, -10f, 0f, 15f, 20f, 20f, 30f, 20f, 20f, 25f, 20f, 20f, 20f, 999f, 999f }, new float[24] { 0f, 0f, -5f, -5f, 0f, 5f, 0f, -5f, -5f, -5f, 5f, 5f, 15f, 30f, 35f, 35f, 35f, 35f, 35f, 35f, 35f, 35f, -999f, -999f }, null), new ShoulderMount("RotaryL", 0, "RotaryYawL", "RotaryPitchL", "RotaryMuzzleL", new float[3] { -0.35304f, 1.82854f, -0.06017f }, new float[3] { -0.34749f, 2.00418f, -0.06305f }, new float[3] { -0.34727f, 2.01097f, 0.35089f }, new float[3] { 0.03162f, 0.99937f, -0.01641f }, new float[3] { 0.9995f, -0.03162f, 0f }, new float[3] { 0.00052f, 0.0164f, 0.99987f }, new float[24] { -40f, -10f, -5f, 5f, 10f, 5f, 5f, -5f, 30f, 30f, 30f, -25f, -30f, -45f, -45f, -45f, -45f, -35f, -30f, -40f, -45f, -45f, -45f, -40f }, new float[24] { 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f }, null), new ShoulderMount("RotaryR", 0, "RotaryYawR", "RotaryPitchR", "RotaryMuzzleR", new float[3] { 0.43372f, 1.72987f, 0.01551f }, new float[3] { 0.43122f, 1.90549f, 0.02188f }, new float[3] { 0.43143f, 1.8905f, 0.43561f }, new float[3] { -0.01419f, 0.99924f, 0.03621f }, new float[3] { 0.9999f, 0.0142f, 0f }, new float[3] { 0.00051f, -0.03621f, 0.99934f }, new float[24] { -45f, -45f, -40f, -40f, -30f, -20f, -15f, -20f, -20f, -15f, -10f, -5f, 5f, 5f, -10f, 25f, 20f, 15f, 15f, 25f, 20f, 15f, 0f, -30f }, new float[24] { 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f, 85f }, null), new ShoulderMount("TeslaL", 2, "TeslaYawL", "TeslaPitchL", "TeslaMuzzleL", new float[3] { -0.35304f, 1.82854f, -0.06017f }, new float[3] { -0.35083f, 1.89849f, -0.06131f }, new float[3] { -0.3453f, 2.07338f, -0.06419f }, new float[3] { 0.03162f, 0.99937f, -0.01641f }, new float[3] { 0.9995f, -0.03162f, 0f }, new float[3] { 0.00052f, 0.0164f, 0.99987f }, new float[24] { -60f, -50f, -30f, -5f, -10f, -20f, -15f, -10f, 15f, 10f, -45f, -40f, -60f, -60f, -60f, -60f, -60f, -60f, -50f, -60f, -60f, -60f, -60f, -60f }, new float[24] { 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f }, new float[24] { 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f }), new ShoulderMount("TeslaR", 2, "TeslaYawR", "TeslaPitchR", "TeslaMuzzleR", new float[3] { 0.43372f, 1.72987f, 0.01551f }, new float[3] { 0.43272f, 1.79982f, 0.01805f }, new float[3] { 0.43024f, 1.97469f, 0.02439f }, new float[3] { -0.01419f, 0.99924f, 0.03621f }, new float[3] { 0.9999f, 0.0142f, 0f }, new float[3] { 0.00051f, -0.03621f, 0.99934f }, new float[24] { -60f, -60f, -60f, -60f, -55f, -55f, -55f, -60f, -55f, -60f, -50f, -40f, -5f, -5f, -35f, 20f, 0f, -10f, -10f, 10f, 10f, -15f, -45f, -60f }, new float[24] { 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f, 90f }, new float[24] { 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f, 30f }), new ShoulderMount("WFRotaryL", 3, "RotaryYawL", "RotaryPitchL", "WFRotaryMuzzleL", new float[3] { -0.35304f, 1.82854f, -0.06017f }, new float[3] { -0.34749f, 2.00418f, -0.06305f }, new float[3] { -0.34261f, 2.15841f, 0.19845f }, new float[3] { 0.03162f, 0.99937f, -0.01641f }, new float[3] { 0.9995f, -0.03162f, 0f }, new float[3] { 0.00052f, 0.0164f, 0.99987f }, new float[24] { -40f, -10f, -5f, 5f, 10f, 5f, 5f, 0f, 30f, 30f, 30f, -25f, -30f, -45f, -45f, -45f, -45f, -35f, -30f, -40f, -45f, -45f, -45f, -40f }, new float[24] { 45f, 45f, 45f, 45f, 45f, 45f, 45f, 45f, 45f, 45f, 45f, 45f, 45f, 45f, 35f, 30f, 30f, 40f, 40f, 30f, 5f, -5f, -5f, 10f }, null), new ShoulderMount("WFRotaryR", 3, "RotaryYawR", "RotaryPitchR", "WFRotaryMuzzleR", new float[3] { 0.43372f, 1.72987f, 0.01551f }, new float[3] { 0.43122f, 1.90549f, 0.02188f }, new float[3] { 0.42923f, 2.04582f, 0.29114f }, new float[3] { -0.01419f, 0.99924f, 0.03621f }, new float[3] { 0.9999f, 0.0142f, 0f }, new float[3] { 0.00051f, -0.03621f, 0.99934f }, new float[24] { -45f, -45f, -40f, -40f, -30f, -20f, -15f, -20f, -20f, -15f, -10f, -5f, 5f, 5f, -10f, 25f, 5f, 15f, 15f, 25f, 20f, 15f, 0f, -30f }, new float[24] { 30f, 30f, 15f, 5f, 0f, 0f, 0f, 5f, 5f, 10f, 20f, 35f, 45f, 45f, 45f, 45f, 45f, 30f, 25f, 25f, 25f, 25f, 25f, 25f }, null) }; public static ShoulderMount Find(string id) { ShoulderMount[] all = All; foreach (ShoulderMount shoulderMount in all) { if (shoulderMount.Id == id) { return shoulderMount; } } return null; } public static ShoulderMount For(int shoulder, int side, bool weaponsFreeHead = false) { string text = ((side == 0) ? "L" : "R"); return shoulder switch { 0 => Find((weaponsFreeHead ? "WFRotary" : "Rotary") + text), 1 => Find("Missile" + text), 2 => Find("Tesla" + text), _ => null, }; } } public struct V3 { public float x; public float y; public float z; public float Length => (float)Math.Sqrt(x * x + y * y + z * z); public V3 Normalized { get { float length = Length; if (!(length > 1E-09f)) { return this; } return this * (1f / length); } } public V3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public V3(float[] a) { x = a[0]; y = a[1]; z = a[2]; } public static V3 operator +(V3 a, V3 b) { return new V3(a.x + b.x, a.y + b.y, a.z + b.z); } public static V3 operator -(V3 a, V3 b) { return new V3(a.x - b.x, a.y - b.y, a.z - b.z); } public static V3 operator *(V3 a, float k) { return new V3(a.x * k, a.y * k, a.z * k); } public static float Dot(V3 a, V3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } public static V3 Cross(V3 a, V3 b) { return new V3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); } public override string ToString() { return $"({x:0.###}, {y:0.###}, {z:0.###})"; } } public enum MountKind { Rotary, Missile, Tesla, RotaryWeaponsFree } public sealed class ShoulderMount { public readonly string Id; public readonly string YawPivot; public readonly string PitchPivot; public readonly string MuzzlePivot; public readonly MountKind Kind; public readonly V3 YawAt; public readonly V3 PitchAt; public readonly V3 MuzzleAt; public readonly V3 Up; public readonly V3 Lateral; public readonly V3 Forward; public readonly float[] MinPitch; public readonly float[] MaxPitch; public readonly float[] MaxLean; public char Side => Id[Id.Length - 1]; public ShoulderMount(string id, int kind, string yawPivot, string pitchPivot, string muzzlePivot, float[] yawAt, float[] pitchAt, float[] muzzleAt, float[] up, float[] lateral, float[] forward, float[] minPitch, float[] maxPitch, float[] maxLean) { Id = id; Kind = (MountKind)kind; YawPivot = yawPivot; PitchPivot = pitchPivot; MuzzlePivot = muzzlePivot; YawAt = new V3(yawAt); PitchAt = new V3(pitchAt); MuzzleAt = new V3(muzzleAt); Up = new V3(up).Normalized; Lateral = new V3(lateral).Normalized; Forward = new V3(forward).Normalized; MinPitch = minPitch; MaxPitch = maxPitch; MaxLean = maxLean; } } public static class ShoulderRules { public const float NoReach = 999f; public static V3 Rotate(V3 v, V3 axis, float degrees) { double num = (double)degrees * Math.PI / 180.0; float num2 = (float)Math.Cos(num); float num3 = (float)Math.Sin(num); V3 normalized = axis.Normalized; return v * num2 + V3.Cross(normalized, v) * num3 + normalized * (V3.Dot(normalized, v) * (1f - num2)); } public static float WrapDegrees(float a) { a %= 360f; if (a > 180f) { a -= 360f; } if (a <= -180f) { a += 360f; } return a; } public static void AimAngles(ShoulderMount m, V3 target, out float yaw, out float pitch) { V3 v = target - m.PitchAt; float num = V3.Dot(v, m.Up); V3 a = v - m.Up * num; V3 b = V3.Cross(m.Up, m.Forward); yaw = (float)(Math.Atan2(V3.Dot(a, b), V3.Dot(a, m.Forward)) * 180.0 / Math.PI); pitch = (float)(Math.Atan2(num, a.Length) * 180.0 / Math.PI); } public static void AimAnglesDir(ShoulderMount m, V3 v, out float yaw, out float pitch) { float num = V3.Dot(v, m.Up); V3 a = v - m.Up * num; V3 b = V3.Cross(m.Up, m.Forward); yaw = (float)(Math.Atan2(V3.Dot(a, b), V3.Dot(a, m.Forward)) * 180.0 / Math.PI); pitch = (float)(Math.Atan2(num, a.Length) * 180.0 / Math.PI); } public static V3 Direction(ShoulderMount m, float yaw, float pitch) { return Rotate((m.Kind == MountKind.Tesla) ? (m.Forward * (float)Math.Cos((double)pitch * Math.PI / 180.0) + m.Up * (float)Math.Sin((double)pitch * Math.PI / 180.0)) : Rotate(m.Forward, m.Lateral, 0f - pitch), m.Up, yaw); } public static V3 Muzzle(ShoulderMount m, float yaw, float pitchOrLean) { return Rotate(Rotate(m.MuzzleAt - m.PitchAt, m.Lateral, (m.Kind == MountKind.Tesla) ? pitchOrLean : (0f - pitchOrLean)) + m.PitchAt - m.YawAt, m.Up, yaw) + m.YawAt; } public static void Reach(ShoulderMount m, float yaw, out float min, out float max) { int num = m.MinPitch.Length; float num2 = 360f / (float)num; float num3 = (yaw % 360f + 360f) % 360f; int num4 = (int)Math.Floor(num3 / num2) % num; int num5 = (num4 + 1) % num; min = Math.Max(m.MinPitch[num4], m.MinPitch[num5]); max = Math.Min(m.MaxPitch[num4], m.MaxPitch[num5]); if (Math.Abs(num3 - (float)num4 * num2) < 0.001f) { min = m.MinPitch[num4]; max = m.MaxPitch[num4]; } } public static bool CanReach(ShoulderMount m, float yaw, float pitch) { Reach(m, yaw, out var min, out var max); if (min < 999f && pitch >= min) { return pitch <= max; } return false; } public static float MaxLean(ShoulderMount m, float yaw) { if (m.MaxLean == null) { return 0f; } int num = m.MaxLean.Length; int num2 = (int)Math.Floor((yaw % 360f + 360f) % 360f / (360f / (float)num)) % num; int num3 = (num2 + 1) % num; return Math.Min(m.MaxLean[num2], m.MaxLean[num3]); } public static float TeslaLean(float elevation, float maxLean) { return Math.Max(0f, Math.Min(maxLean, (90f - elevation) * 0.4f)); } public static float StepAngle(float current, float target, float maxStep, bool wrap) { float value = (wrap ? WrapDegrees(target - current) : (target - current)); if (Math.Abs(value) <= maxStep) { if (!wrap) { return target; } return WrapDegrees(target); } float num = current + (float)Math.Sign(value) * maxStep; if (!wrap) { return num; } return WrapDegrees(num); } public static float SafePitch(ShoulderMount m, float currentYaw, float wantedPitch) { Reach(m, currentYaw, out var min, out var max); if (min >= 999f) { return Math.Max(wantedPitch, 45f); } return Math.Min(Math.Max(wantedPitch, min), Math.Max(min, max)); } public static bool InMissileCone(V3 dir, V3 facing, V3 up, float halfAngle, float overheadElevation = 60f) { V3 normalized = dir.Normalized; float num = V3.Dot(normalized, up); float num2 = (float)(Math.Asin(Math.Max(-1f, Math.Min(1f, num))) * 180.0 / Math.PI); if (num2 >= overheadElevation) { return true; } V3 v = normalized - up * num; V3 v2 = facing - up * V3.Dot(facing, up); if (v.Length < 0.0001f || v2.Length < 0.0001f) { return num2 > 0f; } return V3.Dot(v.Normalized, v2.Normalized) >= (float)Math.Cos((double)halfAngle * Math.PI / 180.0); } } public struct TurretBurst { public int fired; public float shotTimer; public float cooling; public float idle; public bool Cooling => cooling > 0f; public bool Firing => fired > 0; public int Step(float dt, bool canFire, float interval, int rounds, float recovery, float abandonSeconds = 0.4f) { if (cooling > 0f) { cooling -= dt; return 0; } if (!canFire) { shotTimer = Math.Max(0f, shotTimer - dt); if (fired > 0) { idle += dt; if (idle >= abandonSeconds) { cooling = recovery * (float)fired / (float)Math.Max(1, rounds); fired = 0; idle = 0f; } } return 0; } idle = 0f; shotTimer -= dt; int num = 0; while (shotTimer <= 0f && fired < rounds) { num++; fired++; shotTimer += Math.Max(0.01f, interval); } if (fired >= rounds) { cooling = recovery; fired = 0; shotTimer = 0f; } return num; } public static float SustainedRate(float interval, int rounds, float recovery) { float num = (float)(rounds - 1) * interval + recovery; return (float)rounds / num; } } public enum PrimaryCore { None = -1, Shredder, Siege, Arc } public sealed class ShredderSettings { public float damageCoefficient = 0.65f; public float procCoefficient = 0.45f; public float rangeMeters = 45f; public float minShotsPerSecond = 7f; public float maxShotsPerSecond = 11f; public float spinUpSeconds = 1.2f; public float spinDownSeconds = 1.6f; public float spinGraceSeconds = 0.35f; public float minSpreadDegrees = 0.4f; public float maxSpreadDegrees = 2.4f; public float fullSpinMovePenalty = 0.1f; public float fullSpinThreshold = 0.98f; public float overdriveMaxRateBonus = 0.2f; public int maxBulletsPerTick = 6; } public sealed class SiegeSettings { public float directDamageCoefficient = 5.5f; public float splashDamageCoefficient = 3.3f; public float directProcCoefficient = 0.6f; public float splashProcCoefficient = 0.2f; public float fireIntervalSeconds = 1.1f; public float splashRadiusMeters = 7f; public float shellSpeed = 110f; public float shellLifetimeSeconds = 4f; public int breachCount = 2; public float breachDamageCoefficient = 1f; public float breachProcCoefficient; public float breachRadiusMeters = 4f; public float breachSpreadDegrees = 7f; } public sealed class ArcSettings { public float damageCoefficient = 1.65f; public float procCoefficient = 0.55f; public float shotsPerSecond = 1.6f; public float rangeMeters = 35f; public int chainJumps = 3; public float chainRangeMeters = 16f; public float chainFalloff = 0.55f; public float chainProcCoefficient = 0.07f; public int overdriveChainJumps = 5; public float overdriveDamageBonus = 0.2f; } public static class WeaponRules { public static PrimaryCore CoreForChoice(int tierOneChoice) { if (tierOneChoice < 0 || tierOneChoice > 2) { return PrimaryCore.None; } return (PrimaryCore)tierOneChoice; } public static float ShredderSpinStep(float spin, bool firing, float secondsSinceLastShot, float dt, bool overdrive, ShredderSettings s) { if (overdrive && firing) { return 1f; } if (firing) { spin += dt / Math.Max(0.01f, s.spinUpSeconds); } else if (secondsSinceLastShot > s.spinGraceSeconds) { spin -= dt / Math.Max(0.01f, s.spinDownSeconds); } if (!(spin < 0f)) { if (!(spin > 1f)) { return spin; } return 1f; } return 0f; } public static float ShredderShotsPerSecond(float spin, float attackSpeed, bool overdrive, ShredderSettings s) { float num = s.maxShotsPerSecond * (overdrive ? (1f + s.overdriveMaxRateBonus) : 1f); float num2 = ((spin < 0f) ? 0f : ((spin > 1f) ? 1f : spin)); return (s.minShotsPerSecond + (num - s.minShotsPerSecond) * num2) * Math.Max(0.01f, attackSpeed); } public static float ShredderSpread(float spin, ShredderSettings s) { float num = ((spin < 0f) ? 0f : ((spin > 1f) ? 1f : spin)); return s.minSpreadDegrees + (s.maxSpreadDegrees - s.minSpreadDegrees) * num * num; } public static float ShredderMovePenalty(float spin, ShredderSettings s) { if (!(spin >= s.fullSpinThreshold)) { return 0f; } return s.fullSpinMovePenalty; } public static int ConsumeShots(ref float accumulator, float shotsPerSecond, float dt, int maxPerTick) { accumulator += Math.Max(0f, shotsPerSecond) * dt; int num = (int)Math.Floor(accumulator); if (num <= 0) { return 0; } if (num > maxPerTick) { num = maxPerTick; accumulator = Math.Min(accumulator - (float)num, maxPerTick); } else { accumulator -= num; } return num; } public static float SiegeInterval(float attackSpeed, SiegeSettings s) { return Math.Max(0.1f, s.fireIntervalSeconds / Math.Max(0.01f, attackSpeed)); } public static float SiegeDamageCoefficient(bool isDirectVictim, float distance, SiegeSettings s) { if (isDirectVictim) { return s.directDamageCoefficient; } if (!(distance <= s.splashRadiusMeters)) { return 0f; } return s.splashDamageCoefficient; } public static float ArcDirectCoefficient(bool overdrive, ArcSettings s) { return s.damageCoefficient * (overdrive ? (1f + s.overdriveDamageBonus) : 1f); } public static int ArcChainJumps(bool overdrive, ArcSettings s) { return Math.Max(0, overdrive ? s.overdriv