using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HappyPeak.Events; using HappyPeak.Patches; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using UnityEngine; using Zorro.Core; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("HappyPeak")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.12.0.0")] [assembly: AssemblyInformationalVersion("0.12.0+0033eee694bc333f384035062bf501095ca400ff")] [assembly: AssemblyProduct("HappyPeak")] [assembly: AssemblyTitle("HappyPeak")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.12.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace HappyPeak { internal static class CustomLoot { private static readonly HashSet BuiltInExcludedItemNames = new HashSet { "Cheat Compass", "ScoutCookies_Vanilla", "ClimbingChalk", "Skull", "Warp Compass", "AncientIdol", "Passport" }; private static readonly HashSet ExcludedItemPrefixes = new HashSet { "C_", "Guidebook" }; private static readonly HashSet ExcludedItemSuffixes = new HashSet { "_Prop", "_Prop Variant", "_TEMP", "Spawn", "Placed" }; private static HashSet GetEffectiveBlacklist() { HashSet hashSet = new HashSet(BuiltInExcludedItemNames); string value = ModConfig.AdditionalBlacklist.Value; if (string.IsNullOrWhiteSpace(value)) { return hashSet; } string[] array = value.Split(','); foreach (string text in array) { string text2 = text.Trim(); if (text2.Length > 0) { hashSet.Add(text2); } } return hashSet; } public static Dictionary GetItemsDatabaseFiltered() { ItemDatabase instance = SingletonAsset.Instance; HashSet blacklist = GetEffectiveBlacklist(); return instance.itemLookup.Where(delegate(KeyValuePair kv) { Item value = kv.Value; if ((Object)(object)value == (Object)null) { return false; } string name = ((Object)value).name; if (blacklist.Contains(name)) { return false; } if (ExcludedItemPrefixes.Any((string p) => name.StartsWith(p))) { return false; } return (!ExcludedItemSuffixes.Any((string s) => name.EndsWith(s))) ? true : false; }).ToDictionary((KeyValuePair kv) => kv.Key, (KeyValuePair kv) => kv.Value); } public static ushort GetRandomId() { Dictionary.KeyCollection keys = GetItemsDatabaseFiltered().Keys; return keys.ElementAt(Random.Range(0, keys.Count)); } } internal static class ModConfig { public static ConfigEntry EnableItemRandomization { get; private set; } public static ConfigEntry AdditionalBlacklist { get; private set; } public static ConfigEntry EnableLootScaling { get; private set; } public static ConfigEntry LootMultiplier { get; private set; } public static ConfigEntry EnableRandomTornadoes { get; private set; } public static ConfigEntry TornadoIntervalMin { get; private set; } public static ConfigEntry TornadoIntervalMax { get; private set; } public static ConfigEntry TornadoesPerWave { get; private set; } public static ConfigEntry TornadoLifetime { get; private set; } public static ConfigEntry EnableTornadoAtKiln { get; private set; } public static ConfigEntry EnableRandomBananaPeels { get; private set; } public static ConfigEntry BananaPeelIntervalMin { get; private set; } public static ConfigEntry BananaPeelIntervalMax { get; private set; } public static ConfigEntry BananaPeelsPerPlayerMin { get; private set; } public static ConfigEntry BananaPeelsPerPlayerMax { get; private set; } public static ConfigEntry BananaPeelLifetime { get; private set; } public static ConfigEntry BananaPeelDropHeight { get; private set; } public static ConfigEntry EnableRandomTumbleweeds { get; private set; } public static ConfigEntry TumbleweedIntervalMin { get; private set; } public static ConfigEntry TumbleweedIntervalMax { get; private set; } public static ConfigEntry TumbleweedsPerPlayerMin { get; private set; } public static ConfigEntry TumbleweedsPerPlayerMax { get; private set; } public static ConfigEntry TumbleweedLifetime { get; private set; } public static ConfigEntry TumbleweedDropHeight { get; private set; } public static ConfigEntry EnableRandomDynamite { get; private set; } public static ConfigEntry DynamiteIntervalMin { get; private set; } public static ConfigEntry DynamiteIntervalMax { get; private set; } public static ConfigEntry DynamiteCountPerWave { get; private set; } public static ConfigEntry DynamiteSidewaysJitter { get; private set; } public static ConfigEntry DynamiteForwardDistance { get; private set; } public static ConfigEntry DynamiteDropHeight { get; private set; } public static ConfigEntry DynamiteLifetime { get; private set; } public static ConfigEntry LightDynamiteOnSpawn { get; private set; } public static ConfigEntry EnableMarkerSpawn { get; private set; } public static ConfigEntry EnableRandomEruptions { get; private set; } public static ConfigEntry EruptionIntervalMin { get; private set; } public static ConfigEntry EruptionIntervalMax { get; private set; } public static ConfigEntry EruptionOffsetMin { get; private set; } public static ConfigEntry EruptionOffsetMax { get; private set; } public static ConfigEntry CampfireSafeRadius { get; private set; } public static ConfigEntry EnableCampfireInvincibility { get; private set; } public static ConfigEntry EnableScoutmasterTrap { get; private set; } public static ConfigEntry ScoutmasterTrapChance { get; private set; } public static ConfigEntry ScoutmasterChaseTime { get; private set; } public static ConfigEntry EnableZombieTrap { get; private set; } public static ConfigEntry ZombieTrapChance { get; private set; } public static ConfigEntry ZombieCountMin { get; private set; } public static ConfigEntry ZombieCountMax { get; private set; } public static ConfigEntry ZombieLifetime { get; private set; } private static string T(string en, string zh) { return en + "\n" + zh; } public static void Initialize(ConfigFile config) { EnableItemRandomization = config.Bind("Randomizer", "EnableItemRandomization", true, T("[ON/OFF] Enable or disable chest randomization. When true, every chest opened returns a random item from the filtered ItemDatabase instead of the loot table's normal pick.", "【开关】是否开启箱子随机化。开启后,每次开箱返回从过滤后的 ItemDatabase 随机抽取的物品,而不是游戏 loot table 原本给出的物品。")); AdditionalBlacklist = config.Bind("Randomizer", "AdditionalBlacklist", "", T("Comma-separated additional item names to exclude on top of the built-in blacklist. Example: Dynamite, Scorpion, CactusBall", "用逗号分隔的额外物品黑名单(在内置黑名单基础上追加)。例:Dynamite, Scorpion, CactusBall")); EnableLootScaling = config.Bind("LootScaling", "EnableLootScaling", true, T("[ON/OFF] Enable or disable the chest loot multiplier. When true, multiplies the number of items spawned by chests by `LootMultiplier`.", "【开关】是否开启箱子物资倍乘。开启后,把箱子产出的物品数量乘以 `LootMultiplier` 的值。")); LootMultiplier = config.Bind("LootScaling", "LootMultiplier", 3f, T("Multiplier applied to chest loot quantity. 1 = vanilla, 2 = double, 3 = triple, etc.", "箱子物品数量倍率。1 = 原版数量,2 = 双倍,3 = 三倍,依此类推。")); EnableRandomTornadoes = config.Bind("Tornado", "EnableRandomTornadoes", true, T("[ON/OFF] Enable or disable the random tornado event. When true, periodically spawns tornadoes near random players on every biome (vanilla PEAK only spawns them on Mesa). Only the host needs to install this mod; all players see the effects.", "【开关】是否开启随机龙卷风事件。开启后,周期性在随机玩家附近生成龙卷风,所有 biome 都生效(原版 PEAK 只有 Mesa 才有)。只需房主安装本 mod;所有玩家都会看到效果。")); TornadoIntervalMin = config.Bind("Tornado", "TornadoIntervalMin", 90, T("Minimum seconds between tornado waves.", "龙卷风之间的最小间隔秒数。")); TornadoIntervalMax = config.Bind("Tornado", "TornadoIntervalMax", 120, T("Maximum seconds between tornado waves.", "龙卷风之间的最大间隔秒数。")); TornadoesPerWave = config.Bind("Tornado", "TornadoesPerWave", 1, T("How many tornadoes spawn per wave (each targeted at a random player).", "每波生成几个龙卷风(每个针对一个随机玩家)。")); TornadoLifetime = config.Bind("Tornado", "TornadoLifetime", 30f, T("Seconds before each tornado is automatically destroyed.", "每个龙卷风被自动销毁前持续的秒数。")); EnableTornadoAtKiln = config.Bind("Tornado", "EnableTornadoAtKiln", false, T("[ON/OFF] Enable or disable tornadoes at the Kiln (the very top biome). Off by default because they tend to wreck endgame runs.", "【开关】是否允许在 Kiln(最顶部 biome)生成龙卷风。默认关闭,因为容易毁了通关。")); EnableRandomBananaPeels = config.Bind("BananaPeel", "EnableRandomBananaPeels", true, T("[ON/OFF] Enable or disable the random banana peel event. When true, periodically scatters banana peels around every human player. Only the host needs to install this mod; all players see the effects.", "【开关】是否开启随机香蕉皮事件。开启后,周期性在每个真人玩家身边撒香蕉皮。只需房主安装本 mod;所有玩家都会看到效果。")); BananaPeelIntervalMin = config.Bind("BananaPeel", "BananaPeelIntervalMin", 60, T("Minimum seconds between banana peel waves.", "香蕉皮之间的最小间隔秒数。")); BananaPeelIntervalMax = config.Bind("BananaPeel", "BananaPeelIntervalMax", 90, T("Maximum seconds between banana peel waves.", "香蕉皮之间的最大间隔秒数。")); BananaPeelsPerPlayerMin = config.Bind("BananaPeel", "BananaPeelsPerPlayerMin", 1, T("Minimum number of peels spawned around each player per wave.", "每波每个玩家身边生成的香蕉皮最少数量。")); BananaPeelsPerPlayerMax = config.Bind("BananaPeel", "BananaPeelsPerPlayerMax", 5, T("Maximum number of peels spawned around each player per wave.", "每波每个玩家身边生成的香蕉皮最多数量。")); BananaPeelLifetime = config.Bind("BananaPeel", "BananaPeelLifetime", 30f, T("Seconds before each spawned peel is automatically removed (to keep the map from getting buried in peels).", "每个香蕉皮被自动销毁前持续的秒数(防止地图被皮淹没)。")); BananaPeelDropHeight = config.Bind("BananaPeel", "BananaPeelDropHeight", 15f, T("Height in meters above each player to spawn peels at (creates a falling 'banana rain' effect via gravity). 0 would drop them at the player's body.", "香蕉皮在玩家上方多少米生成(重力下落形成香蕉雨效果)。0 = 直接在玩家身上生成。")); EnableRandomTumbleweeds = config.Bind("Tumbleweed", "EnableRandomTumbleweeds", true, T("[ON/OFF] Enable or disable the random tumbleweed rain. When true, periodically rains tumbleweeds from above each player. Only the host needs to install this mod; all players see the effects.", "【开关】是否开启随机风滚草雨。开启后,周期性在每个玩家头顶上方下落风滚草。只需房主安装本 mod;所有玩家都会看到效果。")); TumbleweedIntervalMin = config.Bind("Tumbleweed", "TumbleweedIntervalMin", 60, T("Minimum seconds between tumbleweed waves.", "风滚草之间的最小间隔秒数。")); TumbleweedIntervalMax = config.Bind("Tumbleweed", "TumbleweedIntervalMax", 90, T("Maximum seconds between tumbleweed waves.", "风滚草之间的最大间隔秒数。")); TumbleweedsPerPlayerMin = config.Bind("Tumbleweed", "TumbleweedsPerPlayerMin", 1, T("Minimum number of tumbleweeds spawned around each player per wave.", "每波每个玩家生成的风滚草最少数量。")); TumbleweedsPerPlayerMax = config.Bind("Tumbleweed", "TumbleweedsPerPlayerMax", 3, T("Maximum number of tumbleweeds spawned around each player per wave.", "每波每个玩家生成的风滚草最多数量。")); TumbleweedLifetime = config.Bind("Tumbleweed", "TumbleweedLifetime", 15f, T("Seconds before each spawned tumbleweed is automatically removed.", "每个风滚草被自动销毁前持续的秒数。")); TumbleweedDropHeight = config.Bind("Tumbleweed", "TumbleweedDropHeight", 15f, T("Height in meters above each player to spawn tumbleweeds at (gravity drops them down).", "风滚草在玩家上方多少米生成(重力会把它们拉下来)。")); EnableRandomDynamite = config.Bind("Dynamite", "EnableRandomDynamite", true, T("[ON/OFF] Enable or disable the random dynamite event. When true, periodically drops dynamite in front of each player's head (unlit by default — the player can pick it up). Only the host needs to install this mod; all players see the effects.", "【开关】是否开启随机炸药事件。开启后,周期性在每个玩家眼前掉一根炸药(默认未点燃,玩家可以捡起来用)。只需房主安装本 mod;所有玩家都会看到效果。")); DynamiteIntervalMin = config.Bind("Dynamite", "DynamiteIntervalMin", 60, T("Minimum seconds between dynamite waves.", "炸药之间的最小间隔秒数。")); DynamiteIntervalMax = config.Bind("Dynamite", "DynamiteIntervalMax", 90, T("Maximum seconds between dynamite waves.", "炸药之间的最大间隔秒数。")); DynamiteCountPerWave = config.Bind("Dynamite", "DynamiteCountPerWave", 1, T("How many sticks of dynamite spawn per wave (each near a randomly picked player).", "每波生成几根炸药(每根落在一个随机玩家附近)。")); DynamiteForwardDistance = config.Bind("Dynamite", "DynamiteForwardDistance", 1f, T("How far in front of the player's head to spawn the dynamite (meters). Default 1m = right in front of their face.", "炸弹在玩家头部前方多少米生成。默认 1 米 = 正前方一掌远。")); DynamiteDropHeight = config.Bind("Dynamite", "DynamiteDropHeight", 0f, T("Extra height above the player's head where the spawn point sits (meters). 0 = level with the head. Larger values give more falling distance.", "炸弹在玩家头部上方多少米生成。0 = 与头同高。值越大下落距离越长。")); DynamiteSidewaysJitter = config.Bind("Dynamite", "DynamiteSidewaysJitter", 0.5f, T("Random sideways scatter (meters) added to the spawn point so dynamite isn't always perfectly centered on the player's view. 0 = always dead-center.", "炸弹生成点的左右随机散布(米),避免每次都正中视线中央。0 = 永远正中。")); DynamiteLifetime = config.Bind("Dynamite", "DynamiteLifetime", 30f, T("Hard cleanup timer in case the fuse fizzles or the dynamite gets stuck somewhere. Normally it explodes within ~5s.", "兜底销毁计时(秒)。正常引信约 5 秒就炸,这里是防止意外没炸的清理。")); LightDynamiteOnSpawn = config.Bind("Dynamite", "LightDynamiteOnSpawn", false, T("If true, the dynamite is lit immediately and will explode after its fuse. If false, drops as an unlit stick that players can pick up.", "true = 生成时点燃,到时间会爆炸;false = 掉一根未点燃的炸药,玩家可以捡起来用。")); EnableMarkerSpawn = config.Bind("MarkerSpawn", "EnableMarkerSpawn", true, T("[ON/OFF] Enable or disable the marker pickaxe drop. When true, dropping a middle-click marker (PEAK's native ping) spawns a pickaxe at that location, attached to the nearest surface when possible. Only the host needs to install this mod; all players see the effects.", "【开关】是否开启标记掉锄头事件。开启后,任意玩家鼠标中键标记(PEAK 自带 ping)时,在标记位置生成一个锄头,能找到表面就直接附着上去。只需房主安装本 mod;所有玩家都会看到效果。")); EnableRandomEruptions = config.Bind("Eruption", "EnableRandomEruptions", true, T("[ON/OFF] Enable or disable the random fire eruption event. When true, periodically erupts fire jets from the ground near random players (the game's native 'floor erupts in flames' effect). Requires the game's EruptionSpawner to be present in the scene; silently skipped on biomes/scenes where it isn't. Only the host needs to install this mod; all players see the effects.", "【开关】是否开启随机喷火事件。开启后,周期性在随机玩家附近触发地面喷火柱(PEAK 自带的'地面喷火'效果)。需要场景里有 EruptionSpawner 组件,没有则静默跳过。只需房主安装本 mod;所有玩家都会看到效果。")); EruptionIntervalMin = config.Bind("Eruption", "EruptionIntervalMin", 60, T("Minimum seconds between fire eruption waves.", "喷火之间的最小间隔秒数。")); EruptionIntervalMax = config.Bind("Eruption", "EruptionIntervalMax", 90, T("Maximum seconds between fire eruption waves.", "喷火之间的最大间隔秒数。")); EruptionOffsetMin = config.Bind("Eruption", "EruptionOffsetMin", 0f, T("Minimum distance in meters from the chosen player. 0 means flames erupt directly under their feet.", "距目标玩家的最小距离(米)。0 = 直接踩脚下喷火。")); EruptionOffsetMax = config.Bind("Eruption", "EruptionOffsetMax", 5f, T("Maximum distance in meters from the chosen player.", "距目标玩家的最大距离(米)。")); EnableScoutmasterTrap = config.Bind("ChestTraps", "EnableScoutmasterTrap", true, T("[ON/OFF] Enable or disable the Scoutmaster chest trap. When triggered, the Scoutmaster teleports to the chest and hunts the player who opened it. Only the host needs to install this mod; all players see the effects.", "【开关】是否开启童军队长陷阱。触发时童军队长会被传送到箱子位置并追杀开箱者。只需房主安装本 mod;所有玩家都会看到效果。")); ScoutmasterTrapChance = config.Bind("ChestTraps", "ScoutmasterTrapChance", 5, T("Percent chance (0-100) the Scoutmaster trap fires when a chest is opened. Default 5% because the Scoutmaster can wreck a run.", "开箱时触发童军队长陷阱的百分比几率(0-100)。默认 5%,因为童军队长容易毁了通关。")); ScoutmasterChaseTime = config.Bind("ChestTraps", "ScoutmasterChaseTime", 30f, T("Seconds the Scoutmaster is forced to hunt the chest opener after teleporting.", "童军队长传送过来后被强制追杀开箱者的秒数。")); EnableZombieTrap = config.Bind("ChestTraps", "EnableZombieTrap", true, T("[ON/OFF] Enable or disable the mushroom zombie chest trap. When triggered, one or more zombies spawn at the chest and target the opener. Only the host needs to install this mod; all players see the effects.", "【开关】是否开启蘑菇僵尸陷阱。触发时会在箱子位置生成 1 个或多个僵尸并锁定开箱者。只需房主安装本 mod;所有玩家都会看到效果。")); ZombieTrapChance = config.Bind("ChestTraps", "ZombieTrapChance", 10, T("Percent chance (0-100) the zombie trap fires when a chest is opened.", "开箱时触发僵尸陷阱的百分比几率(0-100)。")); ZombieCountMin = config.Bind("ChestTraps", "ZombieCountMin", 1, T("Minimum number of zombies spawned when the trap fires.", "陷阱触发时生成僵尸的最小数量。")); ZombieCountMax = config.Bind("ChestTraps", "ZombieCountMax", 2, T("Maximum number of zombies spawned when the trap fires.", "陷阱触发时生成僵尸的最大数量。")); ZombieLifetime = config.Bind("ChestTraps", "ZombieLifetime", 60f, T("Seconds before each spawned zombie is automatically removed (so dead-end runs don't pile them up).", "每个生成的僵尸被自动销毁前持续的秒数(避免长时间堆积)。")); CampfireSafeRadius = config.Bind("SafeZones", "CampfireSafeRadius", 30f, T("Radius in meters around any campfire that counts as a safe zone. Tornadoes, fire eruptions and tumbleweeds skip players standing inside this bubble. Set to 0 to disable safe zones.", "篝火周围多少米算作安全区。龙卷风、喷火、风滚草事件会跳过站在这个范围内的玩家。设为 0 = 取消安全区。")); EnableCampfireInvincibility = config.Bind("SafeZones", "EnableCampfireInvincibility", true, T("[ON/OFF] When true, players standing within `CampfireSafeRadius` of a campfire take no status damage at all (no injury, cold, heat, hunger, poison, etc.). Healing still works.", "【开关】开启后,篝火 CampfireSafeRadius 范围内的玩家完全免疫所有 status 伤害(伤害/寒冷/酷热/饥饿/中毒等都不会增加)。治疗仍然有效。")); } } [BepInPlugin("HappyPeak", "HappyPeak", "0.12.0")] public class Plugin : BaseUnityPlugin { [CompilerGenerated] private sealed class d__14 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public float seconds; public GameObject go; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__14(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(seconds); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)go == (Object)null) { return false; } if (!PhotonNetwork.IsMasterClient) { return false; } try { PhotonNetwork.Destroy(go); } catch (Exception ex) { Log.LogInfo((object)("DelayedNetworkDestroy skipped: " + ex.Message)); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const string Id = "HappyPeak"; internal static ManualLogSource Log { get; private set; } internal static Plugin Instance { get; private set; } public static string Name => "HappyPeak"; public static string Version => "0.12.0"; private void Awake() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; ModConfig.Initialize(((BaseUnityPlugin)this).Config); Harmony val = new Harmony("HappyPeak"); val.PatchAll(typeof(SpawnerPatch)); val.PatchAll(typeof(LuggageTrapPatch)); val.PatchAll(typeof(CampfireInvincibilityPatch)); val.PatchAll(typeof(PointPingPatch)); val.PatchAll(typeof(PickaxePermanencePatch)); Log.LogInfo((object)(Name + " " + Version + " loaded.")); } private void Update() { TornadoEventRunner.Tick(); BananaPeelEventRunner.Tick(); TumbleweedEventRunner.Tick(); DynamiteEventRunner.Tick(); EruptionEventRunner.Tick(); } internal static bool IsInGameRun() { MapHandler instance = Singleton.Instance; if ((Object)(object)instance == (Object)null) { return false; } return instance.currentSegment >= 0; } internal static bool IsAtKiln() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 MapHandler instance = Singleton.Instance; if ((Object)(object)instance == (Object)null) { return false; } return (int)instance.GetCurrentSegment() == 4; } internal static bool IsNearCampfire(Vector3 pos, float radius) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (radius <= 0f) { return false; } Campfire[] array = Object.FindObjectsByType((FindObjectsInactive)0, (FindObjectsSortMode)0); if (array == null) { return false; } float num = radius * radius; Campfire[] array2 = array; foreach (Campfire val in array2) { if (!((Object)(object)val == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - pos; if (((Vector3)(ref val2)).sqrMagnitude <= num) { return true; } } } return false; } internal static void DelayedNetworkDestroy(GameObject go, float seconds) { if (!((Object)(object)go == (Object)null) && !(seconds <= 0f)) { ((MonoBehaviour)Instance).StartCoroutine(DelayedNetworkDestroyCoroutine(go, seconds)); } } [IteratorStateMachine(typeof(d__14))] private static IEnumerator DelayedNetworkDestroyCoroutine(GameObject go, float seconds) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__14(0) { go = go, seconds = seconds }; } } } namespace HappyPeak.Patches { [HarmonyPatch] internal static class CampfireInvincibilityPatch { [HarmonyPatch(typeof(CharacterAfflictions), "AddStatus")] [HarmonyPrefix] private static bool AddStatus_Prefix(CharacterAfflictions __instance, STATUSTYPE statusType, float amount, ref bool __result) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.EnableCampfireInvincibility.Value) { return true; } if (amount <= 0f) { return true; } Character character = __instance.character; if ((Object)(object)character == (Object)null || character.isBot) { return true; } if (!Plugin.IsNearCampfire(character.Center, ModConfig.CampfireSafeRadius.Value)) { return true; } __result = false; return false; } } [HarmonyPatch] internal static class LuggageTrapPatch { [CompilerGenerated] private sealed class d__8 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Vector3 chestPos; public Character target; private Vector3 5__2; private int 5__3; private Scoutmaster 5__4; private PhotonView 5__5; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__8(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__4 = null; 5__5 = null; <>1__state = -2; } private bool MoveNext() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = chestPos + Vector3.up * 2f; 5__3 = 1; break; case 1: { <>1__state = -1; PhotonView photonView = ((MonoBehaviourPun)target).photonView; if ((Object)(object)photonView != (Object)null) { try { 5__5.RPC("RPCA_SetCurrentTarget", (RpcTarget)0, new object[2] { photonView.ViewID, ModConfig.ScoutmasterChaseTime.Value }); Plugin.Log.LogInfo((object)$"Scoutmaster trap fired: hunting {target.characterName} for {ModConfig.ScoutmasterChaseTime.Value}s (via RPCA_SetCurrentTarget)"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("RPCA_SetCurrentTarget failed (" + ex.Message + "); falling back to local SetCurrentTarget")); 5__4.SetCurrentTarget(target, ModConfig.ScoutmasterChaseTime.Value); } } return false; } case 2: <>1__state = -1; 5__4 = null; 5__3++; break; } if (5__3 <= 4) { if (Scoutmaster.GetPrimaryScoutmaster(ref 5__4) && (Object)(object)5__4 != (Object)null && (Object)(object)5__4.character != (Object)null) { 5__5 = ((Component)5__4).GetComponent(); if ((Object)(object)5__5 != (Object)null) { 5__5.RPC("WarpPlayerRPC", (RpcTarget)0, new object[2] { 5__2, false }); <>2__current = null; <>1__state = 1; return true; } 5__5 = null; } <>2__current = (object)new WaitForSeconds(0.25f); <>1__state = 2; return true; } Plugin.Log.LogWarning((object)"Scoutmaster trap aborted: Scoutmaster not found in scene after retries."); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const float OpenerSearchRadius = 5f; private const float ChestJitterHorizontal = 2.5f; private const float ChestJitterVertical = 0.5f; private static readonly string[] ZombiePrefabCandidates = new string[3] { "MushroomZombie", "0_Items/MushroomZombie", "Zombie/MushroomZombie" }; [HarmonyPatch(typeof(Luggage), "OpenLuggageRPC")] [HarmonyPostfix] private static void OpenLuggageRPC_Postfix(Luggage __instance, bool spawnItems) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (PhotonNetwork.IsMasterClient && spawnItems && !((Object)(object)__instance == (Object)null)) { Vector3 position = ((Component)__instance).transform.position; Character val = FindClosestOpener(position); if (!((Object)(object)val == (Object)null)) { TryScoutmasterTrap(position, val); TryZombieTrap(position, val); } } } private static Character? FindClosestOpener(Vector3 chestPos) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) Character result = null; float num = 5f; foreach (Character allCharacter in Character.AllCharacters) { if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.isBot) { float num2 = Vector3.Distance(allCharacter.Center, chestPos); if (num2 < num) { num = num2; result = allCharacter; } } } return result; } private static bool RollChance(int chancePercent) { if (chancePercent <= 0) { return false; } if (chancePercent >= 100) { return true; } return Random.Range(1, 101) <= chancePercent; } private static void TryScoutmasterTrap(Vector3 chestPos, Character opener) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (ModConfig.EnableScoutmasterTrap.Value && RollChance(ModConfig.ScoutmasterTrapChance.Value)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(TeleportScoutmasterCoroutine(chestPos, opener)); } } [IteratorStateMachine(typeof(d__8))] private static IEnumerator TeleportScoutmasterCoroutine(Vector3 chestPos, Character target) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__8(0) { chestPos = chestPos, target = target }; } private static void TryZombieTrap(Vector3 chestPos, Character opener) { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.EnableZombieTrap.Value || !RollChance(ModConfig.ZombieTrapChance.Value)) { return; } int num = Mathf.Max(1, ModConfig.ZombieCountMin.Value); int num2 = Mathf.Max(num, ModConfig.ZombieCountMax.Value); int num3 = Random.Range(num, num2 + 1); float seconds = Mathf.Max(1f, ModConfig.ZombieLifetime.Value); int num4 = 0; Vector3 val = default(Vector3); for (int i = 0; i < num3; i++) { ((Vector3)(ref val))..ctor(Random.Range(-2.5f, 2.5f), 0.5f, Random.Range(-2.5f, 2.5f)); GameObject val2 = null; string[] zombiePrefabCandidates = ZombiePrefabCandidates; foreach (string text in zombiePrefabCandidates) { try { val2 = PhotonNetwork.Instantiate(text, chestPos + val, Random.rotation, (byte)0, (object[])null); if ((Object)(object)val2 != (Object)null) { break; } } catch (Exception ex) { Plugin.Log.LogDebug((object)("Zombie spawn with prefab '" + text + "' failed: " + ex.Message)); } } if (!((Object)(object)val2 == (Object)null)) { MushroomZombie component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { component.initialWakeUpTime = 0f; } Mob component2 = val2.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.SetForcedTarget(opener); } Plugin.DelayedNetworkDestroy(val2, seconds); num4++; } } Plugin.Log.LogInfo((object)$"Zombie trap fired: spawned {num4}/{num3} zombies targeting {opener.characterName}"); } } [HarmonyPatch] internal static class PickaxePermanencePatch { internal const string OurInstantiationTag = "HappyPeak_Marker"; [HarmonyPatch(typeof(ShittyPiton), "Start")] [HarmonyPostfix] private static void Start_Postfix(ShittyPiton __instance) { PhotonView photonView = ((MonoBehaviourPun)__instance).photonView; if (!((Object)(object)photonView == (Object)null)) { object[] instantiationData = photonView.InstantiationData; if (instantiationData != null && instantiationData.Length != 0 && instantiationData[0] is string text && text == "HappyPeak_Marker") { __instance.totalSecondsOfHang = float.MaxValue; } } } } [HarmonyPatch] internal static class PointPingPatch { [HarmonyPatch(typeof(PointPing), "Start")] [HarmonyPostfix] private static void Start_Postfix(PointPing __instance) { MarkerSpawnEventRunner.OnPingCreated(__instance); } } [HarmonyPatch] internal static class SpawnerPatch { private static readonly HashSet ChestSpawnerTypeNames = new HashSet { "Luggage", "RespawnChest" }; private const int SpawnPoolExcluded1 = 131072; private const int SpawnPoolExcluded2 = 32768; private const int SpawnPoolExcluded3 = 65536; private static bool IsChestSpawner(Spawner spawner) { return ChestSpawnerTypeNames.Contains(((object)spawner).GetType().Name); } private static bool ShouldSkipPool(SpawnPool pool) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown int num = (int)pool; if (num != 131072 && num != 32768) { return num == 65536; } return true; } private static int ComputeMultiplier() { if (!ModConfig.EnableLootScaling.Value) { return 1; } return Mathf.Max(1, Mathf.RoundToInt(ModConfig.LootMultiplier.Value)); } [HarmonyPatch(typeof(Spawner), "SpawnItems")] [HarmonyPrefix] private static bool SpawnItems_Prefix(Spawner __instance, List spawnSpots, ref List __result) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) if (!IsChestSpawner(__instance)) { return true; } bool flag = ModConfig.EnableItemRandomization.Value; if (flag && ShouldSkipPool(__instance.spawnPool)) { flag = false; } int num = ComputeMultiplier(); if (!flag && num <= 1) { return true; } if (!PhotonNetwork.IsMasterClient) { __result = new List(); return false; } if (spawnSpots == null || spawnSpots.Count == 0) { __result = new List(); return false; } int num2 = spawnSpots.Count * Mathf.Max(1, num); List list; if (flag) { list = new List(num2); Item val = default(Item); for (int i = 0; i < num2; i++) { if (ItemDatabase.TryGetItem(CustomLoot.GetRandomId(), ref val) && (Object)(object)val != (Object)null) { list.Add(((Component)val).gameObject); } } } else { list = __instance.GetObjectsToSpawn(num2, __instance.canRepeatSpawns); } if (list == null || list.Count == 0) { __result = new List(); return false; } List list2 = new List(); for (int j = 0; j < num2 && j < list.Count; j++) { GameObject val2 = list[j]; if ((Object)(object)val2 == (Object)null) { continue; } Transform val3 = spawnSpots[j % spawnSpots.Count]; Vector3 val4 = val3.position; if (j >= spawnSpots.Count) { val4 += new Vector3(Random.Range(-0.5f, 0.5f), Random.Range(0f, 0.2f), Random.Range(-0.5f, 0.5f)); } GameObject val5 = PhotonNetwork.InstantiateItemRoom(((Object)val2).name, val4, val3.rotation); if ((Object)(object)val5 == (Object)null) { continue; } Item component = val5.GetComponent(); if (!((Object)(object)component == (Object)null)) { list2.Add(((Component)component).GetComponent()); if ((Object)(object)__instance.spawnUpTowardsTarget != (Object)null) { Vector3 val6 = __instance.spawnUpTowardsTarget.position - ((Component)component).transform.position; Vector3 normalized = ((Vector3)(ref val6)).normalized; ((Component)component).transform.up = normalized; } if (__instance.centerItemsVisually) { Vector3 val7 = val3.position - component.Center(); Transform transform = ((Component)component).transform; transform.position += val7; } } } __result = list2; return false; } } } namespace HappyPeak.Events { internal static class BananaPeelEventRunner { private static readonly string[] PeelPrefabNames = new string[4] { "Berrynana Peel Yellow", "Berrynana Peel Blue Variant", "Berrynana Peel Brown Variant", "Berrynana Peel Pink Variant" }; private const float OffsetMin = 1f; private const float OffsetMax = 10f; private static float _nextWaveTime; public static void Tick() { if (!ModConfig.EnableRandomBananaPeels.Value) { _nextWaveTime = 0f; } else { if (!PhotonNetwork.IsMasterClient) { return; } if (!Plugin.IsInGameRun()) { _nextWaveTime = 0f; return; } List humanCharacters = GetHumanCharacters(); if (humanCharacters.Count == 0) { _nextWaveTime = 0f; } else if (_nextWaveTime <= 0f) { _nextWaveTime = Time.time + RollInterval(); } else if (!(Time.time < _nextWaveTime)) { SpawnWave(humanCharacters); _nextWaveTime = Time.time + RollInterval(); } } } private static List GetHumanCharacters() { return Character.AllCharacters?.Where((Character c) => (Object)(object)c != (Object)null && !c.isBot).ToList() ?? new List(); } private static float RollInterval() { int num = Mathf.Max(1, ModConfig.BananaPeelIntervalMin.Value); int num2 = Mathf.Max(num, ModConfig.BananaPeelIntervalMax.Value); return Random.Range(num, num2 + 1); } private static void SpawnWave(List humans) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Max(1, ModConfig.BananaPeelsPerPlayerMin.Value); int num2 = Mathf.Max(num, ModConfig.BananaPeelsPerPlayerMax.Value); float seconds = Mathf.Max(1f, ModConfig.BananaPeelLifetime.Value); float num3 = Mathf.Max(0f, ModConfig.BananaPeelDropHeight.Value); float value = ModConfig.CampfireSafeRadius.Value; int num4 = 0; Vector3 val = default(Vector3); foreach (Character human in humans) { if ((Object)(object)human == (Object)null || Plugin.IsNearCampfire(human.Center, value)) { continue; } int num5 = Random.Range(num, num2 + 1); Vector3 center = human.Center; for (int i = 0; i < num5; i++) { int num6 = ((Random.Range(0, 2) == 0) ? 1 : (-1)); int num7 = ((Random.Range(0, 2) == 0) ? 1 : (-1)); ((Vector3)(ref val))..ctor(center.x + (float)num6 * Random.Range(1f, 10f), center.y + num3, center.z + (float)num7 * Random.Range(1f, 10f)); string text = PeelPrefabNames[Random.Range(0, PeelPrefabNames.Length)]; GameObject val2 = PhotonNetwork.InstantiateItemRoom(text, val, Random.rotation); if (!((Object)(object)val2 == (Object)null)) { Plugin.DelayedNetworkDestroy(val2, seconds); num4++; } } } Plugin.Log.LogInfo((object)$"Banana rain wave: spawned {num4} peels above {humans.Count} player(s)."); } } internal static class DynamiteEventRunner { private static float _nextWaveTime; public static void Tick() { if (!ModConfig.EnableRandomDynamite.Value) { _nextWaveTime = 0f; } else { if (!PhotonNetwork.IsMasterClient) { return; } if (!Plugin.IsInGameRun()) { _nextWaveTime = 0f; return; } List humanCharacters = GetHumanCharacters(); if (humanCharacters.Count == 0) { _nextWaveTime = 0f; } else if (_nextWaveTime <= 0f) { _nextWaveTime = Time.time + RollInterval(); } else if (!(Time.time < _nextWaveTime)) { int num = Mathf.Max(1, ModConfig.DynamiteCountPerWave.Value); for (int i = 0; i < num; i++) { SpawnOneStick(humanCharacters); } _nextWaveTime = Time.time + RollInterval(); } } } private static List GetHumanCharacters() { return Character.AllCharacters?.Where((Character c) => (Object)(object)c != (Object)null && !c.isBot).ToList() ?? new List(); } private static float RollInterval() { int num = Mathf.Max(1, ModConfig.DynamiteIntervalMin.Value); int num2 = Mathf.Max(num, ModConfig.DynamiteIntervalMax.Value); return Random.Range(num, num2 + 1); } private static void SpawnOneStick(List humans) { //IL_0081: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(0f, ModConfig.DynamiteForwardDistance.Value); float num2 = Mathf.Max(0f, ModConfig.DynamiteDropHeight.Value); float num3 = Mathf.Max(0f, ModConfig.DynamiteSidewaysJitter.Value); float seconds = Mathf.Max(1f, ModConfig.DynamiteLifetime.Value); float value = ModConfig.CampfireSafeRadius.Value; Character val = humans[Random.Range(0, humans.Count)]; if ((Object)(object)val == (Object)null || Plugin.IsNearCampfire(val.Center, value)) { return; } Vector3 val2 = val.data.lookDirection; if (((Vector3)(ref val2)).sqrMagnitude < 0.001f) { val2 = Vector3.forward; } Vector3 val3 = new Vector3(val2.x, 0f, val2.z); Vector3 val4 = ((Vector3)(ref val3)).normalized; if (((Vector3)(ref val4)).sqrMagnitude < 0.001f) { val4 = Vector3.forward; } val3 = Vector3.Cross(Vector3.up, val4); Vector3 normalized = ((Vector3)(ref val3)).normalized; float num4 = ((num3 > 0f) ? Random.Range(0f - num3, num3) : 0f); Vector3 val5 = val.Head + val4 * num + normalized * num4 + Vector3.up * num2; GameObject val6 = PhotonNetwork.InstantiateItemRoom("Dynamite", val5, Quaternion.identity); if ((Object)(object)val6 == (Object)null) { Plugin.Log.LogInfo((object)"Dynamite spawn aborted: PhotonNetwork.InstantiateItemRoom returned null."); return; } Item component = val6.GetComponent(); if (component != null) { component.SetState((ItemState)0, (Character)null); } if (ModConfig.LightDynamiteOnSpawn.Value) { Dynamite component2 = val6.GetComponent(); if (component2 != null) { component2.LightFlare(); } } Plugin.DelayedNetworkDestroy(val6, seconds); Plugin.Log.LogInfo((object)$"Dynamite drop: {num}m in front + {num2}m above {((Object)val).name}'s head (lit={ModConfig.LightDynamiteOnSpawn.Value})"); } } internal static class EruptionEventRunner { private const int FootBodypartIndex = 13; private static float _nextWaveTime; private static bool _missingSpawnerLogged; public static void Tick() { if (!ModConfig.EnableRandomEruptions.Value) { _nextWaveTime = 0f; _missingSpawnerLogged = false; } else { if (!PhotonNetwork.IsMasterClient) { return; } if (!Plugin.IsInGameRun()) { _nextWaveTime = 0f; return; } List humanCharacters = GetHumanCharacters(); if (humanCharacters.Count == 0) { _nextWaveTime = 0f; } else if (_nextWaveTime <= 0f) { _nextWaveTime = Time.time + RollInterval(); } else { if (Time.time < _nextWaveTime) { return; } EruptionSpawner val = Object.FindAnyObjectByType(); if ((Object)(object)val == (Object)null) { if (!_missingSpawnerLogged) { Plugin.Log.LogInfo((object)"Eruption skipped: no EruptionSpawner found in this scene."); _missingSpawnerLogged = true; } _nextWaveTime = Time.time + RollInterval(); } else { _missingSpawnerLogged = false; TriggerOneEruption(val, humanCharacters); _nextWaveTime = Time.time + RollInterval(); } } } } private static List GetHumanCharacters() { return Character.AllCharacters?.Where((Character c) => (Object)(object)c != (Object)null && !c.isBot).ToList() ?? new List(); } private static float RollInterval() { int num = Mathf.Max(1, ModConfig.EruptionIntervalMin.Value); int num2 = Mathf.Max(num, ModConfig.EruptionIntervalMax.Value); return Random.Range(num, num2 + 1); } private static void TriggerOneEruption(EruptionSpawner spawner, List humans) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: 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) Character val = humans[Random.Range(0, humans.Count)]; if ((Object)(object)val == (Object)null) { return; } if (Plugin.IsNearCampfire(val.Center, ModConfig.CampfireSafeRadius.Value)) { Plugin.Log.LogInfo((object)("Eruption skipped: " + ((Object)val).name + " is in a campfire safe zone.")); return; } Bodypart bodypart = val.GetBodypart((BodypartType)13); if (!((Object)(object)bodypart == (Object)null)) { float num = Mathf.Max(0f, ModConfig.EruptionOffsetMin.Value); float num2 = Mathf.Max(num, ModConfig.EruptionOffsetMax.Value); Vector3 val2 = ((Component)bodypart).transform.position; if (num2 > 0f) { int num3 = ((Random.Range(0, 2) == 0) ? 1 : (-1)); int num4 = ((Random.Range(0, 2) == 0) ? 1 : (-1)); val2 += new Vector3((float)num3 * Random.Range(num, num2), 0f, (float)num4 * Random.Range(num, num2)); } PhotonView component = ((Component)spawner).GetComponent(); if ((Object)(object)component == (Object)null) { Plugin.Log.LogWarning((object)"Eruption skipped: EruptionSpawner has no PhotonView."); return; } component.RPC("RPCA_SpawnEruption", (RpcTarget)0, new object[1] { val2 }); Plugin.Log.LogInfo((object)$"Triggered eruption at {val2} near {((Object)val).name}"); } } } internal static class MarkerSpawnEventRunner { private const string FallbackItemPrefab = "ShittyPiton"; private const float AutoCleanupSeconds = 60f; private const int MaxActivePerPinger = 2; private static readonly string[] EmbeddedItemCandidates = new string[2] { "0_Items/PickAxeHammered_Shitty", "0_Items/ClimbingSpikeHammered" }; private const float EmbedSurfaceOffset = 0.3f; private static readonly HashSet _processedPingIds = new HashSet(); private static readonly Dictionary> _activeSpawnsByPinger = new Dictionary>(); public static void OnPingCreated(PointPing ping) { if (!ModConfig.EnableMarkerSpawn.Value || !PhotonNetwork.IsMasterClient || !Plugin.IsInGameRun() || (Object)(object)ping == (Object)null) { return; } int instanceID = ((Object)ping).GetInstanceID(); if (_processedPingIds.Add(instanceID)) { int num = ResolvePingerViewId(ping); if (num != 0) { HandleNewPing(num, ping); } } } public static void ResetState() { _processedPingIds.Clear(); _activeSpawnsByPinger.Clear(); } private static int ResolvePingerViewId(PointPing ping) { Character val = ping.pointPinger?.character; if ((Object)(object)val == (Object)null) { return 0; } PhotonView photonView = ((MonoBehaviourPun)val).photonView; if (!((Object)(object)photonView != (Object)null)) { return 0; } return photonView.ViewID; } private static void HandleNewPing(int pingerViewId, PointPing ping) { GameObject val = SpawnSinglePiton(ping); if (!((Object)(object)val == (Object)null)) { if (!_activeSpawnsByPinger.TryGetValue(pingerViewId, out List value)) { value = new List(); _activeSpawnsByPinger[pingerViewId] = value; } value.RemoveAll((GameObject go) => (Object)(object)go == (Object)null); value.Add(val); Character pinger = ping.pointPinger?.character; while (value.Count > 2) { EvictFarthestFromPlayer(value, pingerViewId, pinger); } } } private static void EvictFarthestFromPlayer(List queue, int pingerViewId, Character? pinger) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (queue.Count == 0) { return; } int num = queue.Count - 1; if (num <= 0) { return; } int num2 = -1; float num3 = -1f; if ((Object)(object)pinger != (Object)null) { Vector3 center = pinger.Center; for (int i = 0; i < num; i++) { GameObject val = queue[i]; if (!((Object)(object)val == (Object)null)) { float num4 = Vector3.Distance(val.transform.position, center); if (num4 > num3) { num3 = num4; num2 = i; } } } } else { num2 = 0; } if (num2 < 0) { queue.RemoveRange(0, num); return; } GameObject val2 = queue[num2]; queue.RemoveAt(num2); if ((Object)(object)val2 == (Object)null) { return; } Item component = val2.GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.holderCharacter != (Object)null) { Plugin.Log.LogInfo((object)$"Marker spawn: farthest piton for view {pingerViewId} is held by {((Object)component.holderCharacter).name} — skipping destroy."); return; } if (IsParentedToAnyCharacter(val2)) { Plugin.Log.LogInfo((object)$"Marker spawn: farthest piton for view {pingerViewId} is parented under a Character — skipping destroy."); return; } try { PhotonNetwork.Destroy(val2); Plugin.Log.LogInfo((object)$"Marker spawn: evicted farthest pickaxe (~{num3:F1}m from pinger view {pingerViewId})"); } catch (Exception ex) { Plugin.Log.LogInfo((object)$"Skipped destroying farthest pickaxe for view {pingerViewId}: {ex.Message}"); } } private static bool IsParentedToAnyCharacter(GameObject go) { Transform parent = go.transform.parent; while ((Object)(object)parent != (Object)null) { if ((Object)(object)((Component)parent).GetComponent() != (Object)null) { return true; } parent = parent.parent; } return false; } private static GameObject? SpawnSinglePiton(PointPing ping) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_0048: 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_0021: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_004f: 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_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) Vector3 position = ((Component)ping).transform.position; Vector3 hitNormal = ping.hitNormal; Vector3 val; Quaternion rotation; if (((Vector3)(ref hitNormal)).sqrMagnitude > 0.001f) { val = position + hitNormal * 0.3f; rotation = Quaternion.LookRotation(-hitNormal, Vector3.up); } else { val = position; rotation = Quaternion.identity; } GameObject val2 = TrySpawnEmbedded(val, rotation); if ((Object)(object)val2 != (Object)null) { Plugin.DelayedNetworkDestroy(val2, 60f); Plugin.Log.LogInfo((object)$"Marker spawn: embedded climbing tool at {val} (normal {hitNormal})"); return val2; } return SpawnFallbackItem(position); } private static GameObject? TrySpawnEmbedded(Vector3 pos, Quaternion rotation) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) object[] array = new object[1] { "HappyPeak_Marker" }; string[] embeddedItemCandidates = EmbeddedItemCandidates; foreach (string text in embeddedItemCandidates) { try { GameObject val = PhotonNetwork.Instantiate(text, pos, rotation, (byte)0, array); if ((Object)(object)val != (Object)null) { Plugin.Log.LogInfo((object)("Spawned embedded climbing tool prefab '" + text + "'.")); return val; } } catch (Exception ex) { Plugin.Log.LogDebug((object)("Embedded climbing tool candidate '" + text + "' failed: " + ex.Message)); } } return null; } private static GameObject? SpawnFallbackItem(Vector3 markerPos) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = PhotonNetwork.InstantiateItemRoom("ShittyPiton", markerPos + Vector3.up * 0.2f, Quaternion.identity); if ((Object)(object)val != (Object)null) { Plugin.Log.LogInfo((object)string.Format("Marker spawn: dropped {0} item (fallback, not embedded) at {1}", "ShittyPiton", markerPos)); Plugin.DelayedNetworkDestroy(val, 60f); return val; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to spawn 'ShittyPiton' at marker: " + ex.Message)); } return null; } } internal static class TornadoEventRunner { private const int MaxSpawnAttempts = 10; private const float OffsetMin = 100f; private const float OffsetMax = 600f; private const float TerrainCheckRadius = 50f; private const float TargetTransientLifetime = 300f; private static float _nextWaveTime; public static void Tick() { if (!ModConfig.EnableRandomTornadoes.Value) { _nextWaveTime = 0f; } else { if (!PhotonNetwork.IsMasterClient) { return; } if (!Plugin.IsInGameRun()) { _nextWaveTime = 0f; return; } List humanCharacters = GetHumanCharacters(); if (humanCharacters.Count == 0) { _nextWaveTime = 0f; } else if (_nextWaveTime <= 0f) { _nextWaveTime = Time.time + RollInterval(); } else { if (Time.time < _nextWaveTime) { return; } if (IsAtKilnAndDisabled()) { _nextWaveTime = Time.time + RollInterval(); return; } int num = Mathf.Max(1, ModConfig.TornadoesPerWave.Value); for (int i = 0; i < num; i++) { SpawnOneTornado(humanCharacters); } _nextWaveTime = Time.time + RollInterval(); } } } private static List GetHumanCharacters() { return Character.AllCharacters?.Where((Character c) => (Object)(object)c != (Object)null && !c.isBot).ToList() ?? new List(); } private static float RollInterval() { int num = Mathf.Max(1, ModConfig.TornadoIntervalMin.Value); int num2 = Mathf.Max(num, ModConfig.TornadoIntervalMax.Value); return Random.Range(num, num2 + 1); } private static bool IsAtKilnAndDisabled() { if (!ModConfig.EnableTornadoAtKiln.Value) { return Plugin.IsAtKiln(); } return false; } private static void SpawnOneTornado(List humans) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: 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_00ee: Expected O, but got Unknown //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) float value = ModConfig.CampfireSafeRadius.Value; for (int i = 0; i < 10; i++) { Character val = humans[Random.Range(0, humans.Count)]; if ((Object)(object)val == (Object)null || Plugin.IsNearCampfire(val.Center, value)) { continue; } Vector3 center = val.Center; int num = ((Random.Range(0, 2) == 0) ? 1 : (-1)); int num2 = ((Random.Range(0, 2) == 0) ? 1 : (-1)); center += new Vector3((float)num * Random.Range(100f, 600f), 0f, (float)num2 * Random.Range(100f, 600f)); if (!Physics.CheckSphere(center, 50f)) { continue; } GameObject val2 = PhotonNetwork.Instantiate("Tornado", center, Quaternion.identity, (byte)0, (object[])null); if (!((Object)(object)val2 == (Object)null)) { Tornado component = val2.GetComponent(); if (!((Object)(object)component == (Object)null)) { GameObject val3 = new GameObject("TornadoTarget"); val3.transform.position = val.Center; Object.Destroy((Object)(object)val3, 300f); component.target = val3.transform; float seconds = Mathf.Max(1f, ModConfig.TornadoLifetime.Value); Plugin.DelayedNetworkDestroy(val2, seconds); Plugin.Log.LogInfo((object)$"Spawned tornado at {center} targeting {((Object)val).name}"); return; } } } Plugin.Log.LogInfo((object)"Tornado spawn aborted: no valid placement after 10 attempts."); } } internal static class TumbleweedEventRunner { private const string TumbleweedPrefab = "TumbleWeed"; private const float OffsetMin = 1f; private const float OffsetMax = 10f; private static float _nextWaveTime; public static void Tick() { if (!ModConfig.EnableRandomTumbleweeds.Value) { _nextWaveTime = 0f; } else { if (!PhotonNetwork.IsMasterClient) { return; } if (!Plugin.IsInGameRun()) { _nextWaveTime = 0f; return; } List humanCharacters = GetHumanCharacters(); if (humanCharacters.Count == 0) { _nextWaveTime = 0f; } else if (_nextWaveTime <= 0f) { _nextWaveTime = Time.time + RollInterval(); } else if (!(Time.time < _nextWaveTime)) { SpawnWave(humanCharacters); _nextWaveTime = Time.time + RollInterval(); } } } private static List GetHumanCharacters() { return Character.AllCharacters?.Where((Character c) => (Object)(object)c != (Object)null && !c.isBot).ToList() ?? new List(); } private static float RollInterval() { int num = Mathf.Max(1, ModConfig.TumbleweedIntervalMin.Value); int num2 = Mathf.Max(num, ModConfig.TumbleweedIntervalMax.Value); return Random.Range(num, num2 + 1); } private static void SpawnWave(List humans) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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) int num = Mathf.Max(1, ModConfig.TumbleweedsPerPlayerMin.Value); int num2 = Mathf.Max(num, ModConfig.TumbleweedsPerPlayerMax.Value); float seconds = Mathf.Max(1f, ModConfig.TumbleweedLifetime.Value); float num3 = Mathf.Max(0f, ModConfig.TumbleweedDropHeight.Value); float value = ModConfig.CampfireSafeRadius.Value; int num4 = 0; Vector3 val = default(Vector3); foreach (Character human in humans) { if ((Object)(object)human == (Object)null || Plugin.IsNearCampfire(human.Center, value)) { continue; } int num5 = Random.Range(num, num2 + 1); Vector3 center = human.Center; for (int i = 0; i < num5; i++) { int num6 = ((Random.Range(0, 2) == 0) ? 1 : (-1)); int num7 = ((Random.Range(0, 2) == 0) ? 1 : (-1)); ((Vector3)(ref val))..ctor(center.x + (float)num6 * Random.Range(1f, 10f), center.y + num3, center.z + (float)num7 * Random.Range(1f, 10f)); GameObject val2 = PhotonNetwork.Instantiate("TumbleWeed", val, Random.rotation, (byte)0, (object[])null); if (!((Object)(object)val2 == (Object)null)) { Plugin.DelayedNetworkDestroy(val2, seconds); num4++; } } } Plugin.Log.LogInfo((object)$"Tumbleweed rain wave: spawned {num4} tumbleweeds above {humans.Count} player(s)."); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }