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 PetrichorProtocol v2.16.0
PetrichorProtocol.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HG.BlendableTypes; using Microsoft.CodeAnalysis; using On.RoR2; using On.RoR2.Projectile; using On.RoR2.UI; using On.RoR2.UI.MainMenu; using RiskOfOptions; using RiskOfOptions.OptionConfigs; using RiskOfOptions.Options; using RoR2; using RoR2.Projectile; using RoR2.UI; using RoR2.UI.MainMenu; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.Rendering.PostProcessing; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PetrichorProtocol")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.16.0.0")] [assembly: AssemblyInformationalVersion("2.16.0+615702a4273eef4ba1c49317a3516c3ccfedacf9")] [assembly: AssemblyProduct("PetrichorProtocol")] [assembly: AssemblyTitle("PetrichorProtocol")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.16.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PetrichorProtocol { internal class ChallengeContractsModule : ModuleBase { public enum Contract { None, KillTheTitan, NoHealing, SpeedClear, EliteHunt, StayInside, PacifistStart } public ConfigEntry<float> OfferChance; public ConfigEntry<float> RewardGoldMultiplier; public ConfigEntry<float> SpeedClearSeconds; public ConfigEntry<int> EliteHuntCount; public ConfigEntry<float> NoHealingCap; private Contract active; private bool resolved; private float stageStartTime; private float healedThisStage; private int elitesKilledThisStage; private bool titanKilled; private bool pacifistBroken; private CharacterMaster titanMaster; private static readonly Random rng = new Random(); public override string Name => "Challenge Contracts"; public override string StandaloneGuid => null; public override int Stars => 2; public override bool UsesStageStart => true; public override bool UsesRunStart => true; public override bool UsesDeath => true; public override bool UsesHeal => true; public override bool UsesReward => true; public override bool UsesFixedUpdate => true; public override void Bind(ConfigFile cfg) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown Enabled = cfg.Bind<bool>("Contracts", "Enabled", false, "Each stage offers one optional objective (a contract). Complete it for a bonus reward; ignoring it costs nothing. A goals-and-rewards layer - off by default."); OfferChance = cfg.Bind<float>("Contracts", "OfferChancePerStage", 100f, new ConfigDescription("Chance (percent) that a stage offers a contract at all.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); RewardGoldMultiplier = cfg.Bind<float>("Contracts", "RewardGoldMultiplier", 0.5f, new ConfigDescription("Bonus gold on completing a contract, as a fraction of your current gold (0.5 = +50%).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>())); SpeedClearSeconds = cfg.Bind<float>("Contracts", "SpeedClearSeconds", 120f, new ConfigDescription("For the Speed Clear contract: seconds allowed to activate the teleporter.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 600f), Array.Empty<object>())); EliteHuntCount = cfg.Bind<int>("Contracts", "EliteHuntCount", 3, new ConfigDescription("For the Elite Hunt contract: how many elites to kill this stage.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 15), Array.Empty<object>())); NoHealingCap = cfg.Bind<float>("Contracts", "NoHealingCap", 50f, new ConfigDescription("For the No Healing contract: total healing allowed this stage before it fails.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>())); } public override void OnRunStart() { active = Contract.None; } public override void OnStageStart(Stage stage) { active = Contract.None; resolved = false; healedThisStage = 0f; elitesKilledThisStage = 0; titanKilled = false; pacifistBroken = false; titanMaster = null; stageStartTime = Time.time; if (!NetworkServer.active) { return; } if (rng.NextDouble() * 100.0 > (double)OfferChance.Value) { DebugLog($"No contract offered this stage (OfferChance={OfferChance.Value}%)"); return; } active = (Contract)(1 + rng.Next(6)); DebugLog($"Rolled contract: {active}"); if (active == Contract.KillTheTitan && !TrySpawnTitan()) { DebugLog("KillTheTitan could not find a spawn candidate - falling back to EliteHunt"); active = Contract.EliteHunt; } DebugLog($"Offered {active} contract - {Describe(active)}"); if (NotificationsModule.Gate) { Chat.AddMessage("<color=#4dd2ff>CONTRACT:</color> " + Describe(active)); } } private string Describe(Contract c) { return c switch { Contract.KillTheTitan => "Kill the Titan before the teleporter finishes charging.", Contract.NoHealing => $"Heal no more than {NoHealingCap.Value:0} this stage.", Contract.SpeedClear => $"Activate the teleporter within {SpeedClearSeconds.Value:0} seconds.", Contract.EliteHunt => $"Kill {EliteHuntCount.Value} elites this stage.", Contract.StayInside => "Charge the teleporter without leaving the zone.", Contract.PacifistStart => "Do not kill anything for the first 60 seconds.", _ => "", }; } public override float ModifyHeal(HealthComponent target, float amount) { if (active == Contract.NoHealing && !resolved && Object.op_Implicit((Object)(object)target.body) && ModuleBase.IsPlayer(target.body)) { healedThisStage += amount; if (healedThisStage > NoHealingCap.Value) { DebugLog($"NoHealing contract broken - healed {healedThisStage:0.0}/{NoHealingCap.Value:0} this stage"); Fail(); } } return amount; } public override void OnDeath(DamageReport report) { if (active == Contract.None || resolved || report == null) { return; } if (active == Contract.PacifistStart && Object.op_Implicit((Object)(object)report.attackerBody) && ModuleBase.IsPlayer(report.attackerBody) && Time.time - stageStartTime < 60f) { DebugLog($"PacifistStart contract broken - kill at {Time.time - stageStartTime:0.0}s into stage (limit 60s)"); pacifistBroken = true; Fail(); return; } if (active == Contract.EliteHunt && Object.op_Implicit((Object)(object)report.victimBody) && report.victimBody.isElite && Object.op_Implicit((Object)(object)report.attackerBody) && ModuleBase.IsPlayer(report.attackerBody)) { elitesKilledThisStage++; DebugLog($"EliteHunt progress: {elitesKilledThisStage}/{EliteHuntCount.Value} elites killed"); if (elitesKilledThisStage >= EliteHuntCount.Value) { Succeed(); } } if (active == Contract.KillTheTitan && (Object)(object)titanMaster != (Object)null && Object.op_Implicit((Object)(object)report.victimBody) && (Object)(object)report.victimBody.master == (Object)(object)titanMaster) { DebugLog("KillTheTitan contract target killed"); titanKilled = true; Succeed(); } } public override void OnFixedUpdateServer() { if (active == Contract.None || resolved) { return; } TeleporterInteraction instance = TeleporterInteraction.instance; if (active == Contract.SpeedClear) { if (Object.op_Implicit((Object)(object)instance) && instance.isCharged) { DebugLog($"SpeedClear contract succeeded - teleporter charged at {Time.time - stageStartTime:0.0}s (limit {SpeedClearSeconds.Value:0}s)"); Succeed(); } else if (Time.time - stageStartTime > SpeedClearSeconds.Value) { DebugLog($"SpeedClear contract failed - {SpeedClearSeconds.Value:0}s elapsed without charging teleporter"); Fail(); } } if (active == Contract.StayInside && Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)instance.holdoutZoneController) && ((Behaviour)instance.holdoutZoneController).isActiveAndEnabled && instance.holdoutZoneController.charge > 0f && instance.holdoutZoneController.charge < 1f) { bool flag = false; foreach (PlayerCharacterMasterController instance2 in PlayerCharacterMasterController.instances) { CharacterBody val = ((Object.op_Implicit((Object)(object)instance2) && Object.op_Implicit((Object)(object)instance2.master)) ? instance2.master.GetBody() : null); if (Object.op_Implicit((Object)(object)val) && instance.holdoutZoneController.IsBodyInChargingRadius(val)) { flag = true; break; } } if (!flag) { DebugLog($"StayInside contract failed - no player inside charging radius at {instance.holdoutZoneController.charge * 100f:0}% charge"); Fail(); } else if (instance.isCharged) { DebugLog("StayInside contract succeeded - teleporter fully charged with a player inside the zone throughout"); Succeed(); } } if (active == Contract.PacifistStart && !pacifistBroken && Time.time - stageStartTime >= 60f) { DebugLog("PacifistStart contract succeeded - 60s elapsed with no kills"); Succeed(); } if (active == Contract.KillTheTitan && !titanKilled && Object.op_Implicit((Object)(object)instance) && instance.isCharged) { DebugLog("KillTheTitan contract failed - teleporter charged before the titan was killed"); Fail(); } } private void Succeed() { if (!resolved) { resolved = true; DebugLog($"{active} contract resolved: SUCCESS - granting reward (RewardGoldMultiplier={RewardGoldMultiplier.Value})"); GrantReward(); if (NotificationsModule.Gate) { Chat.AddMessage("<color=#7fff9d>CONTRACT COMPLETE!</color> Reward granted."); } } } private void Fail() { if (!resolved) { resolved = true; DebugLog($"{active} contract resolved: FAILED - no penalty applied"); if (NotificationsModule.Gate) { Chat.AddMessage("<color=#ff7a7a>Contract failed.</color> No penalty - better luck next stage."); } } } private void GrantReward() { foreach (PlayerCharacterMasterController instance in PlayerCharacterMasterController.instances) { if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)instance.master)) { uint money = instance.master.money; uint num = (uint)Mathf.Max(25f, (float)instance.master.money * RewardGoldMultiplier.Value); instance.master.GiveMoney(num); DebugLog($"Granted {num} bonus gold to {((Object)instance.master).name} (had {money}, now {instance.master.money})"); } } } public override float RewardMultiplier(DamageReport report) { if (active == Contract.None || resolved) { return 1f; } return 1.1f; } private bool TrySpawnTitan() { //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) List<CharacterBody> list = new List<CharacterBody>(); foreach (CharacterBody readOnlyInstances in CharacterBody.readOnlyInstancesList) { if (Object.op_Implicit((Object)(object)readOnlyInstances) && ModuleBase.IsHostileEnemy(readOnlyInstances) && !readOnlyInstances.isBoss && Object.op_Implicit((Object)(object)readOnlyInstances.healthComponent) && readOnlyInstances.healthComponent.alive) { list.Add(readOnlyInstances); } } if (list.Count == 0) { DebugLog("TrySpawnTitan found no eligible non-boss hostile enemy on the stage"); return false; } list.Sort((CharacterBody a, CharacterBody b) => b.maxHealth.CompareTo(a.maxHealth)); CharacterBody val = list[rng.Next(Mathf.Min(3, list.Count))]; titanMaster = val.master; DebugLog($"TrySpawnTitan chose {((Object)val).name} (base maxHealth {val.maxHealth:0}) from a pool of {list.Count} candidates"); ModelLocator modelLocator = val.modelLocator; if (Object.op_Implicit((Object)(object)modelLocator) && Object.op_Implicit((Object)(object)modelLocator.modelTransform)) { Transform transform = ((Component)modelLocator.modelTransform).gameObject.transform; transform.localScale *= 3f; } if ((Object)(object)val.inventory != (Object)null) { EliteDef fire = Elites.Fire; if (Object.op_Implicit((Object)(object)fire) && Object.op_Implicit((Object)(object)fire.eliteEquipmentDef)) { val.inventory.SetEquipmentIndex(fire.eliteEquipmentDef.equipmentIndex, false); } val.inventory.GiveItemPermanent(Items.BoostHp, 120); val.inventory.GiveItemPermanent(Items.BoostDamage, 30); } val.RecalculateStats(); if (Object.op_Implicit((Object)(object)val.healthComponent)) { val.healthComponent.HealFraction(1f, default(ProcChainMask)); } DebugLog($"Titan ready: {((Object)val).name} scaled 3x, Fire elite affix, +120 BoostHp, +30 BoostDamage, final maxHealth {val.maxHealth:0}"); return (Object)(object)titanMaster != (Object)null; } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("dileppy.petrichorprotocol", "PetrichorProtocol", "2.16.0")] public class PetrichorProtocolPlugin : BaseUnityPlugin { public const string PluginGUID = "dileppy.petrichorprotocol"; public const string PluginName = "PetrichorProtocol"; public const string PluginVersion = "2.16.0"; internal static PetrichorProtocolPlugin Instance; internal static ManualLogSource Log; internal static readonly List<ModuleBase> Modules = new List<ModuleBase>(); private static readonly List<ModuleBase> hookBodyStart = new List<ModuleBase>(); private static readonly List<ModuleBase> hookRecalc = new List<ModuleBase>(); private static readonly List<ModuleBase> hookProjectile = new List<ModuleBase>(); private static readonly List<ModuleBase> hookBullet = new List<ModuleBase>(); private static readonly List<ModuleBase> hookSpawnEffect = new List<ModuleBase>(); private static readonly List<ModuleBase> hookReward = new List<ModuleBase>(); private static readonly List<ModuleBase> hookDeath = new List<ModuleBase>(); private static readonly List<ModuleBase> hookRunStart = new List<ModuleBase>(); private static readonly List<ModuleBase> hookStageStart = new List<ModuleBase>(); private static readonly List<ModuleBase> hookHeal = new List<ModuleBase>(); private static readonly List<ModuleBase> hookFixed = new List<ModuleBase>(); private static readonly List<(string name, int stars)> externalRegistered = new List<(string, int)>(); public static ConfigEntry<bool> EnableRewardScaling; public static ConfigEntry<float> MaxRewardMultiplier; public static ConfigEntry<bool> ShowRunSummary; public static ConfigEntry<bool> DebugLogging; private static int riskScore; private static float protocolRewardMult = 1f; private static bool spawnEffectHooked; private static void BuildHookLists() { hookBodyStart.Clear(); hookRecalc.Clear(); hookProjectile.Clear(); hookBullet.Clear(); hookSpawnEffect.Clear(); hookReward.Clear(); hookDeath.Clear(); hookRunStart.Clear(); hookStageStart.Clear(); hookHeal.Clear(); hookFixed.Clear(); foreach (ModuleBase module in Modules) { if (module.Active) { if (module.UsesBodyStart) { hookBodyStart.Add(module); } if (module.UsesRecalcStats) { hookRecalc.Add(module); } if (module.UsesProjectile) { hookProjectile.Add(module); } if (module.UsesBulletFire) { hookBullet.Add(module); } if (module.UsesSpawnEffect) { hookSpawnEffect.Add(module); } if (module.UsesReward) { hookReward.Add(module); } if (module.UsesDeath) { hookDeath.Add(module); } if (module.UsesRunStart) { hookRunStart.Add(module); } if (module.UsesStageStart) { hookStageStart.Add(module); } if (module.UsesHeal) { hookHeal.Add(module); } if (module.UsesFixedUpdate) { hookFixed.Add(module); } } } } public static void RegisterModifier(string displayName, int stars) { externalRegistered.Add((displayName, Mathf.Clamp(stars, 1, 5))); } private void Awake() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Expected O, but got Unknown //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Expected O, but got Unknown //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Expected O, but got Unknown //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Expected O, but got Unknown //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Expected O, but got Unknown //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; EnableRewardScaling = ((BaseUnityPlugin)this).Config.Bind<bool>("Protocol", "EnableRewardScaling", true, "Scale gold and XP up based on the total Risk Score of active features."); MaxRewardMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Protocol", "MaxRewardMultiplier", 3f, new ConfigDescription("The highest the gold/XP reward multiplier can reach, no matter how high your Risk Score climbs.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>())); ShowRunSummary = ((BaseUnityPlugin)this).Config.Bind<bool>("Protocol", "ShowRunSummary", true, "Announce the Risk Score, tier, and active features in chat at run start."); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Protocol", "DebugLogging", false, "Write extra Protocol diagnostics to the console log. Only useful for troubleshooting; leave off normally."); Modules.Add(new ShrunkenSurvivorModule()); Modules.Add(new EnlargedSurvivorModule()); Modules.Add(new ShrunkenEnemiesModule()); Modules.Add(new EnlargedEnemiesModule()); Modules.Add(new BiggerBulletsModule()); Modules.Add(new EnemyMutationsModule()); Modules.Add(new BossMutationsModule()); Modules.Add(new PresetsModule()); Modules.Add(new CombosModule()); Modules.Add(new CurseDeckModule()); Modules.Add(new HazardsModule()); Modules.Add(new WorldEventsModule()); Modules.Add(new StagePersonalitiesModule()); Modules.Add(new TeleporterRulesModule()); Modules.Add(new EnemySwarmModule()); Modules.Add(new ItemDietModule()); Modules.Add(new LootMutationsModule()); Modules.Add(new SizeRouletteModule()); Modules.Add(new ChallengeContractsModule()); Modules.Add(new RandomizerModule()); Modules.Add(new NotificationsModule()); Modules.Add(new RunTitlesModule()); Modules.Add(new HudModule()); Modules.Add(new TitleBrandingModule()); foreach (ModuleBase module in Modules) { module.Bind(((BaseUnityPlugin)this).Config); } foreach (ModuleBase module2 in Modules) { if (module2.Enabled != null) { module2.Enabled.SettingChanged += delegate { BuildHookLists(); RefreshDynamicHooks(); RefreshRiskLine(); }; } } BuildHookLists(); CharacterBody.Start += new hook_Start(CharacterBody_Start); CharacterBody.RecalculateStats += new hook_RecalculateStats(CharacterBody_RecalculateStats); ProjectileController.Start += new hook_Start(ProjectileController_Start); BulletAttack.Fire += new hook_Fire(BulletAttack_Fire); DeathRewards.OnKilledServer += new hook_OnKilledServer(DeathRewards_OnKilledServer); GlobalEventManager.onCharacterDeathGlobal += OnCharacterDeath; Run.onRunStartGlobal += OnRunStart; Stage.onStageStartGlobal += OnStageStart; HealthComponent.Heal += new hook_Heal(HealthComponent_Heal); RefreshDynamicHooks(); ((Component)this).gameObject.AddComponent<DebugMenuController>(); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("The Petrichor Protocol v{0} online. {1} modules loaded.", "2.16.0", Modules.Count)); } private void Start() { foreach (ModuleBase module in Modules) { module.StandaloneAlsoInstalled = module.StandaloneGuid != null && Chainloader.PluginInfos.ContainsKey(module.StandaloneGuid); if (module.StandaloneAlsoInstalled) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Protocol: standalone " + module.Name + " is also installed. Protocol settings take precedence; the standalone mod is dormant.")); } } RiskOfOptionsBridge.TryRegisterAll(((BaseUnityPlugin)this).Config); } internal static void RefreshDynamicHooks() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown if ((Object)(object)Instance == (Object)null) { return; } bool flag = false; for (int i = 0; i < Modules.Count; i++) { if (Modules[i].WantsSpawnEffectHook) { flag = true; break; } } if (flag == spawnEffectHooked) { return; } try { if (flag) { EffectManager.SpawnEffect_GameObject_EffectData_bool += new hook_SpawnEffect_GameObject_EffectData_bool(Instance.EffectManager_SpawnEffect); } else { EffectManager.SpawnEffect_GameObject_EffectData_bool -= new hook_SpawnEffect_GameObject_EffectData_bool(Instance.EffectManager_SpawnEffect); } spawnEffectHooked = flag; ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("Protocol: SpawnEffect hook " + (flag ? "attached (tracer scaling on)" : "detached (not needed - zero cost)") + ".")); } } catch (Exception arg) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)$"Protocol: SpawnEffect hook toggle failed: {arg}"); } } } private void OnDestroy() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown CharacterBody.Start -= new hook_Start(CharacterBody_Start); CharacterBody.RecalculateStats -= new hook_RecalculateStats(CharacterBody_RecalculateStats); ProjectileController.Start -= new hook_Start(ProjectileController_Start); BulletAttack.Fire -= new hook_Fire(BulletAttack_Fire); DeathRewards.OnKilledServer -= new hook_OnKilledServer(DeathRewards_OnKilledServer); GlobalEventManager.onCharacterDeathGlobal -= OnCharacterDeath; Run.onRunStartGlobal -= OnRunStart; Stage.onStageStartGlobal -= OnStageStart; HealthComponent.Heal -= new hook_Heal(HealthComponent_Heal); if (spawnEffectHooked) { EffectManager.SpawnEffect_GameObject_EffectData_bool -= new hook_SpawnEffect_GameObject_EffectData_bool(EffectManager_SpawnEffect); spawnEffectHooked = false; } foreach (ModuleBase module in Modules) { try { module.Unhook(); } catch (Exception arg) { Log.LogError((object)$"{module.Name}.Unhook: {arg}"); } } } private void CharacterBody_Start(orig_Start orig, CharacterBody self) { orig.Invoke(self); for (int i = 0; i < hookBodyStart.Count; i++) { ModuleBase moduleBase = hookBodyStart[i]; if (moduleBase.Active) { try { moduleBase.OnBodyStart(self); } catch (Exception arg) { Log.LogError((object)$"{moduleBase.Name}.OnBodyStart: {arg}"); } } } } private void CharacterBody_RecalculateStats(orig_RecalculateStats orig, CharacterBody self) { orig.Invoke(self); for (int i = 0; i < hookRecalc.Count; i++) { ModuleBase moduleBase = hookRecalc[i]; if (moduleBase.Active) { try { moduleBase.OnRecalcStats(self); } catch (Exception arg) { Log.LogError((object)$"{moduleBase.Name}.OnRecalcStats: {arg}"); } } } } private void ProjectileController_Start(orig_Start orig, ProjectileController self) { orig.Invoke(self); for (int i = 0; i < hookProjectile.Count; i++) { ModuleBase moduleBase = hookProjectile[i]; if (moduleBase.Active) { try { moduleBase.OnProjectileStart(self); } catch (Exception arg) { Log.LogError((object)$"{moduleBase.Name}.OnProjectileStart: {arg}"); } } } } private void BulletAttack_Fire(orig_Fire orig, BulletAttack self) { bool flag = false; for (int i = 0; i < hookBullet.Count; i++) { if (flag) { break; } ModuleBase moduleBase = hookBullet[i]; if (moduleBase.Active) { try { flag = moduleBase.OnBulletFire(self, orig); } catch (Exception arg) { Log.LogError((object)$"{moduleBase.Name}.OnBulletFire: {arg}"); } } } if (!flag) { orig.Invoke(self); } } private void EffectManager_SpawnEffect(orig_SpawnEffect_GameObject_EffectData_bool orig, GameObject effectPrefab, EffectData effectData, bool transmit) { for (int i = 0; i < hookSpawnEffect.Count; i++) { ModuleBase moduleBase = hookSpawnEffect[i]; if (moduleBase.Active) { try { moduleBase.OnSpawnEffect(effectPrefab, effectData); } catch (Exception arg) { Log.LogError((object)$"{moduleBase.Name}.OnSpawnEffect: {arg}"); } } } orig.Invoke(effectPrefab, effectData, transmit); } private void DeathRewards_OnKilledServer(orig_OnKilledServer orig, DeathRewards self, DamageReport report) { float num = 1f; for (int i = 0; i < hookReward.Count; i++) { ModuleBase moduleBase = hookReward[i]; if (moduleBase.Active) { try { num *= moduleBase.RewardMultiplier(report); } catch (Exception arg) { Log.LogError((object)$"{moduleBase.Name}.RewardMultiplier: {arg}"); } } } if (EnableRewardScaling.Value) { num *= protocolRewardMult; } if (float.IsNaN(num) || float.IsInfinity(num)) { num = 1f; } num = Mathf.Clamp(num, 0f, 1000f); if (num != 1f) { self.goldReward = (uint)((float)self.goldReward * num); self.expReward = (uint)((float)self.expReward * num); } orig.Invoke(self, report); } private void OnCharacterDeath(DamageReport report) { for (int i = 0; i < hookDeath.Count; i++) { ModuleBase moduleBase = hookDeath[i]; if (moduleBase.Active) { try { moduleBase.OnDeath(report); } catch (Exception arg) { Log.LogError((object)$"{moduleBase.Name}.OnDeath: {arg}"); } } } } private void OnStageStart(Stage stage) { for (int i = 0; i < hookStageStart.Count; i++) { ModuleBase moduleBase = hookStageStart[i]; if (moduleBase.Active) { try { moduleBase.OnStageStart(stage); } catch (Exception arg) { Log.LogError((object)$"{moduleBase.Name}.OnStageStart: {arg}"); } } } } private void FixedUpdate() { if (hookFixed.Count == 0 || !NetworkServer.active || (Object)(object)Run.instance == (Object)null) { return; } for (int i = 0; i < hookFixed.Count; i++) { ModuleBase moduleBase = hookFixed[i]; if (moduleBase.Active) { try { moduleBase.OnFixedUpdateServer(); } catch (Exception arg) { Log.LogError((object)$"{moduleBase.Name}.OnFixedUpdateServer: {arg}"); } } } } private float HealthComponent_Heal(orig_Heal orig, HealthComponent self, float amount, ProcChainMask procChainMask, bool nonRegen) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < hookHeal.Count; i++) { ModuleBase moduleBase = hookHeal[i]; if (moduleBase.Active) { try { amount = moduleBase.ModifyHeal(self, amount); } catch (Exception arg) { Log.LogError((object)$"{moduleBase.Name}.ModifyHeal: {arg}"); } } } return orig.Invoke(self, amount, procChainMask, nonRegen); } internal static string TierName(int score) { if (score <= 0) { return "Vanilla"; } if (score <= 5) { return "Casual Chaos"; } if (score <= 10) { return "Risky"; } if (score <= 15) { return "Brutal"; } if (score <= 20) { return "Nightmare"; } return "Apocalypse"; } private void OnRunStart(Run run) { foreach (ModuleBase module in Modules) { if (module.Active) { try { module.OnRunStart(); } catch (Exception arg) { Log.LogError((object)$"{module.Name}.OnRunStart: {arg}"); } } } BuildHookLists(); RefreshDynamicHooks(); List<(string, int)> list = RefreshRiskLine(); if (!ShowRunSummary.Value || list.Count == 0 || !NotificationsModule.Gate) { return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<color=#7fdbca>THE PETRICHOR PROTOCOL</color> - Risk Score <color=#ffd700>").Append(riskScore).Append("</color>"); stringBuilder.Append(" - Tier: <color=#ff6b6b>").Append(TierName(riskScore)).Append("</color>"); if (EnableRewardScaling.Value) { stringBuilder.Append(" - Rewards <color=#ffd700>x").Append(protocolRewardMult.ToString("F2")).Append("</color>"); } Chat.AddMessage(stringBuilder.ToString()); StringBuilder stringBuilder2 = new StringBuilder("Active: "); for (int i = 0; i < list.Count; i++) { if (i > 0) { stringBuilder2.Append(", "); } stringBuilder2.Append(list[i].Item1).Append(" (").Append(new string('*', list[i].Item2)) .Append(")"); } Chat.AddMessage(stringBuilder2.ToString()); } private List<(string, int)> RefreshRiskLine() { riskScore = 0; List<(string, int)> list = new List<(string, int)>(); foreach (ModuleBase module in Modules) { if (module.Stars > 0 && module.FeatureActive) { riskScore += module.Stars; list.Add((module.Name + (module.StandaloneAlsoInstalled ? " (protocol-controlled)" : ""), module.Stars)); } } foreach (var (item, num) in externalRegistered) { riskScore += num; list.Add((item, num)); } protocolRewardMult = ((riskScore <= 5) ? (1f + (float)riskScore * 0.02f) : (1f + 0.004f * (float)riskScore * (float)riskScore)); protocolRewardMult = Mathf.Min(protocolRewardMult, MaxRewardMultiplier.Value); HudModule.RiskLine = $"<color=#ffd700>Risk {riskScore}</color> - <color=#ff6b6b>{TierName(riskScore)}</color>"; return list; } } internal abstract class ModuleBase { internal class SizeMarker : MonoBehaviour { public Vector3 baseScale; } internal class AimOriginMarker : MonoBehaviour { public Vector3 baseAimLocalPos; public Vector3 modelPivotToBodyRoot; } public ConfigEntry<bool> Enabled; public bool StandaloneAlsoInstalled; public abstract string Name { get; } public abstract string StandaloneGuid { get; } public abstract int Stars { get; } public bool Active { get { if (Enabled != null) { return Enabled.Value; } return false; } } public bool FeatureActive => Active; public virtual bool UsesBodyStart => false; public virtual bool UsesRecalcStats => false; public virtual bool UsesProjectile => false; public virtual bool UsesBulletFire => false; public virtual bool UsesSpawnEffect => false; public virtual bool UsesReward => false; public virtual bool UsesDeath => false; public virtual bool UsesRunStart => false; public virtual bool UsesStageStart => false; public virtual bool UsesHeal => false; public virtual bool UsesFixedUpdate => false; public virtual bool WantsSpawnEffectHook => false; public abstract void Bind(ConfigFile cfg); protected void DebugLog(string message) { if (PetrichorProtocolPlugin.DebugLogging != null && PetrichorProtocolPlugin.DebugLogging.Value) { PetrichorProtocolPlugin.Log.LogInfo((object)("[" + Name + "] " + message)); } } public virtual void OnBodyStart(CharacterBody body) { } public virtual void OnRecalcStats(CharacterBody body) { } public virtual void OnProjectileStart(ProjectileController proj) { } public virtual bool OnBulletFire(BulletAttack attack, orig_Fire orig) { return false; } public virtual void OnSpawnEffect(GameObject prefab, EffectData data) { } public virtual float RewardMultiplier(DamageReport report) { return 1f; } public virtual void OnDeath(DamageReport report) { } public virtual void OnRunStart() { } public virtual void OnStageStart(Stage stage) { } public virtual float ModifyHeal(HealthComponent target, float amount) { return amount; } public virtual void OnFixedUpdateServer() { } public virtual void Unhook() { } protected static bool IsPlayer(CharacterBody body) { if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.master)) { return Object.op_Implicit((Object)(object)body.master.playerCharacterMasterController); } return false; } protected static bool IsHostileEnemy(CharacterBody body) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 if (!Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)body.teamComponent)) { return false; } TeamIndex teamIndex = body.teamComponent.teamIndex; if ((int)teamIndex != 2 && (int)teamIndex != 3) { return (int)teamIndex == 4; } return true; } protected static void ApplyAbsoluteScale(GameObject go, float factor) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)go)) { return; } Scene scene = go.scene; if (((Scene)(ref scene)).IsValid()) { SizeMarker sizeMarker = go.GetComponent<SizeMarker>(); if (!Object.op_Implicit((Object)(object)sizeMarker)) { sizeMarker = go.AddComponent<SizeMarker>(); sizeMarker.baseScale = go.transform.localScale; } go.transform.localScale = sizeMarker.baseScale * factor; } } private static void ApplyAimOriginScale(CharacterBody body, Transform modelTransform, float factor) { //IL_006c: 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) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.aimOriginTransform) && Object.op_Implicit((Object)(object)modelTransform)) { AimOriginMarker aimOriginMarker = ((Component)body).gameObject.GetComponent<AimOriginMarker>(); if (!Object.op_Implicit((Object)(object)aimOriginMarker)) { aimOriginMarker = ((Component)body).gameObject.AddComponent<AimOriginMarker>(); aimOriginMarker.baseAimLocalPos = body.aimOriginTransform.localPosition; aimOriginMarker.modelPivotToBodyRoot = body.transform.position - modelTransform.position; } Vector3 val = (aimOriginMarker.modelPivotToBodyRoot + aimOriginMarker.baseAimLocalPos) * factor; body.aimOriginTransform.localPosition = val - aimOriginMarker.modelPivotToBodyRoot; } } protected static void ApplyModelScale(CharacterBody body, float factor) { ModelLocator modelLocator = body.modelLocator; if (Object.op_Implicit((Object)(object)modelLocator) && Object.op_Implicit((Object)(object)modelLocator.modelTransform)) { ApplyAbsoluteScale(((Component)modelLocator.modelTransform).gameObject, factor); if (IsPlayer(body)) { ApplyAimOriginScale(body, modelLocator.modelTransform, factor); } } } } internal static class RiskOfOptionsBridge { private static readonly HashSet<string> KeepEnabledSections = new HashSet<string> { "HUD", "Notifications", "RunTitles", "Protocol" }; private static string CategoryFor(string section) { switch (section) { case "Presets": return "0. Presets (Start Here)"; case "EnlargedSurvivor": case "ShrunkenSurvivor": case "EnlargedEnemies": case "ShrunkenEnemies": case "BiggerBullets": case "SizeRoulette": return "1. Size & Scale"; case "BossMutations": case "EnemyMutations": return "2. Mutations"; case "Hazards": case "TeleporterRules": case "CurseDeck": case "EnemySwarm": case "WorldEvents": case "StagePersonalities": return "3. Environment & Events"; case "LootMutations": case "ItemDiet": return "4. Items & Loot"; case "Contracts": case "Randomizer": case "Combos": return "5. Randomizer, Combos & Contracts"; case "TitleBranding": case "Notifications": case "RunTitles": case "HUD": return "6. Interface & Feedback"; default: return "7. Protocol Core"; } } [MethodImpl(MethodImplOptions.NoInlining)] public static void TryRegisterAll(ConfigFile cfg) { if (!Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions")) { return; } try { RegisterAll(cfg); } catch (Exception ex) { PetrichorProtocolPlugin.Log.LogWarning((object)("Protocol: Risk of Options registration failed (" + ex.Message + "). Config remains file-based.")); } } [MethodImpl(MethodImplOptions.NoInlining)] private static void RegisterAll(ConfigFile cfg) { int num = 0; List<ConfigEntryBase> list = new List<ConfigEntryBase>(((IDictionary<ConfigDefinition, ConfigEntryBase>)cfg).Values); list.Sort(delegate(ConfigEntryBase a, ConfigEntryBase b) { int num2 = string.CompareOrdinal(CategoryFor(a.Definition.Section), CategoryFor(b.Definition.Section)); if (num2 != 0) { return num2; } int num3 = string.CompareOrdinal(a.Definition.Section, b.Definition.Section); if (num3 != 0) { return num3; } bool flag = a.Definition.Key == "Enabled"; bool flag2 = b.Definition.Key == "Enabled"; return (flag != flag2) ? ((!flag) ? 1 : (-1)) : string.CompareOrdinal(a.Definition.Key, b.Definition.Key); }); foreach (ConfigEntryBase item in list) { if (!(item.Definition.Section == "DebugMenu") && TryAddOption(item)) { num++; } } TryAddResetButton(cfg); TryAddPresetButtons(); PetrichorProtocolPlugin.Log.LogInfo((object)$"Protocol: {num} settings registered with Risk of Options, bucketed into categories."); } [MethodImpl(MethodImplOptions.NoInlining)] private static void TryAddPresetButtons() { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown PresetsModule presets = null; foreach (ModuleBase module in PetrichorProtocolPlugin.Modules) { if (module is PresetsModule presetsModule) { presets = presetsModule; break; } } if (presets == null) { return; } foreach (PresetsModule.Preset value in Enum.GetValues(typeof(PresetsModule.Preset))) { if (value == PresetsModule.Preset.None) { continue; } PresetsModule.Preset captured = value; try { ModSettingsManager.AddOption((BaseOption)new GenericButtonOption(PresetsModule.Prettify(captured), "0. Presets (Start Here)", PresetsModule.Flavor(captured), "Apply", (UnityAction)delegate { presets.ApplyNow(captured); })); } catch (Exception ex) { PetrichorProtocolPlugin.Log.LogWarning((object)$"Protocol: could not add preset button for {captured} ({ex.Message})."); } } } [MethodImpl(MethodImplOptions.NoInlining)] private static void TryAddResetButton(ConfigFile cfg) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown try { ModSettingsManager.AddOption((BaseOption)new GenericButtonOption("Reset all settings to default", "7. Protocol Core", "Turn every gameplay feature off and restore all sliders and options to their original values, for a genuine clean slate. The HUD and notifications stay on. Use this to clear a complex setup and rebuild from scratch, enabling just the features you want.", "Reset", (UnityAction)delegate { ResetAllToDefault(cfg); })); } catch (Exception ex) { PetrichorProtocolPlugin.Log.LogWarning((object)("Protocol: could not add reset button (" + ex.Message + ").")); } } internal static void ResetAllToDefault(ConfigFile cfg) { int num = 0; int num2 = 0; foreach (ConfigEntryBase item in new List<ConfigEntryBase>(((IDictionary<ConfigDefinition, ConfigEntryBase>)cfg).Values)) { try { item.BoxedValue = item.DefaultValue; num++; if (item.Definition.Key == "Enabled" && item.SettingType == typeof(bool) && !KeepEnabledSections.Contains(item.Definition.Section)) { item.BoxedValue = false; num2++; } } catch (Exception ex) { PetrichorProtocolPlugin.Log.LogWarning((object)("Protocol reset: could not reset " + item.Definition.Section + "/" + item.Definition.Key + " (" + ex.Message + ").")); } } cfg.Save(); PetrichorProtocolPlugin.Log.LogInfo((object)$"Protocol: reset {num} settings to default; {num2} gameplay features turned off."); try { Chat.AddMessage("<color=#7fdbca>THE PETRICHOR PROTOCOL</color> - reset complete. All gameplay features are now off - pick the ones you want."); } catch { } } private static string FriendlySection(string section) { switch (section) { case "HUD": return "HUD"; case "Protocol": return "Protocol Core"; case "Hazards": return "Environmental Hazards"; default: { StringBuilder stringBuilder = new StringBuilder(section.Length + 4); for (int i = 0; i < section.Length; i++) { char c = section[i]; if (i > 0 && char.IsUpper(c) && !char.IsUpper(section[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(c); } return stringBuilder.ToString(); } } } private static string FriendlyName(string section, string key) { string text = FriendlySection(section); if (key == "Enabled") { return text; } string text2 = SpaceCamel(key); return text + ": " + text2; } private static string SpaceCamel(string s) { StringBuilder stringBuilder = new StringBuilder(s.Length + 4); for (int i = 0; i < s.Length; i++) { char c = s[i]; if (i > 0 && char.IsUpper(c) && !char.IsUpper(s[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(c); } return stringBuilder.ToString(); } [MethodImpl(MethodImplOptions.NoInlining)] private static bool TryAddOption(ConfigEntryBase entry) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00b3: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected O, but got Unknown try { string section = entry.Definition.Section; string category = CategoryFor(section); string name = FriendlyName(section, entry.Definition.Key); Type settingType = entry.SettingType; if (settingType == typeof(bool)) { ModSettingsManager.AddOption((BaseOption)new CheckBoxOption((ConfigEntry<bool>)(object)entry, new CheckBoxConfig { category = category, name = name })); } else if (settingType == typeof(float)) { ModSettingsManager.AddOption((BaseOption)new SliderOption((ConfigEntry<float>)(object)entry, new SliderConfig { category = category, name = name })); } else if (settingType == typeof(int)) { ModSettingsManager.AddOption((BaseOption)new IntSliderOption((ConfigEntry<int>)(object)entry, new IntSliderConfig { category = category, name = name })); } else { if (!settingType.IsEnum) { return false; } ModSettingsManager.AddOption((BaseOption)new ChoiceOption(entry, new ChoiceConfig { category = category, name = name })); } return true; } catch { return false; } } } internal class DebugMenuController : MonoBehaviour { internal ConfigEntry<KeyCode> ToggleKey; internal bool WindowOpen; private Rect windowRect = new Rect(0f, 0f, 640f, 480f); private bool openedFromPauseMenu; private GUISkin scaledSkin; private GUIStyle scaledToolbarStyle; private GUIStyle scaledWrapLabelStyle; private float scaledSkinFor = -1f; private RectTransform pauseMenuMainPanelToRestore; private int selectedTab; private static readonly string[] TabNames = new string[7] { "Presets", "Size & Scale", "Mutations", "Environment & Events", "Items & Loot", "Randomizer/Combos/Contracts", "Interface & Feedback" }; private Vector2 scrollPos; private bool awaitingRebind; private static readonly Dictionary<Type, string[]> EnumNameCache = new Dictionary<Type, string[]>(); private static readonly HashSet<string> WarnedNoRange = new HashSet<string>(); private static readonly Dictionary<string, List<ConfigEntryBase>> SectionCache = new Dictionary<string, List<ConfigEntryBase>>(); private float UiScale => Mathf.Clamp((float)Screen.height / 1080f, 0.8f, 2.5f); private void EnsureScaledSkin() { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown float uiScale = UiScale; if (!((Object)(object)scaledSkin != (Object)null) || !Mathf.Approximately(uiScale, scaledSkinFor)) { scaledSkin = Object.Instantiate<GUISkin>(GUI.skin); int fontSize = Mathf.RoundToInt(15f * uiScale); int fontSize2 = Mathf.RoundToInt(18f * uiScale); scaledSkin.label.fontSize = fontSize; scaledSkin.button.fontSize = fontSize; scaledSkin.toggle.fontSize = fontSize; scaledSkin.window.fontSize = fontSize2; scaledSkin.horizontalSlider.fixedHeight = 16f * uiScale; scaledSkin.horizontalSliderThumb.fixedHeight = 16f * uiScale; scaledToolbarStyle = new GUIStyle(scaledSkin.FindStyle("toolbarbutton") ?? scaledSkin.button) { fontSize = fontSize }; scaledWrapLabelStyle = new GUIStyle(scaledSkin.label) { wordWrap = true }; scaledSkinFor = uiScale; } } private void ResetWindowSizeAndCenter() { ((Rect)(ref windowRect)).width = (float)Screen.width * 0.5f * 0.83f; ((Rect)(ref windowRect)).height = (float)Screen.height * 0.5f; ((Rect)(ref windowRect)).x = (float)Screen.width * 0.03f; ((Rect)(ref windowRect)).y = ((float)Screen.height - ((Rect)(ref windowRect)).height) / 2f; } private static string[] EnumNamesFor(Type t) { if (!EnumNameCache.TryGetValue(t, out var value)) { value = Enum.GetNames(t); EnumNameCache[t] = value; } return value; } private static void RenderEntry(ConfigEntryBase entry) { string text = SpaceCamel(entry.Definition.Key); Type settingType = entry.SettingType; if (settingType == typeof(bool)) { ConfigEntry<bool> obj = (ConfigEntry<bool>)(object)entry; obj.Value = GUILayout.Toggle(obj.Value, text, Array.Empty<GUILayoutOption>()); } else if (settingType == typeof(float)) { ConfigEntry<float> val = (ConfigEntry<float>)(object)entry; float num = 0f; float num2 = 1f; ConfigDescription description = entry.Description; if (((description != null) ? description.AcceptableValues : null) is AcceptableValueRange<float> val2) { num = val2.MinValue; num2 = val2.MaxValue; } else { WarnNoRange(entry); } GUILayout.Label($"{text}: {val.Value:0.00}", Array.Empty<GUILayoutOption>()); val.Value = GUILayout.HorizontalSlider(val.Value, num, num2, Array.Empty<GUILayoutOption>()); } else if (settingType == typeof(int)) { ConfigEntry<int> val3 = (ConfigEntry<int>)(object)entry; int num3 = 0; int num4 = 100; ConfigDescription description2 = entry.Description; if (((description2 != null) ? description2.AcceptableValues : null) is AcceptableValueRange<int> val4) { num3 = val4.MinValue; num4 = val4.MaxValue; } else { WarnNoRange(entry); } GUILayout.Label($"{text}: {val3.Value}", Array.Empty<GUILayoutOption>()); val3.Value = Mathf.RoundToInt(GUILayout.HorizontalSlider((float)val3.Value, (float)num3, (float)num4, Array.Empty<GUILayoutOption>())); } else if (settingType.IsEnum) { string[] array = EnumNamesFor(settingType); int num5 = Array.IndexOf<string>(array, entry.BoxedValue.ToString()); if (num5 < 0) { num5 = 0; } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(text + ": " + array[num5], Array.Empty<GUILayoutOption>()); if (GUILayout.Button("<", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) })) { entry.BoxedValue = Enum.Parse(settingType, array[(num5 - 1 + array.Length) % array.Length]); } if (GUILayout.Button(">", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) })) { entry.BoxedValue = Enum.Parse(settingType, array[(num5 + 1) % array.Length]); } GUILayout.EndHorizontal(); } } private static void WarnNoRange(ConfigEntryBase entry) { string item = entry.Definition.Section + "/" + entry.Definition.Key; if (!WarnedNoRange.Contains(item)) { WarnedNoRange.Add(item); PetrichorProtocolPlugin.Log.LogWarning((object)("DebugMenu: " + entry.Definition.Section + "/" + entry.Definition.Key + " has no AcceptableValueRange - using a default 0-1/0-100 slider range that may not fit its real values.")); } } private static string SpaceCamel(string s) { StringBuilder stringBuilder = new StringBuilder(s.Length + 4); for (int i = 0; i < s.Length; i++) { char c = s[i]; if (i > 0 && char.IsUpper(c) && !char.IsUpper(s[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(c); } return stringBuilder.ToString(); } private static string FriendlySection(string section) { return section switch { "HUD" => "HUD", "Protocol" => "Protocol Core", "Hazards" => "Environmental Hazards", _ => SpaceCamel(section), }; } private static List<ConfigEntryBase> EntriesForSections(ConfigFile cfg, params string[] sections) { string key = string.Join("|", sections); if (SectionCache.TryGetValue(key, out var value)) { return value; } HashSet<string> hashSet = new HashSet<string>(sections); List<ConfigEntryBase> list = new List<ConfigEntryBase>(); foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in (IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)cfg) { if (hashSet.Contains(item.Key.Section)) { list.Add(item.Value); } } list.Sort(delegate(ConfigEntryBase a, ConfigEntryBase b) { int num = string.CompareOrdinal(a.Definition.Section, b.Definition.Section); if (num != 0) { return num; } bool flag = a.Definition.Key == "Enabled"; bool flag2 = b.Definition.Key == "Enabled"; return (flag != flag2) ? ((!flag) ? 1 : (-1)) : string.CompareOrdinal(a.Definition.Key, b.Definition.Key); }); SectionCache[key] = list; return list; } private void Awake() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown ToggleKey = ((BaseUnityPlugin)PetrichorProtocolPlugin.Instance).Config.Bind<KeyCode>("DebugMenu", "ToggleKey", (KeyCode)284, "Opens/closes the PetrichorProtocol debug menu. Default F3 (not F2 - SpawnBox, a separate Dileppy mod, already uses F2)."); PauseScreenController.Awake += new hook_Awake(PauseScreenController_Awake); } private void Update() { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Invalid comparison between Unknown and I4 //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) if (openedFromPauseMenu && PauseScreenController.instancesList.Count == 0) { WindowOpen = false; openedFromPauseMenu = false; pauseMenuMainPanelToRestore = null; } if (awaitingRebind) { if (Input.GetKeyDown((KeyCode)27)) { awaitingRebind = false; } else { if (!Input.anyKeyDown) { return; } foreach (KeyCode value in Enum.GetValues(typeof(KeyCode))) { if ((int)value < 323 && Input.GetKeyDown(value)) { ToggleKey.Value = value; awaitingRebind = false; break; } } } } else if (Input.GetKeyDown(ToggleKey.Value)) { bool windowOpen = WindowOpen; WindowOpen = !WindowOpen; if (WindowOpen && !windowOpen) { ResetWindowSizeAndCenter(); } if (!WindowOpen) { RestorePauseMenuIfNeeded(); } } } private void OnGUI() { //IL_0026: 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_0046: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (WindowOpen) { EnsureScaledSkin(); GUISkin skin = GUI.skin; GUI.skin = scaledSkin; GUILayout.Window(((Object)this).GetInstanceID(), windowRect, new WindowFunction(DrawWindow), "PetrichorProtocol", Array.Empty<GUILayoutOption>()); GUI.skin = skin; } } private void DrawPresetsTab() { ConfigFile config = ((BaseUnityPlugin)PetrichorProtocolPlugin.Instance).Config; PresetsModule presetsModule = null; foreach (ModuleBase module in PetrichorProtocolPlugin.Modules) { if (module is PresetsModule presetsModule2) { presetsModule = presetsModule2; break; } } if (presetsModule == null) { GUILayout.Label("Presets module not found.", Array.Empty<GUILayoutOption>()); return; } foreach (ConfigEntryBase item in EntriesForSections(config, "Presets")) { if (item.Definition.Key == "SelectedPreset") { GUILayout.Label("Currently selected: " + PresetsModule.Prettify(presetsModule.Selected.Value), Array.Empty<GUILayoutOption>()); } else { RenderEntry(item); } } GUILayout.Space(8f); GUILayout.Label("Apply a preset:", Array.Empty<GUILayoutOption>()); float num = Mathf.Max(200f, ((Rect)(ref windowRect)).width - 60f); foreach (PresetsModule.Preset value in Enum.GetValues(typeof(PresetsModule.Preset))) { if (value == PresetsModule.Preset.None) { continue; } PresetsModule.Preset p = value; GUILayout.Label(PresetsModule.Prettify(p) + " - " + PresetsModule.Flavor(p), scaledWrapLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); if (GUILayout.Button("Apply", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) })) { try { presetsModule.ApplyNow(p); } catch (Exception arg) { PetrichorProtocolPlugin.Log.LogError((object)$"DebugMenu: preset apply failed - {arg}"); } } GUILayout.Space(6f); } GUILayout.Space(8f); if (GUILayout.Button("Reset all settings to default", Array.Empty<GUILayoutOption>())) { RiskOfOptionsBridge.ResetAllToDefault(config); } } private static void DrawSectionedTab(params string[] sections) { foreach (ConfigEntryBase item in EntriesForSections(((BaseUnityPlugin)PetrichorProtocolPlugin.Instance).Config, sections)) { if (item.Definition.Key == "Enabled") { GUILayout.Space(6f); GUILayout.Label(FriendlySection(item.Definition.Section), Array.Empty<GUILayoutOption>()); } RenderEntry(item); } } private void DrawInterfaceFeedbackTab() { //IL_00f3: Unknown result type (might be due to invalid IL or missing references) DrawSectionedTab("RunTitles", "HUD", "Notifications", "TitleBranding"); GUILayout.Space(10f); GUILayout.Label("— Protocol Core —", Array.Empty<GUILayoutOption>()); foreach (ConfigEntryBase item in EntriesForSections(((BaseUnityPlugin)PetrichorProtocolPlugin.Instance).Config, "Protocol", "DebugMenu")) { if (!(item.Definition.Section == "DebugMenu") || !(item.Definition.Key == "ToggleKey")) { RenderEntry(item); } } GUILayout.Space(6f); if (awaitingRebind) { GUILayout.Label("Press any key (Esc to cancel)...", Array.Empty<GUILayoutOption>()); return; } GUILayout.Label($"Toggle Key: {ToggleKey.Value}", Array.Empty<GUILayoutOption>()); if (GUILayout.Button("Rebind", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { awaitingRebind = true; } } private void DrawWindow(int id) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) selectedTab = GUILayout.Toolbar(selectedTab, TabNames, scaledToolbarStyle, Array.Empty<GUILayoutOption>()); scrollPos = GUILayout.BeginScrollView(scrollPos, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(((Rect)(ref windowRect)).height - 100f) }); try { switch (selectedTab) { case 0: DrawPresetsTab(); break; case 1: DrawSectionedTab("ShrunkenSurvivor", "EnlargedSurvivor", "ShrunkenEnemies", "EnlargedEnemies", "BiggerBullets", "SizeRoulette"); break; case 2: DrawSectionedTab("EnemyMutations", "BossMutations"); break; case 3: DrawSectionedTab("CurseDeck", "Hazards", "WorldEvents", "StagePersonalities", "TeleporterRules", "EnemySwarm"); break; case 4: DrawSectionedTab("ItemDiet", "LootMutations"); break; case 5: DrawSectionedTab("Combos", "Randomizer", "Contracts"); break; case 6: DrawInterfaceFeedbackTab(); break; default: GUILayout.Label(TabNames[selectedTab] + " - added in a later task", Array.Empty<GUILayoutOption>()); break; } } catch (Exception arg) { PetrichorProtocolPlugin.Log.LogError((object)$"DebugMenu: tab '{TabNames[selectedTab]}' failed to render - {arg}"); GUILayout.Label("This tab failed to render. See the log (" + TabNames[selectedTab] + ").", Array.Empty<GUILayoutOption>()); } GUILayout.EndScrollView(); if (GUILayout.Button("Close", Array.Empty<GUILayoutOption>())) { WindowOpen = false; RestorePauseMenuIfNeeded(); } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref windowRect)).width, 20f)); } private void PauseScreenController_Awake(orig_Awake orig, PauseScreenController self) { orig.Invoke(self); try { AddPauseMenuButton(self); } catch (Exception arg) { PetrichorProtocolPlugin.Log.LogError((object)$"DebugMenu: failed to add pause menu button - {arg}"); } } private void AddPauseMenuButton(PauseScreenController controller) { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown if ((Object)(object)controller.exitGameButton == (Object)null) { PetrichorProtocolPlugin.Log.LogWarning((object)"DebugMenu: PauseScreenController.exitGameButton was null, cannot add pause menu button."); return; } GameObject obj = Object.Instantiate<GameObject>(controller.exitGameButton, controller.exitGameButton.transform.parent); ((Object)obj).name = "PetrichorProtocolPauseButton"; LanguageTextMeshController component = obj.GetComponent<LanguageTextMeshController>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } HGTextMeshProUGUI componentInChildren = obj.GetComponentInChildren<HGTextMeshProUGUI>(); if ((Object)(object)componentInChildren != (Object)null) { ((TMP_Text)componentInChildren).text = "PetrichorProtocol"; } HGButton component2 = obj.GetComponent<HGButton>(); if ((Object)(object)component2 == (Object)null) { PetrichorProtocolPlugin.Log.LogWarning((object)"DebugMenu: cloned pause menu button has no HGButton component, cannot wire click."); return; } for (int i = 0; i < ((UnityEventBase)((Button)component2).onClick).GetPersistentEventCount(); i++) { ((UnityEventBase)((Button)component2).onClick).SetPersistentListenerState(i, (UnityEventCallState)0); } ((UnityEventBase)((Button)component2).onClick).RemoveAllListeners(); ((UnityEvent)((Button)component2).onClick).AddListener((UnityAction)delegate { OnPauseMenuButtonClicked(controller); }); } private void OnPauseMenuButtonClicked(PauseScreenController controller) { try { ((Component)controller.mainPanel).gameObject.SetActive(false); pauseMenuMainPanelToRestore = controller.mainPanel; openedFromPauseMenu = true; WindowOpen = true; ResetWindowSizeAndCenter(); } catch (Exception arg) { PetrichorProtocolPlugin.Log.LogError((object)$"DebugMenu: pause menu button click failed - {arg}"); } } private void LateUpdate() { if (openedFromPauseMenu && (Object)(object)pauseMenuMainPanelToRestore != (Object)null) { ((Component)pauseMenuMainPanelToRestore).gameObject.SetActive(false); } } private void RestorePauseMenuIfNeeded() { if (openedFromPauseMenu) { openedFromPauseMenu = false; if ((Object)(object)pauseMenuMainPanelToRestore != (Object)null) { ((Component)pauseMenuMainPanelToRestore).gameObject.SetActive(true); } pauseMenuMainPanelToRestore = null; } } } internal class TeleporterRulesModule : ModuleBase { public enum TeleRule { None, ShrinkingZone, BloodZone, EnemyFavoredZone, Overcharged } public ConfigEntry<float> RuleChance; private TeleRule active; private HoldoutZoneController zone; private float baseRadiusCache = -1f; private static readonly Random rng = new Random(); public override string Name => "Teleporter Rules"; public override string StandaloneGuid => null; public override int Stars => 2; public override bool UsesStageStart => true; public override bool UsesFixedUpdate => true; public override bool UsesRecalcStats => true; public override bool UsesHeal => true; public override void Bind(ConfigFile cfg) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown Enabled = cfg.Bind<bool>("TeleporterRules", "Enabled", false, "Each teleporter event can roll a rule modifier: Shrinking Zone, Blood Zone, Enemy-Favored Zone, Overcharged."); RuleChance = cfg.Bind<float>("TeleporterRules", "TeleporterRuleChance", 50f, new ConfigDescription("Chance (percent) that starting a teleporter event applies one random rule modifier to it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); HoldoutZoneController.OnEnable += new hook_OnEnable(Zone_OnEnable); } public override void Unhook() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown HoldoutZoneController.OnEnable -= new hook_OnEnable(Zone_OnEnable); } public override void OnStageStart(Stage stage) { active = TeleRule.None; zone = null; baseRadiusCache = -1f; } private void Zone_OnEnable(orig_OnEnable orig, HoldoutZoneController self) { orig.Invoke(self); if (!base.Active || !NetworkServer.active || !Object.op_Implicit((Object)(object)TeleporterInteraction.instance) || (Object)(object)((Component)self).gameObject != (Object)(object)((Component)TeleporterInteraction.instance.holdoutZoneController).gameObject) { return; } double num = rng.NextDouble() * 100.0; if (num > (double)RuleChance.Value) { DebugLog($"Teleporter rule rolled {num:0.0}% vs {RuleChance.Value:0.0}% chance - no rule applied this event"); active = TeleRule.None; return; } active = (TeleRule)(1 + rng.Next(4)); zone = self; baseRadiusCache = self.baseRadius; DebugLog($"Teleporter rule rolled {num:0.0}% vs {RuleChance.Value:0.0}% chance - applying {active} (baseRadius={baseRadiusCache:0.0})"); if (NotificationsModule.Gate) { string arg = active switch { TeleRule.ShrinkingZone => "The zone shrinks as it charges. It gets personal at the end.", TeleRule.BloodZone => "Inside the zone: +25% damage dealt, healing cut to 60%.", TeleRule.EnemyFavoredZone => "Enemies inside the zone gain 40 armor. Kite them out or fight uphill.", TeleRule.Overcharged => "Charges 50% faster - but the boss is much stronger.", _ => "", }; Chat.AddMessage($"<color=#ff9d5c>TELEPORTER RULE: {active}</color> - {arg}"); } } public override void OnFixedUpdateServer() { if (active == TeleRule.None || !Object.op_Implicit((Object)(object)zone) || !((Behaviour)zone).isActiveAndEnabled) { return; } try { if (active == TeleRule.ShrinkingZone && baseRadiusCache > 0f) { zone.baseRadius = Mathf.Lerp(baseRadiusCache, baseRadiusCache * 0.35f, zone.charge); } else if (active == TeleRule.Overcharged) { zone.charge = Mathf.Min(1f, zone.charge + 0.5f * Time.fixedDeltaTime * ChargeRatePerSecond(zone)); } } catch (Exception arg) { PetrichorProtocolPlugin.Log.LogError((object)$"{Name}: zone tick failed: {arg}"); } } private static float ChargeRatePerSecond(HoldoutZoneController z) { if (!(z.baseChargeDuration > 0f)) { return 0f; } return 1f / z.baseChargeDuration; } public bool InZone(CharacterBody body) { if (active != TeleRule.None && Object.op_Implicit((Object)(object)zone) && ((Behaviour)zone).isActiveAndEnabled) { return zone.IsBodyInChargingRadius(body); } return false; } public override void OnRecalcStats(CharacterBody self) { if (active == TeleRule.None || !Object.op_Implicit((Object)(object)zone)) { return; } switch (active) { case TeleRule.BloodZone: if (ModuleBase.IsPlayer(self) && InZone(self)) { self.damage *= 1.25f; } break; case TeleRule.EnemyFavoredZone: if (ModuleBase.IsHostileEnemy(self) && InZone(self)) { self.armor += 40f; } break; case TeleRule.Overcharged: if (self.isBoss) { self.maxHealth *= 1.5f; self.damage *= 1.4f; } break; } } public override float ModifyHeal(HealthComponent target, float amount) { if (active != TeleRule.BloodZone || !Object.op_Implicit((Object)(object)target.body) || !ModuleBase.IsPlayer(target.body) || !InZone(target.body)) { return amount; } return amount * 0.6f; } } internal class StagePersonalitiesModule : ModuleBase { public enum Personality { None, LowGravity, TreasureStorm, MonsterMigration, BossTerritory } public ConfigEntry<float> PersonalityChance; public ConfigEntry<float> RewardBonus; private Personality active; private Personality previous; private Vector3 baseGravity; private bool gravityStored; private float nextMigrationPulse; private static readonly Random rng = new Random(); public override string Name => "Stage Personalities"; public override string StandaloneGuid => null; public override int Stars => 2; public override bool UsesStageStart => true; public override bool UsesRunStart => true; public override bool UsesRecalcStats => true; public override bool UsesFixedUpdate => true; public override bool UsesReward => true; public override void OnRunStart() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (gravityStored) { Physics.gravity = baseGravity; gravityStored = false; } active = Personality.None; previous = Personality.None; } public override void Bind(ConfigFile cfg) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown Enabled = cfg.Bind<bool>("StagePersonalities", "Enabled", false, "Every stage can roll a temporary identity: Low Gravity, Treasure Storm, Monster Migration, Boss Territory."); PersonalityChance = cfg.Bind<float>("StagePersonalities", "PersonalityChance", 60f, new ConfigDescription("Chance (percent) that a stage takes on one random personality when you arrive.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); RewardBonus = cfg.Bind<float>("StagePersonalities", "RewardMultiplier", 0.2f, new ConfigDescription("Extra gold/XP granted while a stage personality is active, as a fraction (0.2 = +20%).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>())); SceneDirector.Start += new hook_Start(SceneDirector_Start); } public override void Unhook() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown SceneDirector.Start -= new hook_Start(SceneDirector_Start); } public override void OnStageStart(Stage stage) { //IL_0009: 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_012b: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_029b: 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_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) if (gravityStored) { Physics.gravity = baseGravity; gravityStored = false; } active = Personality.None; if (!NetworkServer.active) { return; } double num = rng.NextDouble() * 100.0; if (num > (double)PersonalityChance.Value) { DebugLog($"Stage personality rolled {num:0.0}% vs {PersonalityChance.Value:0.0}% chance - stage stays plain"); return; } Personality personality; do { personality = (Personality)(1 + rng.Next(4)); } while (personality == previous); active = personality; previous = personality; nextMigrationPulse = Time.time + 25f; DebugLog($"Stage personality rolled {num:0.0}% vs {PersonalityChance.Value:0.0}% chance - applying {active} (reward bonus +{RewardBonus.Value:P0})"); if (active == Personality.LowGravity) { if (!gravityStored) { baseGravity = Physics.gravity; gravityStored = true; } Physics.gravity = baseGravity * 0.55f; DebugLog($"Low Gravity applied - gravity set to {Physics.gravity} (base {baseGravity} x0.55)"); } else if (active == Personality.BossTerritory) { List<MasterIndex> list = new List<MasterIndex>(); foreach (CharacterMaster allAiMaster in MasterCatalog.allAiMasters) { if (Object.op_Implicit((Object)(object)allAiMaster) && Object.op_Implicit((Object)(object)allAiMaster.bodyPrefab)) { CharacterBody component = allAiMaster.bodyPrefab.GetComponent<CharacterBody>(); if (Object.op_Implicit((Object)(object)component) && component.isChampion) { list.Add(allAiMaster.masterIndex); } } } if (list.Count > 0) { GameObject masterPrefab = MasterCatalog.GetMasterPrefab(list[rng.Next(list.Count)]); ReadOnlyCollection<PlayerCharacterMasterController> instances = PlayerCharacterMasterController.instances; Vector3 val = Vector3.zero; if (instances != null && instances.Count > 0 && Object.op_Implicit((Object)(object)instances[0]) && Object.op_Implicit((Object)(object)instances[0].master)) { CharacterBody body = instances[0].master.GetBody(); if (Object.op_Implicit((Object)(object)body)) { val = body.corePosition + new Vector3(60f, 10f, 60f); } } CharacterMaster val2 = new MasterSummon { masterPrefab = masterPrefab, position = val, rotation = Quaternion.identity, teamIndexOverride = (TeamIndex)2, ignoreTeamMemberLimit = true }.Perform(); if ((Object)(object)val2 == (Object)null) { PetrichorProtocolPlugin.Log.LogWarning((object)(Name + ": BossTerritory summon failed this stage.")); } else { CharacterBody body2 = val2.GetBody(); DebugLog(string.Format("Boss Territory spawned {0} at {1} ({2} champion candidates available)", Object.op_Implicit((Object)(object)body2) ? Util.GetBestBodyName(((Component)body2).gameObject) : "unknown body", val, list.Count)); } } else { DebugLog("Boss Territory rolled but no champion-class master candidates were found - nothing spawned"); } } if (NotificationsModule.Gate) { string arg = active switch { Personality.LowGravity => "Gravity is light here. Enjoy the airtime.", Personality.TreasureStorm => "Riches everywhere - and the enemies know you're greedy.", Personality.MonsterMigration => "The swarms run deep on this one.", Personality.BossTerritory => "Something enormous already lives here.", _ => "", }; Chat.AddMessage($"<color=#7fd4a8>STAGE PERSONALITY: {active}</color> - {arg} <color=#ffd700>(+{RewardBonus.Value:P0} rewards)</color>"); } } private void SceneDirector_Start(orig_Start orig, SceneDirector self) { if (base.Active && NetworkServer.active && active == Personality.TreasureStorm) { try { int interactableCredit = self.interactableCredit; self.interactableCredit = (int)((float)self.interactableCredit * 1.6f); DebugLog($"Treasure Storm boosted interactable credit {interactableCredit} -> {self.interactableCredit} (x1.6)"); } catch (Exception arg) { PetrichorProtocolPlugin.Log.LogError((object)$"{Name}: credit boost failed: {arg}"); } } orig.Invoke(self); } public override void OnRecalcStats(CharacterBody self) { if (active == Personality.TreasureStorm && ModuleBase.IsHostileEnemy(self)) { self.maxHealth *= 1.15f; self.damage *= 1.15f; } } public override void OnFixedUpdateServer() { if (active != Personality.MonsterMigration || Time.time < nextMigrationPulse) { return; } nextMigrationPulse = Time.time + 25f; try { float num = 40f * (Object.op_Implicit((Object)(object)Run.instance) ? Run.instance.difficultyCoefficient : 1f); int num2 = 0; foreach (CombatDirector instances in CombatDirector.instancesList) { if (Object.op_Implicit((Object)(object)instances)) { instances.monsterCredit += num; num2++; } } DebugLog($"Monster Migration pulse added {num:0.0} credit to {num2} combat director(s) - next pulse in 25s"); } catch (Exception arg) { PetrichorProtocolPlugin.Log.LogError((object)$"{Name}: migration pulse failed: {arg}"); } } public override float RewardMultiplier(DamageReport report) { if (active == Personality.None) { return 1f; } return 1f + RewardBonus.Value; } } internal class WorldEventsModule : ModuleBase { public enum WorldEvent { None, TheHunt, SupplyDrop, MutationWave, TitanArrival, GravityFailure, BloodFrenzy } public class HunterMarker : MonoBehaviour { } private class TitanSizeMarker : MonoBehaviour { public Vector3 baseScale; } public class TitanMarker : MonoBehaviour { } public ConfigEntry<float> MinInterval; public ConfigEntry<float> MaxInterval; public ConfigEntry<float> EventDuration; internal WorldEvent active; private float nextEventTime; private float eventEndTime; private Vector3 baseGravity; private bool gravityStored; private CharacterMaster hunter; private static readonly Random rng = new Random(); public override string Name => "World Events"; public override string StandaloneGuid => null; public override int Stars => 3; public override bool UsesRunStart => true; public override bool UsesStageStart => true; public override bool UsesFixedUpdate => true; public override bool UsesRecalcStats => true; public override bool UsesReward => true; public bool MutationWaveActive => active == WorldEvent.MutationWave; public override void Bind(ConfigFile cfg) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown Enabled = cfg.Bind<bool>("WorldEvents", "Enabled", false, "Every few minutes a temporary world event can trigger: The Hunt, Supply Drop, Mutation Wave, Titan Arrival, Gravity Failure, Blood Frenzy."); MinInterval = cfg.Bind<float>("WorldEvents", "MinimumTimeBetweenEvents", 120f, new ConfigDescription("Shortest wait, in seconds, before another world event can fire. The actual gap is random between this and the maximum.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 600f), Array.Empty<object>())); MaxInterval = cfg.Bind<float>("WorldEvents", "MaximumTimeBetweenEvents", 300f, new ConfigDescription("Longest wait, in seconds, before a world event must fire. The actual gap is random between the minimum and this.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 900f), Array.Empty<object>())); EventDuration = cfg.Bind<float>("WorldEvents", "EventDuration", 60f, new ConfigDescription("How long a timed world event stays active, in seconds (The Hunt runs until the hunter dies; instant events like Supply Drop ignore this).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(15f, 180f), Array.Empty<object>())); HealthComponent.TakeDamage += new hook_TakeDamage(HealthComponent_TakeDamage); } public override void Unhook() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown HealthComponent.TakeDamage -= new hook_TakeDamage(HealthComponent_TakeDamage); } public override void OnRunStart() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (gravityStored) { Physics.gravity = baseGravity; gravityStored = false; } active = WorldEvent.None; ScheduleNext(); } public override void OnStageStart(Stage stage) { EndEvent(silent: true); ScheduleNext(); } private void ScheduleNext() { float num = Mathf.Min(MinInterval.Value, MaxInterval.Value); float num2 = Mathf.Max(MinInterval.Value, MaxInterval.Value); float num3 = num + (float)rng.NextDouble() * (num2 - num); nextEventTime = Time.time + num3; DebugLog($"Next world event scheduled in {num3:0.0}s (range {num:0.0}-{num2:0.0}s)"); } public override void OnFixedUpdateServer() { if (active != WorldEvent.None) { bool flag = active == WorldEvent.TheHunt && ((Object)(object)hunter == (Object)null || !hunter.hasBody); if (Time.time >= eventEndTime || flag) { EndEvent(silent: false); } } else if (!(Time.time < nextEventTime)) { WorldEvent worldEvent = (WorldEvent)(1 + rng.Next(6)); DebugLog($"World event timer elapsed - picked {worldEvent}"); StartEvent(worldEvent); } } private void StartEvent(WorldEvent e) { //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) active = e; eventEndTime = Time.time + EventDuration.Value; string arg = ""; try { switch (e) { case WorldEvent.TheHunt: hunter = SummonHunter(); if ((Object)(object)hunter == (Object)null) { DebugLog("The Hunt failed to find a champion candidate or valid target - skipped, rescheduling"); active = WorldEvent.None; ScheduleNext(); return; } eventEndTime = Time.time + 600f; arg = "A hunter has your scent. Kill it for a rich bounty."; DebugLog("The Hunt started - hunter " + Util.GetBestBodyName(((Component)hunter.GetBody()).gameObject) + " summoned, +Fire elite equipment, +30 BoostHp, +15 BoostDamage"); break; case WorldEvent.SupplyDrop: if (!SpawnSupplyDrop()) { DebugLog("Supply Drop rolled but spawn failed (no chest spawn card or no valid director spawn) - skipped, rescheduling"); active = WorldEvent.None; ScheduleNext(); return; } eventEndTime = Time.time + 1f; arg = "Extra supplies have appeared somewhere on the map."; DebugLog("Supply Drop spawned a chest near the lead player"); break; case WorldEvent.MutationWave: arg = "Mutation rates are spiking. Everything spawning now is wrong."; DebugLog($"Mutation Wave started - active for {EventDuration.Value:0.0}s (EnemyMutationsModule reads MutationWaveActive)"); break; case WorldEvent.TitanArrival: if (!TitanizeRandomEnemy()) { DebugLog("Titan Arrival rolled but no eligible non-boss enemy was alive on the stage - skipped, rescheduling"); active = WorldEvent.None; ScheduleNext(); return; } eventEndTime = Time.time + 1f; arg = "One of them just became enormous."; DebugLog("Titan Arrival applied - one enemy scaled to 2.6x model size, x3.5 max health, x1.6 damage, x0.8 move speed"); break; case WorldEvent.GravityFailure: if (!gravityStored) { baseGravity = Physics.gravity; gravityStored = true; } Physics.gravity = baseGravity * 0.45f; arg = "Gravity is failing. Temporarily. Probably."; DebugLog($"Gravity Failure started - gravity set to {Physics.gravity} (base {baseGravity} x0.45), lasts {EventDuration.Value:0.0}s"); break; case WorldEvent.BloodFrenzy: MarkAllStatsDirty(); arg = "Everything attacks faster and dies easier. Including you."; DebugLog($"Blood Frenzy started - x1.5 attack speed for everyone, x1.35 damage taken via TakeDamage hook, lasts {EventDuration.Value:0.0}s"); break; } if (NotificationsModule.Gate) { Chat.AddMessage($"<color=#ff5c5c>WORLD EVENT: {e}</color> - {arg}"); } } catch (Exception arg2) { PetrichorProtocolPlugin.Log.LogError((object)$"{Name}: event start failed: {arg2}"); active = WorldEvent.None; ScheduleNext(); } } private void EndEvent(bool silent) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (active == WorldEvent.None) { ScheduleNext(); return; } WorldEvent worldEvent = active; if (worldEvent == WorldEvent.GravityFailure && gravityStored) { Physics.gravity = baseGravity; } if (worldEvent == WorldEvent.TheHunt && (Object)(object)hunter != (Object)null && hunter.hasBody) { hunter.TrueKill(); } hunter = null; active = WorldEvent.None; if (worldEvent == WorldEvent.BloodFrenzy) { MarkAllStatsDirty(); } DebugLog($"World event {worldEvent} ended (silent={silent})"); if (!silent && NotificationsModule.Gate && worldEvent != WorldEvent.SupplyDrop && worldEvent != WorldEvent.TitanArrival) { Chat.AddMessage($"<color=#8a8a8a>World event over: {worldEvent}.</color>"); } ScheduleNext(); } private static void MarkAllStatsDirty() { foreach (CharacterBody readOnlyInstances in CharacterBody.readOnlyInstancesList) { if (Object.op_Implicit((Object)(object)readOnlyInstances)) { readOnlyInstances.MarkAllStatsDirty(); } } } private CharacterMaster SummonHunter() { //IL_0057: 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) //IL_00cc: 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_00ed: 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_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: 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_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: 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_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) List<MasterIndex> list = new List<MasterIndex>(); foreach (CharacterMaster allAiMaster in MasterCatalog.allAiMasters) { if (Object.op_Implicit((Object)(object)allAiMaster) && Object.op_Implicit((Object)(object)allAiMaster.bodyPrefab)) { CharacterBody component = allAiMaster.bodyPrefab.GetComponent<CharacterBody>(); if (Object.op_Implicit((Object)(object)component) && component.isChampion) { list.Add(allAiMaster.masterIndex); } } } if (list.Count == 0) { return null; } ReadOnlyCollection<PlayerCharacterMasterController> instances = PlayerCharacterMasterController.instances; if (instances == null || instances.Count == 0) { return null; } CharacterBody val = (Object.op_Implicit((Object)(object)instances[0].master) ? instances[0].master.GetBody() : null); if (!Object.op_Implicit((Object)(object)val)) { return null; } CharacterMaster val2 = new MasterSummon { masterPrefab = MasterCatalog.GetMasterPrefab(list[rng.Next(list.Count)]), position = val.corePosition + new Vector3(40f, 8f, 40f), rotation = Quaternion.identity, teamIndexOverride = (TeamIndex)2, ignoreTeamMemberLimit = true }.Perform(); if ((Object)(object)val2 == (Object)null) { return null; } if (Object.op_Implicit((Object)(object)val2.inventory)) { EliteDef fire = Elites.Fire; if (Object.op_Implicit((Object)(object)fire) && Object.op_Implicit((Object)(object)fire.eliteEquipmentDef)) { val2.inventory.SetEquipmentIndex(fire.eliteEquipmentDef.equipmentIndex, false); } val2.inventory.GiveItemPermanent(Items.BoostHp, 30); val2.inventory.GiveItemPermanent(Items.BoostDamage, 15); } ((Component)val2).gameObject.AddComponent<HunterMarker>(); return val2; } private bool SpawnSupplyDrop() { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown InteractableSpawnCard val = LegacyResourcesAPI.Load<InteractableSpawnCard>("SpawnCards/InteractableSpawnCard/iscChest1"); if (!Object.op_Implicit((Object)(object)val)) { PetrichorProtocolPlugin.Log.LogWarning((object)(Name + ": chest spawn card not found in this game version - skipping Supply Drop.")); return false; } ReadOnlyCollection<PlayerCharacterMasterController> instances = PlayerCharacterMasterController.instances; if (instances == null || instances.Count == 0) { return false; } CharacterBody val2 = (Object.op_Implicit((Object)(object)instances[0].master) ? instances[0].master.GetBody() : null); if (!Object.op_Implicit((Object)(object)val2)) { return false; } DirectorPlacementRule val3 = new DirectorPlacementRule { placementMode = (PlacementMode)1, position = val2.corePosition, minDistance = 15f, maxDistance = 60f }; DirectorSpawnRequest val4 = new DirectorSpawnRequest((SpawnCard)(object)val, val3, new Xoroshiro128Plus((ulong)rng.Next())); return (Object)(object)(Object.op_Implicit((Object)(object)DirectorCore.instance) ? DirectorCore.instance.TrySpawnObject(val4) : null) != (Object)null; } private bool TitanizeRandomEnemy() { //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) List<CharacterBody> list = new List<CharacterBody>(); foreach (CharacterBody readOnlyInstances in CharacterBody.readOnlyInstancesList) { if (Object.op_Implicit((Object)(object)readOnlyInstances) && ModuleBase.IsHostileEnemy(readOnlyInstances) && !readOnlyInstances.isBoss && Object.op_Implicit((Object)(object)readOnlyInstances.healthComponent) && readOnlyInstances.healthComponent.alive) { list.Add(readOnlyInstances); } } if (list.Count == 0) { return false; } CharacterBody val = list[rng.Next(list.Count)]; DebugLog($"Titan Arrival chose {Util.GetBestBodyName(((Component)val).gameObject)} out of {list.Count} eligible enemies"); ModelLocator modelLocator = val.modelLocator; if (Object.op_Implicit((Object)(object)modelLocator) && Object.op_Implicit((Object)(object)modelLocator.modelTransform)) { GameObject gameObject = ((Component)modelLocator.modelTransform).gameObject; TitanSizeMarker titanSizeMarker = gameObject.GetComponent<TitanSizeMarker>(); if (!Object.op_Implicit((Object)(object)titanSizeMarker)) { titanSizeMarker = gameObject.AddComponent<TitanSizeMarker>(); titanSizeMarker.baseScale = gameObject.transform.localScale; } gameObject.transform.localScale = titanSizeMarker.baseScale * 2.6f; } ((Component)val).gameObject.AddComponent<TitanMarker>(); val.RecalculateStats(); if (Object.op_Implicit((Object)(object)val.healthComponent)) { val.healthComponent.HealFraction(1f, default(ProcChainMask)); } return true; } public override void OnRecalcStats(CharacterBody self) { if (Object.op_Implicit((Object)(object)((Component)self).GetComponent<TitanMarker>())) { self.maxHealth *= 3.5f; self.damage *= 1.6f; self.moveSpeed *= 0.8f; } if (active == WorldEvent.BloodFrenzy) { self.attackSpeed *= 1.5f; } } private void HealthComponent_TakeDamage(orig_TakeDamage orig, HealthComponent self, DamageInfo damageInfo) { if (base.Active && active == WorldEvent.BloodFrenzy && damageInfo != null) { damageInfo.damage *= 1.35f; } orig.Invoke(self, damageInfo); } public override float RewardMultiplier(DamageReport report) { if ((Object)(object)report?.victimBody == (Object)null) { return 1f; } if (Object.op_Implicit((Object)(object)((Component)report.victimBody).GetComponent<HunterMarker>())) { return 6f; } if (Object.op_Implicit((Object)(object)((