using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BTD_Mod_Helper; using BTD_Mod_Helper.Api; using BTD_Mod_Helper.Api.Components; using BTD_Mod_Helper.Api.ModOptions; using BTD_Mod_Helper.Extensions; using HarmonyLib; using Il2Cpp; using Il2CppAssets.Scripts.Data.ParagonData; using Il2CppAssets.Scripts.Models; using Il2CppAssets.Scripts.Models.Effects; using Il2CppAssets.Scripts.Models.Entities; using Il2CppAssets.Scripts.Models.Towers; using Il2CppAssets.Scripts.Models.Towers.Behaviors; using Il2CppAssets.Scripts.Models.Towers.Upgrades; using Il2CppAssets.Scripts.Simulation.Objects; using Il2CppAssets.Scripts.Simulation.SMath; using Il2CppAssets.Scripts.Simulation.Towers; using Il2CppAssets.Scripts.Simulation.Towers.Behaviors; using Il2CppAssets.Scripts.Unity; using Il2CppAssets.Scripts.Unity.Bridge; using Il2CppAssets.Scripts.Unity.UI_New.InGame; using Il2CppAssets.Scripts.Unity.UI_New.InGame.TowerSelectionMenu; using Il2CppAssets.Scripts.Unity.UI_New.Popups; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppTMPro; using MelonLoader; using Microsoft.CodeAnalysis; using Paragonomics; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(ParagonomicsMod), "Paragonomics", "1.0.6", "doombubbles", null)] [assembly: MelonGame("Ninja Kiwi", "BloonsTD6")] [assembly: MelonGame("Ninja Kiwi", "BloonsTD6-Epic")] [assembly: MelonOptionalDependencies(new string[] { "BuffsInShop" })] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Paragonomics")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+cd122a2d9a79721a1b928fd6b43b65f55d643544")] [assembly: AssemblyProduct("Paragonomics")] [assembly: AssemblyTitle("Paragonomics")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Paragonomics { public static class Calculations { public static readonly Queue DegreeCalculationHints = new Queue(); public static double DegreeToPower(long d) { if (1 == 0) { } double result; if (d > 0) { if (d <= 100) { if (d != 1) { if (d != 100) { goto IL_0066; } result = 200000.0; } else { result = 0.0; } } else { if (!ModSettingBool.op_Implicit(ParagonomicsMod.LinearScalingPastLimit)) { goto IL_0066; } result = d * 2000; } } else { result = 0.0 - DegreeToPower(-d + 1); } goto IL_00c1; IL_0066: result = Math.Round((50.0 * Math.Pow(d, 3.0) + 5025.0 * Math.Pow(d, 2.0) + (double)(168324 * d) + 843000.0) / 600.0); goto IL_00c1; IL_00c1: if (1 == 0) { } return result; } public static long PowerToDegree(double power) { if (power != 0.0) { if (!(power < 0.0)) { if (power != 200000.0) { if (power > 200000.0 && ModSettingBool.op_Implicit(ParagonomicsMod.LinearScalingPastLimit)) { return (long)Math.Round(power / 2000.0); } if (!DegreeCalculationHints.TryDequeue(out var result)) { result = 100; } double num = result; for (int i = 1; i < 100; i++) { double num2 = DegreeToPower((long)num); double num3 = DegreeToPower((long)num + 1); if (num2 <= power && num3 > power) { return (long)num; } double num4 = num2 - power; double num5 = (150.0 * Math.Pow(num, 2.0) + 10050.0 * num + 168324.0) / 600.0; double num6 = num4 / num5; num -= num6; num = Math.Max(1.0, num); } return (int)num; } return 100L; } return Math.Min(-PowerToDegree(0.0 - power) + 1, -1L); } return 1L; } public static int GetDisplayDegreeIndex(ParagonTower paragon, int degree) { Il2CppReferenceArray displayDegreePaths = paragon.paragonTowerModel.displayDegreePaths; if (!((IEnumerable)displayDegreePaths).Any()) { return -1; } Il2CppStructArray powerDegreeRequirements = ((RootObject)paragon).Sim.model.paragonDegreeDataModel.powerDegreeRequirements; return Math.Min(degree / (((Il2CppArrayBase)(object)powerDegreeRequirements).Length / ((Il2CppArrayBase)(object)displayDegreePaths).Length), ((Il2CppArrayBase)(object)powerDegreeRequirements).Length - 1); } public static float GetCostFactor(ParagonTower paragon) { string baseId = ((EntityModel)((TowerBehavior)paragon).tower.towerModel).baseId; GameModel model = ((RootObject)paragon).Sim.model; float num = Math.Max(model.GetTower(baseId, 0, 0, 0).cost, 1f) * 3f; int num2 = (from tower in Il2CppGenericIEnumerable.AsIEnumerable(model.GetTowersWithBaseId(baseId, false)) where !tower.isParagon select tower).SelectMany((TowerModel tower) => (IEnumerable)tower.appliedUpgrades).Distinct().Select((Func)model.GetUpgrade) .Sum((UpgradeModel upgrade) => Math.Max(upgrade.cost, 1)); int num3 = Math.Max(ParagonomicsMod.GetParagonUpgrade(((TowerBehavior)paragon).tower).cost, 10); return (num + (float)num2) / (num + (float)num2 + (float)num3); } public static float FactorForBaseDegree(float factor, int baseDegree) { return 100f * ((1f / factor - 1f) / (float)baseDegree); } public static PowerDegreeMutator GetDegreeMutator(ParagonTower paragon, int degree, AssetPathModel displayDegree) { //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown ParagonDegreeDataModel paragonDegreeDataModel = InGame.Bridge.Simulation.model.paragonDegreeDataModel; int num = degree - 1; float num2 = Math.Sign((float)num) * Math.Round(Math.Sqrt(Math.Abs((float)num) * paragonDegreeDataModel.attackCooldownReductionX) * 10f) / 10f; float num3 = (float)num * paragonDegreeDataModel.piercePercentPerDegree; float num4 = (float)num * paragonDegreeDataModel.pierceIncreasePerDegree; float num5 = (float)num * paragonDegreeDataModel.damagePercentPerDegree; float num6 = (float)num / paragonDegreeDataModel.damageIncreaseForDegrees * paragonDegreeDataModel.damageIncreasePerDegree; if (degree >= paragonDegreeDataModel.degreeCount) { num3 += paragonDegreeDataModel.piercePercentPerDegree; num4 += paragonDegreeDataModel.piercePercentPerDegree; num6 += paragonDegreeDataModel.damageIncreasePerDegree; } float num7 = 0f; if (degree >= paragonDegreeDataModel.bonusBossDamagePerDegrees) { num7 = (float)(degree / paragonDegreeDataModel.bonusBossDamagePerDegrees) * paragonDegreeDataModel.bonusBossDamagePercent; } int num8 = GetDisplayDegreeIndex(paragon, degree) + 1; if (num < 0) { num6 = Math.Max(0f, num6); num4 = Math.Max(0f, num4); float costFactor = GetCostFactor(paragon); float num9 = FactorForBaseDegree(costFactor, 29) * (1f + num2 / 100f); num3 = 0f - (100f + 10000f / (-100f + num3 * num9)); num5 = 0f - (100f + 10000f / (-100f + num5 * num9)); } return new PowerDegreeMutator(degree, num2, num3, num4, num5, (float)(int)num6, num8, displayDegree, paragon.paragonTowerModel.changeAttackDisplay, paragon.paragonTowerModel.changeAirUnitDisplay, num7); } } public static class ModHelperData { public const string WorksOnVersion = "54.0"; public const string Version = "1.0.6"; public const string Name = "Paragonomics"; public const string Description = "Adds new ways to change Paragon degrees using cash.\n\nSubprime Paragons: Buy negative degree paragons for less cash than normal.\nParagon Investing: Spend cash to increase paragon degree after creation.\nSet Degree: Directly set paragon degree in Sandbox mode."; public const string RepoOwner = "doombubbles"; public const string PrevRepoName = "paragonomics"; public const string RepoName = "Paragonomics"; } public class ParagonomicsMod : BloonsTD6Mod { public const int MinimumCostFactor = 100; public static readonly ModSettingBool AllowDegreeSettingOutsideSandbox = new ModSettingBool(false) { description = "Allows the cheat-y Set Degree feature to be used outside Sandbox mode.", icon = "1bf486871cae1224ca90cf52c1965462" }; public static readonly ModSettingBool NoDegreeLimit = new ModSettingBool(false) { description = "Removes the standard degree 100 limit for the purposes of investing and degree setting.", icon = "1757f8ef9167a3842b6d977f379d8548" }; public static readonly ModSettingBool LinearScalingPastLimit = new ModSettingBool(false) { description = "Makes the power investment requirement scaling be linear when the degree increases beyond 100, rather than continuing to be exponentially more." }; public override object? Call(string operation, params object[] p) { if (1 == 0) { } float investment = default(float); object result = ((!(operation == "GetDegree") || !ArrayExt.CheckTypes(p, ref investment)) ? null : ((object)GetDegree(investment))); if (1 == 0) { } return result; } public static void DegreeBtnPressed(ParagonTower paragon) { Calculations.DegreeCalculationHints.Enqueue(paragon.GetCurrentDegree()); PopupScreenExt.SafelyQueue(PopupScreen.instance, (Action)delegate(PopupScreen screen) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) screen.ShowSetValuePopup("Set Paragon Degree", "Directly set the degree of this paragon at no cost.", Action.op_Implicit((Action)delegate(int degree) { SetDegree(paragon, degree); }), GetDegree(paragon.investmentInfo.totalInvestment)); }); } public static UpgradeModel GetParagonUpgrade(Tower tower) { GameModel val = (((Object)(object)InGame.instance != (Object)null) ? InGame.Bridge.Model : Game.instance.model); BloonsMod val2 = default(BloonsMod); return (((Mutable)tower).IsMutatedBy("HonoraryParagon") && ModHelper.HasMod("HonoraryParagons", ref val2)) ? val2.Call("GetParagonUpgrade", new object[2] { val, tower.towerModel }) : val.GetParagonUpgradeForTowerId(((EntityModel)tower.towerModel).baseId); } public static bool HasEnoughToShowPopup(Tower tower) { UpgradeModel paragonUpgrade = GetParagonUpgrade(tower); if (InGame.Bridge.GetCash(-1) * 100.0 >= (double)paragonUpgrade.cost) { return true; } StartPopup(); PopupScreenExt.SafelyQueue(PopupScreen.instance, (Action)delegate(PopupScreen screen) { screen.ShowOkPopup("You must have at least 1% of the Paragon's Cost in cash.", ReturnCallback.op_Implicit((Action)FinishPopup)); }); return false; } public static void InvestBtnPressed(ParagonTower paragon, Action? okCallback = null, Action? cancelCallback = null) { if (!HasEnoughToShowPopup(((TowerBehavior)paragon).tower)) { return; } StartPopup(); PopupScreenExt.SafelyQueue(PopupScreen.instance, (Action)delegate(PopupScreen screen) { screen.ShowParagonConfirmationPopup((Placement)0, "Invest in Paragon", "Add to your current Paragon Power investment.", Action.op_Implicit((Action)delegate(double cash) { FinishPopup(); InvestInParagon(paragon, cash); okCallback?.Invoke(cash); }), "Do It", Action.op_Implicit((Action)delegate { FinishPopup(); cancelCallback?.Invoke(); }), "Cancel", (TransitionAnim)1, (int)InGame.Bridge.GetCash(-1), int.MaxValue, 0); }); } public static void NegativeParagonPopup(TowerSelectionMenu tsm, int index) { if (!HasEnoughToShowPopup(tsm.selectedTower.tower)) { return; } UpgradeModel upgrade = GetParagonUpgrade(tsm.selectedTower.tower); int minCost = upgrade.cost / 100; if (tsm.Bridge.GetCash(-1) >= (double)upgrade.cost || tsm.Bridge.GetCash(-1) < (double)minCost || !TowerSelectionMenuUtils.CanUpgradeToParagon(tsm.selectedTower, true)) { return; } StartPopup(); PopupScreenExt.SafelyQueue(PopupScreen.instance, (Action)delegate(PopupScreen screen) { screen.ShowParagonConfirmationPopup((Placement)0, "Subprime Paragon", "You dont have enough cash to initiate a full Paragon sacrifice. However, you can spend the cash you do have to get a weaker, Negative-Degree Paragon and invest more later.", Action.op_Implicit((Action)delegate(double amount) { FinishPopup(); tsm.isUpgradePopupShowing = false; double num = amount + (double)minCost; Tower tower = tsm.selectedTower.tower; tsm.UpgradeTower(upgrade, index, (float)num, num - (double)upgrade.cost); tower.worth -= (float)upgrade.cost; tower.worth += (float)num; }), "Do It", Action.op_Implicit((Action)FinishPopup), "Cancel", (TransitionAnim)1, (int)InGame.Bridge.GetCash(-1) - minCost, upgrade.cost, minCost); }); } public static void StartPopup() { InGame.instance.StopClock(true); InGame.instance.hotkeys.SuppressHotkeys = true; TowerSelectionMenuExt.instance.isUpgradePopupShowing = true; } public static void FinishPopup() { InGame.instance.ResumeClock(true); InGame.instance.hotkeys.SuppressHotkeys = false; TowerSelectionMenuExt.instance.isUpgradePopupShowing = false; } public static int GetDegree(float investment) { ParagonDegreeDataModel paragonDegreeDataModel = InGame.Bridge.Model.paragonDegreeDataModel; Il2CppStructArray powerDegreeRequirements = paragonDegreeDataModel.powerDegreeRequirements; if (investment < 0f || (investment > (float)paragonDegreeDataModel.MaxInvestment && ModSettingBool.op_Implicit(NoDegreeLimit))) { return (int)Calculations.PowerToDegree(investment); } int i; for (i = 0; i < ((Il2CppArrayBase)(object)powerDegreeRequirements).Length && investment >= (float)((Il2CppArrayBase)(object)powerDegreeRequirements)[i]; i++) { } return i; } public static void SetDegree(ParagonTower paragon, int degree) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) GameModel model = ((RootObject)paragon).Sim.model; ParagonDegreeDataModel paragonDegreeDataModel = model.paragonDegreeDataModel; if (degree < 1 || degree > ((Il2CppArrayBase)(object)paragonDegreeDataModel.powerDegreeRequirements).Length) { paragon.investmentInfo = new InvestmentInfo { totalInvestment = (float)Calculations.DegreeToPower(degree) }; Calculations.DegreeCalculationHints.Enqueue(degree); } else { paragon.investmentInfo = new InvestmentInfo { totalInvestment = ((Il2CppArrayBase)(object)paragonDegreeDataModel.powerDegreeRequirements)[degree - 1] }; } OnDegreeChanged(paragon); } public static void InvestInParagon(ParagonTower paragon, double investment) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) ParagonDegreeDataModel paragonDegreeDataModel = ((RootObject)paragon).Sim.model.paragonDegreeDataModel; int cost = GetParagonUpgrade(((TowerBehavior)paragon).tower).cost; float num = (float)investment * paragonDegreeDataModel.moneySpentOverX / ((1f + paragonDegreeDataModel.paidContributionPenalty) * (float)Math.Max(cost, 1)); InvestmentInfo investmentInfo = paragon.investmentInfo; investmentInfo.totalInvestment = paragon.investmentInfo.totalInvestment + num; paragon.investmentInfo = investmentInfo; OnDegreeChanged(paragon); InGame.Bridge.SetCash(InGame.Bridge.GetCash(-1) - investment); } public static void OnDegreeChanged(ParagonTower paragon) { paragon.UpdateDegree(); paragon.PlayParagonUpgradeSound(); paragon.Finish(); Il2CppGenericIEnumerable.ForEach(((RootBehavior)paragon).entity.GetBehaviorsInDependants(), (Action)delegate(TowerCreateParagonTower tower) { tower.towerAdded = false; }); TowerSelectionMenu instance = TowerSelectionMenuExt.instance; object obj; if (instance == null) { obj = null; } else { TowerToSimulation selectedTower = instance.selectedTower; obj = ((selectedTower != null) ? selectedTower.tower : null); } if (obj == ((TowerBehavior)paragon).tower) { TowerSelectionMenuExt.instance.OnTowerSelectionPanelForceUpdate(); } } } [HarmonyPatch(typeof(TowerSelectionMenu), "UpdateParagon")] internal static class TowerSelectionMenu_UpdateParagon { [HarmonyPostfix] internal static void Postfix(TowerSelectionMenu __instance) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0144: 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_015c: 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_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) GameObject gameObject = ((Component)((TMP_Text)__instance.paragonDegree).transform.parent).gameObject; if (!Object.op_Implicit((Object)(object)gameObject.GetComponent