Decompiled source of BoomLagoon v1.3.4
BepInEx/plugins/BoomLagoon/BoomLagoon.dll
Decompiled 4 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using BoomLagoon.Amenities; using BoomLagoon.Diagnostics; using BoomLagoon.Explosives; using BoomLagoon.MapLoaderFixes; using BoomLagoon.Net; using BoomLagoon.Ramps; using BoomLagoon.Wildlife; using FishNet; using FishNet.Managing.Timing; using FishNet.Object; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.Audio; using UnityEngine.Networking; 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("BoomLagoon")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.3.4.0")] [assembly: AssemblyInformationalVersion("1.3.4+f7ff5ca33fe9bd49045b15873f81a45705063810")] [assembly: AssemblyProduct("BoomLagoon")] [assembly: AssemblyTitle("BoomLagoon")] [assembly: AssemblyVersion("1.3.4.0")] [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 BoomLagoon { public readonly struct DevSetting<T> { private readonly T _fixedValue; public T Value => _fixedValue; public DevSetting(T fixedValue) { _fixedValue = fixedValue; } } public static class Config { public static class MapLoader { public static class Defaults { public const bool SkipIdleUpdate = true; } public static DevSetting<bool> SkipIdleUpdate; public static void Bind(ConfigFile config) { SkipIdleUpdate = new DevSetting<bool>(fixedValue: true); } } public static class Telemetry { public static class Defaults { public const bool Enabled = false; public const float SampleSeconds = 10f; public const bool WriteCsv = true; public const bool ProbeMethods = true; public const float HitchMilliseconds = 33.4f; } public static DevSetting<bool> Enabled; public static DevSetting<float> SampleSeconds; public static DevSetting<bool> WriteCsv; public static DevSetting<bool> ProbeMethods; public static DevSetting<float> HitchMilliseconds; public static void Bind(ConfigFile config) { Enabled = new DevSetting<bool>(fixedValue: false); SampleSeconds = new DevSetting<float>(10f); WriteCsv = new DevSetting<bool>(fixedValue: true); ProbeMethods = new DevSetting<bool>(fixedValue: true); HitchMilliseconds = new DevSetting<float>(33.4f); } } public static class Shop { public static class Defaults { public const bool RespectIslandProgression = true; } public static ConfigEntry<bool> RespectIslandProgression; public static void Bind(ConfigFile config) { RespectIslandProgression = config.Bind<bool>("Shop", "RespectIslandProgression", true, "Only stock shop items whose vanilla island the host has unlocked coordinates to (e.g. the Sniper Rifle appears once Island4 is on the map, the Assault Rifle once Island5 is). Items appear live as progress is made. Set false to always stock everything. In multiplayer the host's setting applies to everyone."); } } public static class RampBoost { public static class Defaults { public const bool Enabled = true; public const float BoostSpeed = 20f; public const bool RequireDriver = true; } public static ConfigEntry<bool> Enabled; public static ConfigEntry<float> BoostSpeed; public static ConfigEntry<bool> RequireDriver; public static void Bind(ConfigFile config) { Enabled = config.Bind<bool>("Ramp", "Enabled", true, "Enable the ramp boost pads baked into the map."); BoostSpeed = config.Bind<float>("Ramp", "BoostSpeed", 20f, "On contact, the boat's speed is set to this many units/second along its direction of travel. The boat's normal top speed is 50, so 50 launches it at full speed and higher values exceed the normal cap (which is temporarily lifted to allow it, then restored as the boat slows)."); RequireDriver = config.Bind<bool>("Ramp", "RequireDriver", true, "Only boost the boat while it is being driven (has a driver). Set false to also boost an unoccupied boat that enters a pad."); } } public static class FatManBomb { public static class Defaults { public const bool ShootToDetonate = false; public const float BlastMultiplier = 20f; public const bool BlastKillAllBirds = true; public const bool BlastKillGreenery = true; public const int SpawnDeadFishCount = 10; public const int MaxDeadFishOnMap = 20; public const bool BlastCooksAll = true; public const float CookedLevel = 1f; public const float RespawnAfterDetonationSeconds = 60f; public const float FlashDurationSeconds = 0.75f; public const float ExplosionDelayAfterFlashPeak = 0f; public const string FlashbangSoundFile = "flashbang-gah-dayum.mp3"; public const float FlashbangRingVolume = 1f; public const float FlashbangToneVolumeScale = 0.05f; public const float FlashbangRingFrequency = 4000f; public const float FlashbangRingDurationSeconds = 15f; public const string MassiveExplosionSoundFile = "MassiveExplosion.ogg"; public const float MassiveExplosionVolume = 0.35f; public const float MassiveExplosionMinDistance = 150f; public const float MassiveExplosionMaxDistance = 2000f; } internal const string BombName = "FatManBomb"; internal const string FireVfxNames = "LavaExplosion,VolcanoEruption"; internal const string KillLeafsRootName = "Decorations"; internal const string KillLeafsMatchNames = "PalmLeafs,Foliage"; internal const string DynamiteResourcePath = "items/purchasable/Dynamite"; internal const string MushroomCloudObjectName = "MushroomCloud"; internal const float AssociationRadius = 3f; public static ConfigEntry<bool> BlastKillAllBirds; public static ConfigEntry<int> SpawnDeadFishCount; public static ConfigEntry<int> MaxDeadFishOnMap; public static ConfigEntry<bool> BlastCooksAll; public static ConfigEntry<float> CookedLevel; public static ConfigEntry<bool> BlastKillGreenery; public static DevSetting<bool> ShootToDetonate; public static DevSetting<float> BlastMultiplier; public static DevSetting<float> RespawnAfterDetonationSeconds; public static DevSetting<float> FlashDurationSeconds; public static DevSetting<float> ExplosionDelayAfterFlashPeak; public static DevSetting<string> FlashbangSoundFile; public static DevSetting<float> FlashbangRingVolume; public static DevSetting<float> FlashbangToneVolumeScale; public static DevSetting<float> FlashbangRingFrequency; public static DevSetting<float> FlashbangRingDurationSeconds; public static DevSetting<string> MassiveExplosionSoundFile; public static DevSetting<float> MassiveExplosionVolume; public static DevSetting<float> MassiveExplosionMinDistance; public static DevSetting<float> MassiveExplosionMaxDistance; public static void Bind(ConfigFile config) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Expected O, but got Unknown BlastKillAllBirds = config.Bind<bool>("FatManBomb", "BlastKillAllBirds", true, "Kill every bird currently in the air over the island when the FatManBomb detonates (the base game's explosion only kills creatures within the damage radius). Birds over other islands are left alone."); BlastKillGreenery = config.Bind<bool>("FatManBomb", "BlastKillGreenery", true, "Blow the island's palm leaves and foliage away at the moment the pre-detonation screen flash peaks. They stay gone for the rest of the session. In multiplayer the host's setting applies, and players who join later see the foliage already gone."); SpawnDeadFishCount = config.Bind<int>("FatManBomb", "SpawnDeadFishCount", 10, new ConfigDescription("Number of dead fish scattered underwater when the FatManBomb detonates (the same native behaviour dynamite has: already-dead fish scattered near the blast). 0 = no dead fish.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 200), Array.Empty<object>())); MaxDeadFishOnMap = config.Bind<int>("FatManBomb", "MaxDeadFishOnMap", 20, new ConfigDescription("Cap on dead fish present on the map after a FatManBomb detonation. The bomb only spawns the difference between this cap and the dead fish already there (never more than SpawnDeadFishCount).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 200), Array.Empty<object>())); BlastCooksAll = config.Bind<bool>("FatManBomb", "BlastCooksAll", true, "When the FatManBomb detonates, cook EVERY creature on the island to CookedLevel -- fish, birds and clams, spawned or pre-existing, dead or alive, inside the blast radius or not; creatures at other islands are left alone (sets the same Cookness value the grill/lava use). Cookness persists, so a creature that is still alive when cooked stays cooked once it is later caught or killed. Runs after the birds are downed, so BlastKillAllBirds kills are cooked too."); CookedLevel = config.Bind<float>("FatManBomb", "CookedLevel", 1f, new ConfigDescription("Cookness value (the game's _cookness, 0 = raw, 2 = burnt) applied to every creature cooked by BlastCooksAll. 1 is the sweet spot -- the peak of the game's worth curve (1.5x raw value) -- same as CookWithlava; values above 1 start overcooking toward burnt (as low as 0.1x at 2).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); ShootToDetonate = new DevSetting<bool>(fixedValue: false); BlastMultiplier = new DevSetting<float>(20f); RespawnAfterDetonationSeconds = new DevSetting<float>(60f); FlashDurationSeconds = new DevSetting<float>(0.75f); ExplosionDelayAfterFlashPeak = new DevSetting<float>(0f); FlashbangSoundFile = new DevSetting<string>("flashbang-gah-dayum.mp3"); FlashbangRingVolume = new DevSetting<float>(1f); FlashbangToneVolumeScale = new DevSetting<float>(0.05f); FlashbangRingFrequency = new DevSetting<float>(4000f); FlashbangRingDurationSeconds = new DevSetting<float>(15f); MassiveExplosionSoundFile = new DevSetting<string>("MassiveExplosion.ogg"); MassiveExplosionVolume = new DevSetting<float>(0.35f); MassiveExplosionMinDistance = new DevSetting<float>(150f); MassiveExplosionMaxDistance = new DevSetting<float>(2000f); } } public static class Bomber { public static class Defaults { public const bool Enabled = true; public const float RaidIntervalSeconds = 300f; public const float LapSeconds = 40f; public const float Altitude = 250f; public const float SirenLeadSeconds = 15f; public const float SirenVolume = 0.35f; } internal const string RootName = "Bomber"; internal const string PlaneName = "BombPlane"; internal const string BombName = "ABomb"; internal const string SirenName = "AirRaidSiren"; public static ConfigEntry<bool> Enabled; public static ConfigEntry<float> RaidIntervalSeconds; public static DevSetting<float> LapSeconds; public static DevSetting<float> Altitude; public static DevSetting<float> SirenLeadSeconds; public static DevSetting<float> SirenVolume; public static void Bind(ConfigFile config) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown Enabled = config.Bind<bool>("Bomber", "Enabled", true, "Show the bomber circling the island and run its recurring air raids (siren from the lighthouse, then the ABomb drops onto the FatManBomb and detonates it). In multiplayer the host's Bomber settings apply to everyone."); RaidIntervalSeconds = config.Bind<float>("Bomber", "RaidIntervalSeconds", 300f, new ConfigDescription("Seconds between air raids. The bomb is only released when the plane is directly over the FatManBomb, so the actual gap is rounded up to a whole number of laps (LapSeconds).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 7200f), Array.Empty<object>())); LapSeconds = new DevSetting<float>(40f); Altitude = new DevSetting<float>(250f); SirenLeadSeconds = new DevSetting<float>(15f); SirenVolume = new DevSetting<float>(0.35f); } } public static void Bind(ConfigFile config) { RampBoost.Bind(config); FatManBomb.Bind(config); Bomber.Bind(config); Shop.Bind(config); MapLoader.Bind(config); Telemetry.Bind(config); } } public abstract class LagoonSceneComponent : MonoBehaviour { public const string LagoonSceneName = "TournamentLagoon"; private void OnEnable() { SceneManager.sceneLoaded += HandleSceneLoaded; } private void OnDisable() { SceneManager.sceneLoaded -= HandleSceneLoaded; } private void HandleSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (((Scene)(ref scene)).IsValid() && string.Equals(((Scene)(ref scene)).name, "TournamentLagoon", StringComparison.OrdinalIgnoreCase)) { OnLagoonLoaded(scene); } } protected abstract void OnLagoonLoaded(Scene scene); } [BepInPlugin("0cahlo.howtofish.boomlagoon", "BoomLagoon", "1.3.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "0cahlo.howtofish.boomlagoon"; public const string PluginName = "BoomLagoon"; public const string PluginVersion = "1.3.4"; private const string HTFMapLoaderGuid = "com.howtofish.maploader"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; BindConfig(); _harmony = new Harmony("0cahlo.howtofish.boomlagoon"); _harmony.PatchAll(); if (Config.MapLoader.SkipIdleUpdate.Value) { MapLoaderIdleSkip.Install(_harmony); } ((Component)this).gameObject.AddComponent<HostSettings>(); ((Component)this).gameObject.AddComponent<IslandLoadCompletion>(); ((Component)this).gameObject.AddComponent<CurrentIslandSync>(); ((Component)this).gameObject.AddComponent<ClamSpawner>(); ((Component)this).gameObject.AddComponent<BirdSpawner>(); ((Component)this).gameObject.AddComponent<BakedPurchasableWiring>(); ((Component)this).gameObject.AddComponent<ShopProgressionFilter>(); ((Component)this).gameObject.AddComponent<SellerNpcSpawner>(); ((Component)this).gameObject.AddComponent<RampBooster>(); ((Component)this).gameObject.AddComponent<FatManBombManager>(); ((Component)this).gameObject.AddComponent<BomberManager>(); AddTelemetry(); Log.LogInfo((object)"BoomLagoon 1.3.4 loaded."); } private void AddTelemetry() { if (Config.Telemetry.Enabled.Value) { if (Config.Telemetry.ProbeMethods.Value) { PerfProbes.Install(_harmony); } ((Component)this).gameObject.AddComponent<PerfTelemetry>(); ((Component)this).gameObject.AddComponent<SceneCensus>(); } } private void BindConfig() { Config.Bind(((BaseUnityPlugin)this).Config); } } internal static class Reflect { public static FieldRef<T> StaticField<T>(Type owner, string name) { FieldInfo fieldInfo = AccessTools.Field(owner, name); if (fieldInfo == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Reflect] " + owner.Name + "." + name + " is gone; the code that reads it will stand down.")); } return null; } return AccessTools.StaticFieldRefAccess<T>(fieldInfo); } public static FieldRef<TOwner, TField> Field<TOwner, TField>(string name) { FieldInfo fieldInfo = AccessTools.Field(typeof(TOwner), name); if (fieldInfo == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Reflect] " + typeof(TOwner).Name + "." + name + " is gone; the code that reads it will stand down.")); } return null; } return AccessTools.FieldRefAccess<TOwner, TField>(fieldInfo); } } internal static class SceneUtil { public const float ZoneRadius = 240f; public static GameObject FindIslandHolder(Scene scene) { GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects(); foreach (GameObject val in rootGameObjects) { if ((Object)(object)val != (Object)null && string.Equals(((Object)val).name, "IslandHolder", StringComparison.OrdinalIgnoreCase)) { return val; } } return null; } public static bool IsBundleScene(Scene scene) { if (((Scene)(ref scene)).IsValid()) { return ((Scene)(ref scene)).buildIndex < 0; } return false; } public static bool WithinZone(Vector3 center, Vector3 pos) { //IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = pos.x - center.x; float num2 = pos.z - center.z; return num * num + num2 * num2 <= 57600f; } public static string StripClone(string name) { if (string.IsNullOrEmpty(name)) { return name; } if (name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name.Substring(0, name.Length - "(Clone)".Length).TrimEnd(); } return name; } } } namespace BoomLagoon.Wildlife { public class BirdSpawner : LagoonSceneComponent { private const string MarkerParentName = "BirdSpawners"; private const string MarkerName = "SeagullSpawner"; private const bool SpawnsDeadCreature = false; private const int StartFrequencyForRadio = 98; private const bool UseRandomRotation = true; private const int MaxItemsSpawned = 3; private const bool SpawnInstant = true; private const bool OnlySpawnOnce = false; private const bool SpawnInAir = true; private const float SpawnDelaySeconds = 180f; private const int SpawnCount = 1; private static readonly Vector3 SpawnBoxSize = new Vector3(50f, 1f, 50f); private static readonly FieldInfo IsActiveField = GetItemSpawnerField("_isActive"); private static readonly FieldInfo ItemToSpawnField = GetItemSpawnerField("_itemToSpawn"); private static readonly FieldInfo SpawnsDeadCreatureField = GetItemSpawnerField("_spawnsDeadCreature"); private static readonly FieldInfo StartFrequencyForRadioField = GetItemSpawnerField("_startFrequencyForRadio"); private static readonly FieldInfo UseRandomRotationField = GetItemSpawnerField("_useRandomRotation"); private static readonly FieldInfo MaxItemsSpawnedField = GetItemSpawnerField("_maxItemsSpawned"); private static readonly FieldInfo SpawnInstantField = GetItemSpawnerField("_spawnInstant"); private static readonly FieldInfo OnlySpawnOnceField = GetItemSpawnerField("_onlySpawnOnce"); private static readonly FieldInfo SpawnInAirField = GetItemSpawnerField("_spawnInAir"); private static readonly FieldInfo SpawnDelayField = GetItemSpawnerField("_spawnDelay"); private static readonly FieldInfo SpawnCountField = GetItemSpawnerField("_spawnCount"); private static readonly FieldInfo SpawnBoxField = GetItemSpawnerField("_spawnBox"); private static Item _seagullPrefab; private static FieldInfo GetItemSpawnerField(string name) { return typeof(ItemSpawner).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic); } protected override void OnLagoonLoaded(Scene scene) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) GameObject val = SceneUtil.FindIslandHolder(scene); if ((Object)(object)val == (Object)null) { return; } Transform val2 = val.transform.Find("BirdSpawners/SeagullSpawner"); if ((Object)(object)val2 == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Wildlife] 'BirdSpawners/SeagullSpawner' marker not found; skipping bird spawner."); } } else { if (val2.childCount > 0) { return; } EnsureSeagullPrefab(); if (!((Object)(object)_seagullPrefab == (Object)null)) { try { CreateSpawnerFor(val2); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[Wildlife] Created seagull spawner."); } return; } catch (Exception arg) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)$"[Wildlife] Failed to create seagull spawner: {arg}"); } return; } } ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)"[Wildlife] 'Seagull' Item prefab not found in memory; skipping bird spawner."); } } } private static void CreateSpawnerFor(Transform marker) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0037: 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_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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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) GameObject val = new GameObject("SeagullItemSpawner"); val.transform.SetParent(marker, false); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; val.transform.localScale = Vector3.one; val.SetActive(false); BoxCollider val2 = val.AddComponent<BoxCollider>(); ((Collider)val2).isTrigger = true; val2.size = SpawnBoxSize; val2.center = Vector3.zero; ItemSpawner target = val.AddComponent<ItemSpawner>(); SetField(IsActiveField, target, true); SetField(ItemToSpawnField, target, _seagullPrefab); SetField(SpawnsDeadCreatureField, target, false); SetField(StartFrequencyForRadioField, target, 98); SetField(UseRandomRotationField, target, true); SetField(MaxItemsSpawnedField, target, 3); SetField(SpawnInstantField, target, true); SetField(OnlySpawnOnceField, target, false); SetField(SpawnInAirField, target, true); SetField(SpawnDelayField, target, 180f); SetField(SpawnCountField, target, 1); SetField(SpawnBoxField, target, val2); val.SetActive(true); } private static void SetField(FieldInfo field, object target, object value) { if (field == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Wildlife] ItemSpawner field layout changed; a bird spawner setting was skipped."); } } else { field.SetValue(target, value); } } private static void EnsureSeagullPrefab() { if ((Object)(object)_seagullPrefab != (Object)null) { return; } Item[] array = Resources.FindObjectsOfTypeAll<Item>(); foreach (Item val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name.IndexOf("Seagull", StringComparison.OrdinalIgnoreCase) >= 0) { _seagullPrefab = val; break; } } } } public class ClamSpawner : LagoonSceneComponent { private const string SpawnerZonesParentName = "CreatureSpawners"; private const int MaxPerZone = 3; private const float SpawnDelaySeconds = 20f; private static readonly FieldInfo IsActiveField = GetItemSpawnerField("_isActive"); private static readonly FieldInfo ItemToSpawnField = GetItemSpawnerField("_itemToSpawn"); private static readonly FieldInfo SpawnsDeadCreatureField = GetItemSpawnerField("_spawnsDeadCreature"); private static readonly FieldInfo UseRandomRotationField = GetItemSpawnerField("_useRandomRotation"); private static readonly FieldInfo MaxItemsSpawnedField = GetItemSpawnerField("_maxItemsSpawned"); private static readonly FieldInfo SpawnInstantField = GetItemSpawnerField("_spawnInstant"); private static readonly FieldInfo OnlySpawnOnceField = GetItemSpawnerField("_onlySpawnOnce"); private static readonly FieldInfo SpawnInAirField = GetItemSpawnerField("_spawnInAir"); private static readonly FieldInfo SpawnDelayField = GetItemSpawnerField("_spawnDelay"); private static readonly FieldInfo SpawnCountField = GetItemSpawnerField("_spawnCount"); private static readonly FieldInfo SpawnBoxField = GetItemSpawnerField("_spawnBox"); private static Item _clamPrefab; private static FieldInfo GetItemSpawnerField(string name) { return typeof(ItemSpawner).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic); } protected override void OnLagoonLoaded(Scene scene) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown GameObject val = SceneUtil.FindIslandHolder(scene); if ((Object)(object)val == (Object)null) { return; } Transform val2 = val.transform.Find("CreatureSpawners"); if ((Object)(object)val2 == (Object)null) { return; } EnsureClamPrefab(); if ((Object)(object)_clamPrefab == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Wildlife] 'Clam' Item prefab not found in memory; skipping clam spawners."); } return; } int num = 0; foreach (Transform item in val2) { Transform val3 = item; if (val3.childCount > 0) { continue; } try { CreateSpawnerFor(val3); num++; } catch (Exception arg) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)$"[Wildlife] Failed to create clam spawner for '{((Object)val3).name}': {arg}"); } } } if (num > 0) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[Wildlife] Created {num} clam spawner(s) around the shoreline."); } } } private static void CreateSpawnerFor(Transform zone) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0037: 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_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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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) GameObject val = new GameObject("ClamItemSpawner"); val.transform.SetParent(zone, false); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; val.transform.localScale = Vector3.one; val.SetActive(false); BoxCollider val2 = val.AddComponent<BoxCollider>(); ((Collider)val2).isTrigger = true; val2.size = Vector3.one; val2.center = Vector3.zero; ItemSpawner target = val.AddComponent<ItemSpawner>(); SetField(IsActiveField, target, true); SetField(ItemToSpawnField, target, _clamPrefab); SetField(SpawnsDeadCreatureField, target, true); SetField(UseRandomRotationField, target, true); SetField(MaxItemsSpawnedField, target, 3); SetField(SpawnInstantField, target, true); SetField(OnlySpawnOnceField, target, false); SetField(SpawnInAirField, target, false); SetField(SpawnDelayField, target, 20f); SetField(SpawnCountField, target, 1); SetField(SpawnBoxField, target, val2); val.SetActive(true); } private static void SetField(FieldInfo field, object target, object value) { if (field == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Wildlife] ItemSpawner field layout changed; a clam spawner setting was skipped."); } } else { field.SetValue(target, value); } } private static void EnsureClamPrefab() { if ((Object)(object)_clamPrefab != (Object)null) { return; } Item[] array = Resources.FindObjectsOfTypeAll<Item>(); foreach (Item val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name.IndexOf("Clam", StringComparison.OrdinalIgnoreCase) >= 0) { _clamPrefab = val; break; } } } } } namespace BoomLagoon.Save { [HarmonyPatch(typeof(SaveManager), "SaveServer")] internal static class RecordSavedIslandPatch { [HarmonyPostfix] private static void Record() { ServerSaveObject curServerSave = SaveManager.CurServerSave; if (curServerSave != null && !((Object)(object)OnlineIslandManager.Instance == (Object)null)) { bool flag = CurrentIslandSync.IsLagoonIndex(OnlineIslandManager.CurIsland); LagoonSaveStore.WriteActiveMapId(curServerSave, flag ? "tournament_lagoon" : null); } } } internal static class LagoonSaveStore { [Serializable] private sealed class Record { public string activeMapId = string.Empty; public long lastSave; } private const string FolderName = "BoomLagoon"; private static string Folder => Path.Combine(Paths.ConfigPath, "BoomLagoon"); public static string ReadActiveMapId(ServerSaveObject save) { string text = PathFor(save?.Name); if (text == null || !File.Exists(text)) { return string.Empty; } try { Record record = JsonUtility.FromJson<Record>(File.ReadAllText(text)); if (record == null || string.IsNullOrEmpty(record.activeMapId)) { return string.Empty; } if (record.lastSave != save.LastSave) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)($"[Save] {Path.GetFileName(text)} records '{record.activeMapId}' as of save {record.lastSave}, " + $"but '{save.Name}' was last saved at {save.LastSave}; ignoring it.")); } return string.Empty; } return record.activeMapId; } catch (Exception arg) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)$"[Save] Couldn't read '{text}'; treating the save as being on a native island. {arg}"); } return string.Empty; } } public static void WriteActiveMapId(ServerSaveObject save, string mapId) { string text = PathFor(save?.Name); if (text == null) { return; } try { if (string.IsNullOrEmpty(mapId)) { if (File.Exists(text)) { File.Delete(text); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[Save] " + Path.GetFileName(text) + ": no custom island active; record cleared.")); } } return; } Directory.CreateDirectory(Folder); Record record = new Record { activeMapId = mapId, lastSave = save.LastSave }; File.WriteAllText(text, JsonUtility.ToJson((object)record, true)); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[Save] " + Path.GetFileName(text) + ": active island is '" + mapId + "'.")); } } catch (Exception arg) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)$"[Save] Couldn't update '{text}'. {arg}"); } } } private static string PathFor(string saveName) { string text = Sanitize(saveName); if (text != null) { return Path.Combine(Folder, text + ".json"); } return null; } private static string Sanitize(string saveName) { if (string.IsNullOrWhiteSpace(saveName)) { return null; } StringBuilder stringBuilder = new StringBuilder(saveName.Length); foreach (char c in saveName) { stringBuilder.Append((Array.IndexOf(Path.GetInvalidFileNameChars(), c) >= 0) ? '_' : c); } return stringBuilder.ToString(); } } [HarmonyPatch(typeof(SaveManager), "SelectServer")] internal static class SavedIslandRepair { private const byte FirstIsland = 0; [HarmonyPostfix] private static void Repair() { ServerSaveObject curServerSave = SaveManager.CurServerSave; if (curServerSave == null) { return; } byte spawnedIsland = curServerSave.SpawnedIsland; bool flag = string.Equals(LagoonSaveStore.ReadActiveMapId(curServerSave), "tournament_lagoon", StringComparison.Ordinal); byte index; Vector3 center; bool flag2 = CurrentIslandSync.TryLocate(out index, out center); if (flag && flag2) { if (spawnedIsland != index) { curServerSave.SpawnedIsland = index; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)($"[Save] '{curServerSave.Name}' was saved on Boom Lagoon, which is island {index} this session; " + $"retargeting it from {spawnedIsland}.")); } } } else if (flag || !IsResumable(spawnedIsland)) { FallBackToFirstIsland(curServerSave, spawnedIsland, flag ? "Boom Lagoon, which isn't registered this session" : null); } } private static bool IsResumable(byte island) { if (!CustomIslands.IsCustomIndex(island) && !IslandManager.IsMainIsland(island)) { return IslandManager.IsMiniIsland(island); } return true; } private static void FallBackToFirstIsland(ServerSaveObject save, byte saved, string reason) { int num = save.WorldItems?.Count ?? 0; save.WorldItems?.Clear(); save.HasUpdatedWorldPositions = true; save.SpawnedIsland = 0; save.HasMiniIsland = false; save.SpawnedMiniIsland = byte.MaxValue; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Save] '" + save.Name + "' was saved on " + (reason ?? $"island {saved}, which is not a main island this session") + "; " + $"resuming on island {(byte)0} instead and leaving {num} world item(s) belonging to the old " + "island behind. Money, unlocks, progression and everyone's inventory are unaffected.")); } } } [HarmonyPatch(typeof(OnlineIslandManager), "OnStartServer")] internal static class SpawnableIslandGuard { private const byte FirstIsland = 0; [HarmonyPostfix] private static void EnsureSpawnable(OnlineIslandManager __instance) { byte value = __instance._activeIsland.Value; byte value2 = __instance._curIsland.Value; if (!CanSatisfySpawnCheck(value)) { if (value == 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)($"[Spawn] Island {(byte)0} can't satisfy the spawn check either, so there is nothing " + "to fall back to. Is the game's build settings list intact?")); } return; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)($"[Spawn] Island {value} can never satisfy SpawnManager's spawn check -- no scene at that " + "build slot and no custom map registered for it -- so nobody would be able to spawn. " + $"Overwriting the current and active island with island {(byte)0}.")); } __instance._curIsland.Value = 0; __instance._activeIsland.Value = 0; DiscardOldIslandContents(); } else if (!CanSatisfySpawnCheck(value2)) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)($"[Spawn] Island {value} is loading normally, but the island to return to ({value2}) could never " + $"satisfy SpawnManager's spawn check; setting it to island {(byte)0}.")); } __instance._curIsland.Value = 0; } } private static bool CanSatisfySpawnCheck(byte island) { if (island + 1 >= SceneManager.sceneCountInBuildSettings) { return CustomIslands.IsCustomIndex(island); } return true; } private static void DiscardOldIslandContents() { ServerSaveObject curServerSave = SaveManager.CurServerSave; if (curServerSave == null) { return; } int num = curServerSave.WorldItems?.Count ?? 0; curServerSave.WorldItems?.Clear(); curServerSave.HasUpdatedWorldPositions = true; curServerSave.HasMiniIsland = false; curServerSave.SpawnedMiniIsland = byte.MaxValue; if (num > 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"[Spawn] Left {num} world item(s) belonging to the old island behind."); } } } } } namespace BoomLagoon.Ramps { public class RampBooster : LagoonSceneComponent { private const string MarkerName = "boost"; protected override void OnLagoonLoaded(Scene scene) { if (!Config.RampBoost.Enabled.Value) { return; } int num = 0; GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects(); foreach (GameObject val in rootGameObjects) { if ((Object)(object)val == (Object)null) { continue; } Transform[] componentsInChildren = val.GetComponentsInChildren<Transform>(true); foreach (Transform val2 in componentsInChildren) { if (!string.Equals(((Object)val2).name, "boost", StringComparison.OrdinalIgnoreCase) || (Object)(object)((Component)val2).GetComponent<BoostPad>() != (Object)null) { continue; } Collider component = ((Component)val2).GetComponent<Collider>(); if ((Object)(object)component == (Object)null || !component.isTrigger) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Ramp] '" + ((Object)val2).name + "' has no trigger collider; skipping (a boost marker needs a Collider with IsTrigger).")); } continue; } if (BundleTriggerRepair.EnsureEnabled(component)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[Ramp] Enabled disabled trigger collider on '" + ((Object)val2).name + "'.")); } } ((Component)val2).gameObject.AddComponent<BoostPad>(); num++; } } if (num > 0) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[Ramp] Wired {num} boost pad(s)."); } } } } [DisallowMultipleComponent] public class BoostPad : MonoBehaviour { private readonly HashSet<Collider> _boatColsInside = new HashSet<Collider>(); private void Awake() { Rigidbody val = ((Component)this).GetComponent<Rigidbody>(); if ((Object)(object)val == (Object)null) { val = ((Component)this).gameObject.AddComponent<Rigidbody>(); } val.isKinematic = true; val.useGravity = false; } private void OnTriggerEnter(Collider other) { if (!((Object)(object)other == (Object)null) && BoatManager.ColToBoat.TryGetValue(other, out var value) && !((Object)(object)value == (Object)null)) { _boatColsInside.RemoveWhere((Collider c) => (Object)(object)c == (Object)null); bool num = _boatColsInside.Count > 0; _boatColsInside.Add(other); if (!num) { TryBoost(value); } } } private void OnTriggerExit(Collider other) { if ((Object)(object)other != (Object)null) { _boatColsInside.Remove(other); } } private void TryBoost(Boat boat) { //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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_006d: Unknown result type (might be due to invalid IL or missing references) if (Config.RampBoost.Enabled.Value && ((NetworkBehaviour)boat).IsServerInitialized && (!Config.RampBoost.RequireDriver.Value || !((Object)(object)boat.Driver == (Object)null))) { Rigidbody hiddenPhysicsRig = boat.HiddenPhysicsRig; if (!((Object)(object)hiddenPhysicsRig == (Object)null)) { Vector3 dir = boat.Velocity; dir = ((((Vector3)(ref dir)).sqrMagnitude > 0.25f) ? ((Vector3)(ref dir)).normalized : ((Component)this).transform.forward); BoatMaxSpeedOverride.Apply(hiddenPhysicsRig, dir, Config.RampBoost.BoostSpeed.Value); } } } } [DisallowMultipleComponent] public class BoatMaxSpeedOverride : MonoBehaviour { private const float RestoreSpeedFactor = 1.05f; private const float MaxBoostSeconds = 6f; private const float RestoreSeconds = 0.5f; private Rigidbody _rig; private float _originalMax; private float _boostExpireTime; private bool _easing; private float _easeFromMax; private float _easeStartTime; public static void Apply(Rigidbody rig, Vector3 dir, float targetSpeed) { //IL_005b: 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) if (!((Object)(object)rig == (Object)null)) { BoatMaxSpeedOverride boatMaxSpeedOverride = ((Component)rig).GetComponent<BoatMaxSpeedOverride>(); if ((Object)(object)boatMaxSpeedOverride == (Object)null) { boatMaxSpeedOverride = ((Component)rig).gameObject.AddComponent<BoatMaxSpeedOverride>(); boatMaxSpeedOverride._rig = rig; boatMaxSpeedOverride._originalMax = rig.maxLinearVelocity; } float num = Mathf.Max(boatMaxSpeedOverride._originalMax, targetSpeed); rig.maxLinearVelocity = Mathf.Max(rig.maxLinearVelocity, num); rig.linearVelocity = ((Vector3)(ref dir)).normalized * num; boatMaxSpeedOverride._easing = false; boatMaxSpeedOverride._boostExpireTime = Time.time + 6f; } } private void FixedUpdate() { //IL_0023: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_rig == (Object)null) { Object.Destroy((Object)(object)this); } else if (!_easing) { Vector3 linearVelocity = _rig.linearVelocity; if (((Vector3)(ref linearVelocity)).magnitude <= _originalMax * 1.05f || Time.time >= _boostExpireTime) { _easing = true; _easeStartTime = Time.time; float originalMax = _originalMax; linearVelocity = _rig.linearVelocity; _easeFromMax = Mathf.Max(originalMax, ((Vector3)(ref linearVelocity)).magnitude); } } else { float num = (Time.time - _easeStartTime) / 0.5f; if (num >= 1f) { _rig.maxLinearVelocity = _originalMax; Object.Destroy((Object)(object)this); } else { _rig.maxLinearVelocity = Mathf.Lerp(_easeFromMax, _originalMax, num); } } } } } namespace BoomLagoon.Net { public class HostSettings : MonoBehaviour { private const string LobbyKey = "boomlagoon_host"; private const int FormatVersion = 1; private const float PollSeconds = 0.5f; private static Dictionary<string, string> _received; private static string _lastReceivedRaw; private static bool _hostGreeneryKilled; private static Object _hostSessionServer; private static string _lastWritten; private static CSteamID _lastWrittenLobby = CSteamID.Nil; private float _nextPoll; public static bool IsHost { get { if ((Object)(object)Server.Instance != (Object)null) { return ((NetworkBehaviour)Server.Instance).IsServerInitialized; } return false; } } public static bool ShootToDetonate => GetBool("shoot", Config.FatManBomb.ShootToDetonate.Value); public static bool KillGreenery => GetBool("leafs", Config.FatManBomb.BlastKillGreenery.Value); public static bool BomberEnabled => GetBool("bomber", Config.Bomber.Enabled.Value); public static float RaidIntervalSeconds => GetFloat("interval", Config.Bomber.RaidIntervalSeconds.Value); public static float LapSeconds => GetFloat("lap", Config.Bomber.LapSeconds.Value); public static float Altitude => GetFloat("alt", Config.Bomber.Altitude.Value); public static float SirenLeadSeconds => GetFloat("lead", Config.Bomber.SirenLeadSeconds.Value); public static bool ShopRespectsProgression => GetBool("shopprog", Config.Shop.RespectIslandProgression.Value); public static bool GreeneryKilled { get { if (IsHost) { ResetIfNewHostSession(); return _hostGreeneryKilled; } return GetBool("leafsdead", local: false); } } public static void MarkGreeneryKilled() { if (IsHost) { ResetIfNewHostSession(); _hostGreeneryKilled = true; } } private void Update() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_005e: 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) if (Time.unscaledTime < _nextPoll) { return; } _nextPoll = Time.unscaledTime + 0.5f; CSteamID currentLobbyID = SteamManager.CurrentLobbyID; if (IsHost) { _received = null; _lastReceivedRaw = null; ResetIfNewHostSession(); if (currentLobbyID != CSteamID.Nil) { Publish(currentLobbyID); } } else if (currentLobbyID != CSteamID.Nil) { Receive(currentLobbyID); } else { _received = null; _lastReceivedRaw = null; } } private static void ResetIfNewHostSession() { if (!(_hostSessionServer == (Object)(object)Server.Instance)) { _hostSessionServer = (Object)(object)Server.Instance; _hostGreeneryKilled = false; } } private static void Publish(CSteamID lobby) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) string text = BuildPayload(); if (text == _lastWritten && lobby == _lastWrittenLobby) { return; } try { if (!SteamMatchmaking.SetLobbyData(lobby, "boomlagoon_host", text)) { return; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Sync] Failed to publish host settings: " + ex.Message)); } return; } _lastWritten = text; _lastWrittenLobby = lobby; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[Sync] Published host settings: " + text)); } } private static void Receive(CSteamID lobby) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) string lobbyData; try { lobbyData = SteamMatchmaking.GetLobbyData(lobby, "boomlagoon_host"); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Sync] Failed to read host settings: " + ex.Message)); } return; } if (lobbyData == _lastReceivedRaw) { return; } _lastReceivedRaw = lobbyData; Dictionary<string, string> dictionary = Parse(lobbyData); if (dictionary == null || !dictionary.TryGetValue("v", out var value) || value != 1.ToString(CultureInfo.InvariantCulture)) { _received = null; if (!string.IsNullOrEmpty(lobbyData)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[Sync] Host settings are from a different BoomLagoon version; using local config."); } } } else { _received = dictionary; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("[Sync] Using host settings: " + lobbyData)); } } } private static string BuildPayload() { StringBuilder stringBuilder = new StringBuilder(); Append(stringBuilder, "v", 1.ToString(CultureInfo.InvariantCulture)); Append(stringBuilder, "shoot", Config.FatManBomb.ShootToDetonate.Value ? "1" : "0"); Append(stringBuilder, "leafs", Config.FatManBomb.BlastKillGreenery.Value ? "1" : "0"); Append(stringBuilder, "leafsdead", _hostGreeneryKilled ? "1" : "0"); Append(stringBuilder, "bomber", Config.Bomber.Enabled.Value ? "1" : "0"); Append(stringBuilder, "interval", Config.Bomber.RaidIntervalSeconds.Value.ToString("R", CultureInfo.InvariantCulture)); Append(stringBuilder, "lap", Config.Bomber.LapSeconds.Value.ToString("R", CultureInfo.InvariantCulture)); Append(stringBuilder, "alt", Config.Bomber.Altitude.Value.ToString("R", CultureInfo.InvariantCulture)); Append(stringBuilder, "lead", Config.Bomber.SirenLeadSeconds.Value.ToString("R", CultureInfo.InvariantCulture)); Append(stringBuilder, "shopprog", Config.Shop.RespectIslandProgression.Value ? "1" : "0"); return stringBuilder.ToString(); } private static void Append(StringBuilder sb, string key, string value) { if (sb.Length > 0) { sb.Append(';'); } sb.Append(key).Append('=').Append(value); } private static Dictionary<string, string> Parse(string raw) { if (string.IsNullOrEmpty(raw)) { return null; } Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); string[] array = raw.Split(';'); foreach (string text in array) { int num = text.IndexOf('='); if (num > 0) { dictionary[text.Substring(0, num)] = text.Substring(num + 1); } } return dictionary; } private static bool GetBool(string key, bool local) { if (IsHost || _received == null || !_received.TryGetValue(key, out var value)) { return local; } return value == "1"; } private static float GetFloat(string key, float local) { if (IsHost || _received == null || !_received.TryGetValue(key, out var value)) { return local; } if (!float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return local; } return result; } } } namespace BoomLagoon.MapLoaderFixes { internal static class BakedInteractableRepair { private const string InteractableLayer = "Interactable"; private const string InteractableTag = "Interactable"; private const string OutlineField = "_modelsToOutline"; internal static void RestoreInteractionLayer(GameObject go) { if ((Object)(object)go == (Object)null) { return; } int num = LayerMask.NameToLayer("Interactable"); if (num >= 0) { go.layer = num; } try { go.tag = "Interactable"; } catch { } } internal static bool PruneOutlineList(Component owner) { if ((Object)(object)owner == (Object)null) { return false; } FieldInfo fieldInfo = AccessTools.Field(((object)owner).GetType(), "_modelsToOutline"); if (!(fieldInfo?.GetValue(owner) is GameObject[] array)) { return false; } int num = 0; GameObject[] array2 = array; for (int i = 0; i < array2.Length; i++) { if ((Object)(object)array2[i] != (Object)null) { num++; } } if (num == array.Length) { return false; } GameObject[] array3 = (GameObject[])(object)new GameObject[num]; int num2 = 0; array2 = array; foreach (GameObject val in array2) { if ((Object)(object)val != (Object)null) { array3[num2++] = val; } } fieldInfo.SetValue(owner, array3); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)string.Format("[MapFix] Repaired {0} on '{1}' ({2} -> {3}).", "_modelsToOutline", ((Object)owner.gameObject).name, array.Length, num)); } return true; } } internal static class BundleTriggerRepair { internal static bool EnsureEnabled(Collider trigger) { if ((Object)(object)trigger == (Object)null || trigger.enabled) { return false; } trigger.enabled = true; return true; } } public class CurrentIslandSync : LagoonSceneComponent { internal const string MapId = "tournament_lagoon"; private const string SpawnerName = "HTFML_IslandPosition_tournament_lagoon"; private static readonly FieldRef<IslandSpawner, byte> IslandIndexRef = AccessTools.FieldRefAccess<IslandSpawner, byte>("_islandIndex"); private static bool _located; private static byte _index; private static Vector3 _center; internal static bool TryLocate(out byte index, out Vector3 center) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (!_located) { IslandSpawner[] array = Resources.FindObjectsOfTypeAll<IslandSpawner>(); foreach (IslandSpawner val in array) { if ((Object)(object)val == (Object)null) { continue; } Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid() && string.Equals(((Object)val).name, "HTFML_IslandPosition_tournament_lagoon", StringComparison.Ordinal)) { _index = IslandIndexRef.Invoke(val); _center = ((Component)val).transform.position; _located = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Island] Boom Lagoon is island index {_index} at {_center}."); } break; } } } index = _index; center = _center; return _located; } internal static bool IsLagoonIndex(byte island) { if (TryLocate(out var index, out var _)) { return island == index; } return false; } internal static bool WithinLagoonZone(Vector3 pos) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (TryLocate(out var _, out var center)) { return SceneUtil.WithinZone(center, pos); } return false; } protected override void OnLagoonLoaded(Scene scene) { ((MonoBehaviour)this).StartCoroutine(SyncCurrentIsland()); } private static IEnumerator SyncCurrentIsland() { yield return null; while (IslandManager.IsLoading) { yield return null; } yield return null; if ((Object)(object)Server.Instance == (Object)null || !((NetworkBehaviour)Server.Instance).IsServerInitialized) { yield break; } OnlineIslandManager instance = OnlineIslandManager.Instance; if ((Object)(object)instance == (Object)null) { yield break; } if (!TryLocate(out var index, out var _)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Island] 'HTFML_IslandPosition_tournament_lagoon' not found; can't sync the current island."); } yield break; } byte value = instance._curIsland.Value; byte value2 = instance._activeIsland.Value; if (value != index || value2 != index) { instance._curIsland.Value = index; instance._activeIsland.Value = index; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[Island] Set the current island {value} -> {index}, active {value2} -> {index} (Boom Lagoon)."); } } } } [HarmonyPatch(typeof(IslandManager))] internal static class CustomIslandClassification { [HarmonyPostfix] [HarmonyPatch("IsMiniIsland")] private static void IsMiniIsland(byte islandId, ref bool __result) { if (__result && CustomIslands.IsCustomIndex(islandId)) { __result = false; } } [HarmonyPostfix] [HarmonyPatch("IsMainIsland")] private static void IsMainIsland(byte islandId, ref bool __result) { if (!__result && CustomIslands.IsCustomIndex(islandId)) { __result = true; } } } [HarmonyPatch(typeof(RadarUI), "Awake")] internal static class RadarUIAwakePatch { [HarmonyPostfix] private static void AddDotFixer(RadarUI __instance) { if ((Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).GetComponent<CustomIslandDots>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<CustomIslandDots>(); } } } public class CustomIslandDots : MonoBehaviour { private struct Entry { public string Id; public byte Index; public MapDot Dot; } private static class MapAssets { private struct Entry { public Sprite Icon; public bool HasColor; public Color Color; } [Serializable] private class MapManifest { public string id; public string radarIcon; public string radarColor; } private const string DefaultIconFile = "radar-icon.png"; private static readonly Dictionary<string, Entry> Cache = new Dictionary<string, Entry>(StringComparer.OrdinalIgnoreCase); public static Sprite Icon(string mapId) { return Resolve(mapId).Icon; } public static bool RadarColor(string mapId, out Color color) { //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) Entry entry = Resolve(mapId); color = entry.Color; return entry.HasColor; } private static Entry Resolve(string mapId) { if (string.IsNullOrEmpty(mapId)) { return default(Entry); } if (Cache.TryGetValue(mapId, out var value)) { return value; } Entry entry = Read(mapId); Cache[mapId] = entry; return entry; } private static Entry Read(string mapId) { //IL_0065: 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) Entry result = default(Entry); MapManifest manifest; string text = FindManifest(mapId, out manifest); if (text == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[RadarIcon] No map.json found for map '" + mapId + "'; keeping the plain dot.")); } return result; } Color color = default(Color); if (!string.IsNullOrWhiteSpace(manifest.radarColor) && ColorUtility.TryParseHtmlString(manifest.radarColor.Trim(), ref color)) { color.a = 1f; result.Color = color; result.HasColor = true; } string path = (string.IsNullOrEmpty(manifest.radarIcon) ? "radar-icon.png" : manifest.radarIcon); string path2 = Path.Combine(Path.GetDirectoryName(text) ?? ".", path); result.Icon = (File.Exists(path2) ? LoadSprite(path2, mapId) : null); if ((Object)(object)result.Icon == (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[RadarIcon] No radar icon found for map '" + mapId + "'; keeping the plain dot.")); } } return result; } private static Sprite LoadSprite(string path, string mapId) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_002a: Expected O, but got Unknown //IL_0081: 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) Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false) { name = "BoomLagoon_RadarIcon_" + mapId, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)61 }; if (!ImageConversion.LoadImage(val, File.ReadAllBytes(path), true)) { Object.Destroy((Object)(object)val); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[RadarIcon] '" + path + "' isn't a readable PNG; keeping the plain dot.")); } return null; } ((Texture)val).filterMode = (FilterMode)1; Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); ((Object)val2).name = ((Object)val).name; ((Object)val2).hideFlags = (HideFlags)61; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[RadarIcon] Loaded '" + path + "' for map '" + mapId + "'.")); } return val2; } private static string FindManifest(string mapId, out MapManifest manifest) { manifest = null; string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "Maps"); if (!Directory.Exists(path)) { return null; } string[] files = Directory.GetFiles(path, "map.json", SearchOption.AllDirectories); foreach (string text in files) { MapManifest mapManifest; try { mapManifest = JsonUtility.FromJson<MapManifest>(File.ReadAllText(text)); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[RadarIcon] Couldn't read '" + text + "': " + ex.Message)); } continue; } if (mapManifest != null && string.Equals(mapManifest.id, mapId, StringComparison.OrdinalIgnoreCase)) { manifest = mapManifest; return text; } } return null; } } private const string DotPrefix = "HTFML_Radar_"; private const float ScanInterval = 2f; private static readonly FieldRef<RadarUI, MapDot[]> IslandDotsRef = AccessTools.FieldRefAccess<RadarUI, MapDot[]>("_islandDots"); private static readonly FieldRef<RadarUI, bool> IsOnRef = AccessTools.FieldRefAccess<RadarUI, bool>("_isOn"); private static readonly FieldRef<MapDot, Image> DotImageRef = AccessTools.FieldRefAccess<MapDot, Image>("_dot"); private static readonly Action<RadarUI, MapDot, Vector3> UpdateDot = AccessTools.MethodDelegate<Action<RadarUI, MapDot, Vector3>>(AccessTools.Method(typeof(RadarUI), "UpdateDot", new Type[2] { typeof(MapDot), typeof(Vector3) }, (Type[])null), (object)null, true); private static readonly Dictionary<string, Color> KnownColors = new Dictionary<string, Color>(); private readonly List<Entry> _entries = new List<Entry>(); private RadarUI _radar; private float _nextScan; private void Awake() { _radar = ((Component)this).GetComponent<RadarUI>(); if ((Object)(object)_radar == (Object)null) { Object.Destroy((Object)(object)this); } else if (UpdateDot == null || IslandDotsRef == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Radar] RadarUI has changed shape; leaving custom island dots alone."); } Object.Destroy((Object)(object)this); } } private void Update() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) if (Time.unscaledTime >= _nextScan) { _nextScan = Time.unscaledTime + 2f; EnsureDots(); } if (_entries.Count == 0 || !IsOnRef.Invoke(_radar)) { return; } for (int num = _entries.Count - 1; num >= 0; num--) { Entry entry = _entries[num]; if (TryGetPosition(entry.Index, entry.Id, out var position)) { try { UpdateDot(_radar, entry.Dot, position); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Radar] Dropping the '" + entry.Id + "' dot on " + ((Object)this).name + ": " + ex.Message)); } _entries.RemoveAt(num); } } } } private void EnsureDots() { List<CustomIslands.Entry> all = CustomIslands.All; if (all.Count == 0) { return; } MapDot[] array = IslandDotsRef.Invoke(_radar); if (array == null || array.Length == 0) { return; } MapDot val = FirstDot(array); if (val == null) { return; } int num = 0; foreach (CustomIslands.Entry item in all) { num = Mathf.Max(num, item.Index + 1); } if (num > array.Length) { Array.Resize(ref array, num); } bool flag = num > IslandDotsRef.Invoke(_radar).Length; foreach (CustomIslands.Entry item2 in all) { if (Known(item2.Index)) { continue; } MapDot val2 = array[item2.Index]; if (val2 != null) { if (IsLoaderDot(val2, item2.Id)) { Remember(item2.Id, val2); ApplyIcon(DotImageRef.Invoke(val2), item2.Id); _entries.Add(new Entry { Id = item2.Id, Index = item2.Index, Dot = val2 }); } continue; } MapDot val3 = CloneDot(val, "HTFML_Radar_" + item2.Id, item2.Id); if (val3 != null) { array[item2.Index] = val3; flag = true; _entries.Add(new Entry { Id = item2.Id, Index = item2.Index, Dot = val3 }); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Radar] Added the '{item2.Id}' dot (index {item2.Index}) to {((Object)this).name}."); } } } if (flag) { IslandDotsRef.Invoke(_radar) = array; } } private bool Known(byte index) { foreach (Entry entry in _entries) { if (entry.Index == index) { return true; } } return false; } private static MapDot CloneDot(MapDot template, string name, string mapId) { //IL_0056: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00be: 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_00d2: Expected O, but got Unknown Image val = DotImageRef.Invoke(template); if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).transform.parent == (Object)null) { return null; } GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent, false); ((Object)val2).name = name; val2.transform.localPosition = ((Component)val).transform.localPosition; val2.transform.localRotation = ((Component)val).transform.localRotation; val2.transform.localScale = ((Component)val).transform.localScale; Image component = val2.GetComponent<Image>(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val2); return null; } if (MapAssets.RadarColor(mapId, out var color) || KnownColors.TryGetValue(mapId, out color)) { ((Graphic)component).color = color; } ApplyIcon(component, mapId); MapDot val3 = new MapDot(); DotImageRef.Invoke(val3) = component; val3.Hide(); val2.SetActive(((Component)val).gameObject.activeSelf); return val3; } private static void ApplyIcon(Image image, string mapId) { if (!((Object)(object)image == (Object)null)) { Sprite val = MapAssets.Icon(mapId); if (!((Object)(object)val == (Object)null) && !((Object)(object)image.sprite == (Object)(object)val)) { image.sprite = val; image.type = (Type)0; image.preserveAspect = true; } } } private static bool IsLoaderDot(MapDot dot, string mapId) { Image val = DotImageRef.Invoke(dot); if ((Object)(object)val != (Object)null) { return string.Equals(((Object)((Component)val).gameObject).name, "HTFML_Radar_" + mapId, StringComparison.OrdinalIgnoreCase); } return false; } private static void Remember(string mapId, MapDot dot) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) Image val = DotImageRef.Invoke(dot); if ((Object)(object)val != (Object)null) { KnownColors[mapId] = ((Graphic)val).color; } } private static MapDot FirstDot(MapDot[] dots) { foreach (MapDot val in dots) { if (val != null && (Object)(object)DotImageRef.Invoke(val) != (Object)null) { return val; } } return null; } private static bool TryGetPosition(byte index, string mapId, out Vector3 position) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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_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) if (CustomIslands.TryGetIslandInfo(index, out var info)) { position = info.IslandPosition; return true; } foreach (CustomIslands.Entry item in CustomIslands.All) { if (item.Index == index && string.Equals(item.Id, mapId, StringComparison.Ordinal)) { if ((Object)(object)item.Anchor == (Object)null) { break; } position = item.Anchor.position; return true; } } position = Vector3.zero; return false; } } internal static class CustomIslands { internal struct Entry { public string Id; public byte Index; public Transform Anchor; } public const string SpawnerPrefix = "HTFML_IslandPosition_"; private const float ScanInterval = 2f; private static readonly FieldRef<IslandSpawner, byte> IslandIndexRef = AccessTools.FieldRefAccess<IslandSpawner, byte>("_islandIndex"); private static readonly FieldRef<IslandManager> InstanceRef = Reflect.StaticField<IslandManager>(typeof(IslandManager), "_instance"); private static readonly FieldRef<IslandManager, IslandInfo[]> IslandInfosRef = Reflect.Field<IslandManager, IslandInfo[]>("_islandInfos"); private static readonly List<Entry> Known = new List<Entry>(); private static float _nextScan; public static List<Entry> All { get { Refresh(); return Known; } } public static bool IsCustomIndex(byte index) { foreach (Entry item in All) { if (item.Index == index) { return true; } } return false; } public static bool TryGetIslandInfo(int index, out IslandInfo info) { info = null; if (InstanceRef == null || IslandInfosRef == null) { return false; } IslandManager val = InstanceRef.Invoke(); if ((Object)(object)val == (Object)null) { return false; } IslandInfo[] array = IslandInfosRef.Invoke(val); if (array == null || index < 0 || index >= array.Length) { return false; } info = array[index]; return info != null; } private static void Refresh() { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) if (Time.unscaledTime < _nextScan) { return; } _nextScan = Time.unscaledTime + 2f; foreach (Entry item in Known) { if (!((Object)(object)item.Anchor != (Object)null)) { Known.Clear(); break; } } if (Known.Count > 0) { return; } IslandSpawner[] array = Resources.FindObjectsOfTypeAll<IslandSpawner>(); foreach (IslandSpawner val in array) { if (!((Object)(object)val == (Object)null)) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid() && ((Object)val).name.StartsWith("HTFML_IslandPosition_", StringComparison.Ordinal)) { Known.Add(new Entry { Id = ((Object)val).name.Substring("HTFML_IslandPosition_".Length), Index = IslandIndexRef.Invoke(val), Anchor = ((Component)val).transform }); } } } if (Known.Count > 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Island] Tracking {Known.Count} custom island(s) from HTFMapLoader."); } } } } [HarmonyPatch(typeof(SpawnManager), "TryGetInitialPlayerSpawn")] internal static class InitialSpawnFix { private static readonly FieldRef<Transform> MainPlayerSpawnPointRef = Reflect.StaticField<Transform>(typeof(SpawnManager), "_mainPlayerSpawnPoint"); private static bool _logged; [HarmonyPostfix] private static void ResolveOnCustomMap(ref Vector3 position, ref float rotation, ref bool __result) { //IL_0074: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_00fc: Unknown result type (might be due to invalid IL or missing references) if (__result || MainPlayerSpawnPointRef == null || IslandManager.IsLoading || !IslandManager.IsInitialized || (Object)(object)OnlineIslandManager.Instance == (Object)null || !((NetworkBehaviour)OnlineIslandManager.Instance).IsClientInitialized || OnlineIslandManager.CurMini != byte.MaxValue || (Object)(object)Island.MiniIsland != (Object)null || (Object)(object)Island.CurIsland == (Object)null || !CustomIslands.IsCustomIndex(OnlineIslandManager.CurIsland) || !SceneUtil.IsBundleScene(((Component)Island.CurIsland).gameObject.scene)) { return; } Transform val = MainPlayerSpawnPointRef.Invoke(); if ((Object)(object)val == (Object)null || ((Component)val).gameObject.scene != ((Component)Island.CurIsland).gameObject.scene) { return; } position = val.position; rotation = val.eulerAngles.y; __result = true; if (!_logged) { _logged = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Spawn] Resolving spawns on custom island {OnlineIslandManager.CurIsland} at {position}."); } } } } internal sealed class IslandLoadCompletion : MonoBehaviour { private const float SettleTimeout = 15f; private static readonly Action<bool> SetIsInitialized = BoolSetter("IsInitialized"); private static readonly FieldRef<Action> FirstIslandLoadedRef = Reflect.StaticField<Action>(typeof(IslandManager), "OnFirstIslandLoaded"); private void OnEnable() { SceneManager.sceneLoaded += HandleSceneLoaded; } private void OnDisable() { SceneManager.sceneLoaded -= HandleSceneLoaded; } private void HandleSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_0008: 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) if (SetIsInitialized != null && SceneUtil.IsBundleScene(scene)) { ((MonoBehaviour)this).StartCoroutine(Complete(scene)); } } private static IEnumerator Complete(Scene scene) { //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 (object)new WaitForFixedUpdate(); yield return (object)new WaitForFixedUpdate(); float deadline = Time.unscaledTime + 15f; while (!IsLive(scene)) { if (!((Scene)(ref scene)).IsValid() || !((Scene)(ref scene)).isLoaded) { yield break; } if (Time.unscaledTime > deadline) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Island] Bundle scene '" + ((Scene)(ref scene)).name + "' never published an Island; leaving IslandManager.IsInitialized alone. If this is a custom map, nobody will spawn on it.")); } yield break; } yield return null; } if (!IslandManager.IsInitialized) { SetIsInitialized(obj: true); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[Island] '" + ((Scene)(ref scene)).name + "' is live; IslandManager.IsInitialized set (HTFMapLoader bypasses the native load that would have done it, which is what left players bodiless).")); } RaiseFirstIslandLoaded(); } HonourPendingTeleport(); } private static bool IsLive(Scene scene) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Island.CurIsland != (Object)null) { return ((Component)Island.CurIsland).gameObject.scene == scene; } return false; } private static void RaiseFirstIslandLoaded() { FieldRef<Action> firstIslandLoadedRef = FirstIslandLoadedRef; Action action = ((firstIslandLoadedRef != null) ? firstIslandLoadedRef.Invoke() : null); if (action == null) { return; } try { action(); } catch (Exception arg) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"[Island] An OnFirstIslandLoaded handler threw: {arg}"); } } } private static void HonourPendingTeleport() { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: 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_0037: 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_0071: Unknown result type (might be due to invalid IL or missing references) if (!OnlineIslandManager.TeleportPlayers) { return; } bool teleportToMini = OnlineIslandManager.TeleportToMini; if ((Object)(object)Server.Instance != (Object)null && ((NetworkBehaviour)Server.Instance).IsServerInitialized && (Object)(object)Player.LocalPlayer != (Object)null) { Vector3 val = (teleportToMini ? SpawnManager.MiniSpawnPos : SpawnManager.MainSpawnPos); float num = (teleportToMini ? SpawnManager.MiniSpawnRot : SpawnManager.MainSpawnRot); foreach (Player alivePlayer in PlayerManager.AlivePlayers) { Server.Instance.TeleportPlayer(alivePlayer, val, num); } } if ((Object)(object)Player.LocalPlayer != (Object)null && (Object)(object)BoatManager.Instance != (Object)null) { Vector3 position = Player.LocalPlayer.Transform.position; BoatManager.Instance.TryMoveBoat(SpawnManager.GetBoatSpawnPos(position), SpawnManager.GetBoatSpawnRot(position)); } OnlineIslandManager.ToggleTeleportPlayers(false, false); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[Island] Honoured the pending teleport request and cleared it."); } } private static Action<bool> BoolSetter(string property) { MethodInfo methodInfo = AccessTools.PropertySetter(typeof(IslandManager), property); if (methodInfo == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Reflect] IslandManager." + property + " has no setter; custom-map load completion stands down.")); } return null; } return (Action<bool>)Delegate.CreateDelegate(typeof(Action<bool>), methodInfo); } } [HarmonyPatch(typeof(IslandManager), "GetCurIslandMovedDiff")] internal static class IslandRebase { [HarmonyPrefix] private static bool NoDiffOnCustomIsland(ref Vector3 __result) { //IL_004a: 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) if ((Object)(object)OnlineIslandManager.Instance == (Object)null) { return true; } byte curIsland = OnlineIslandManager.CurIsland; if (!CustomIslands.IsCustomIndex(curIsland) && CustomIslands.TryGetIslandInfo(curIsland, out var _)) { return true; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Island] Restoring legacy world items on island {curIsland} without a moved-position rebase."); } __result = Vector3.zero; return false; } } internal static class MapLoaderIdleSkip { private const string LoaderPluginType = "HTFMapLoader.Plugin"; private static FieldRef<object, bool> _registeredRef; private static FieldRef<object, bool> _verifiedRef; private static bool _loggedIdle; internal static void Install(Harmony harmony) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown Type type = AccessTools.TypeByName("HTFMapLoader.Plugin"); if (type == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[MapLoader] HTFMapLoader.Plugin not found; leaving its Update alone."); } return; } try { _registeredRef = AccessTools.FieldRefAccess<bool>(type, "_registered"); _verifiedRef = AccessTools.FieldRefAccess<bool>(type, "_verifiedRegisteredTriggers"); harmony.Patch((MethodBase)AccessTools.Method(type, "Update", (Type[])null, (Type[])null), new HarmonyMethod(AccessTools.Method(typeof(MapLoaderIdleSkip), "SkipWhenIdle", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { _registeredRef = null; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[MapLoader] HTF Map Loader has changed shape; leaving its Update alone: " + ex.Message)); } return; } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"[MapLoader] HTF Map Loader's per-frame radar scan will be skipped once its maps are registered."); } } [HarmonyPriority(200)] private static bool SkipWhenIdle(object __instance) { if (_registeredRef == null) { return true; } if (!_registeredRef.Invoke(__instance) || !_verifiedRef.Invoke(__instance)) { return true; } if (!_loggedIdle) { _loggedIdle = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[MapLoader] Maps registered and triggers verified; its Update is idle from here."); } } return false; } } } namespace BoomLagoon.Explosives { public class BomberManager : LagoonSceneComponent { protected override void OnLagoonLoaded(Scene scene) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) ((MonoBehaviour)this).StartCoroutine(Setup(scene)); } private IEnumerator Setup(Scene scene) { //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 null; yield return null; Transform val = FindByName(scene, "Bomber"); if ((Object)(object)val == (Object)null) { yield break; } Transform val2 = FindChild(val, "BombPlane"); Transform val3 = FindChild(val, "ABomb"); Transform val4 = FindByName(scene, "AirRaidSiren"); AudioSource val5 = (((Object)(object)val4 != (Object)null) ? ((Component)val4).GetComponent<AudioSource>() : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Bomber] 'Bomber' is missing its 'BombPlane' or 'ABomb' child; no air raids."); } yield break; } if ((Object)(object)val5 != (Object)null) { GameAudio.Route(val5); } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[Bomber] No AudioSource on 'AirRaidSiren'; air raids will run without the siren."); } } BomberRaid bomberRaid = ((Component)val).GetComponent<BomberRaid>(); if ((Object)(object)bomberRaid == (Object)null) { bomberRaid = ((Component)val).gameObject.AddComponent<BomberRaid>(); } bomberRaid.Init(((Component)val).GetComponent<Animation>(), val2, val3, val5); } private static Transform FindByName(Scene scene, string name) { GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects(); foreach (GameObject val in rootGameObjects) { if (!((Object)(object)val == (Object)null)) { Transform val2 = FindChild(val.transform, name); if ((Object)(object)val2 != (Object)null) { return val2; } } } return null; } private static Transform FindChild(Transform root, string name) { Transform[] componentsInChildren = ((Component)root).GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if (string.Equals(((Object)val).name, name, StringComparison.OrdinalIgnoreCase)) { return val; } } return null; } } public class BomberRaid : MonoBehaviour { private const float NoseDownSeconds = 3f; private const float MissedRaidGraceSeconds = 5f; private const float FallGiveUpDepth = 100f; private const float MaxImpactHoldSeconds = 2f; private Animation _anim; private AnimationState _lap; private Transform _plane; private Transform _bomb; private AudioSource _siren; private Vector3 _bayLocalPos; private Quaternion _bayLocalRot; private Rigidbody _rig; private Renderer[] _bombRenderers; private long _nextRaid = -1L; private bool _sirenStarted; private int _shownEnabled = -1; private Vector3 _basePosition; private float _bakedAltitude; private float _appliedAltitude = float.NaN; private bool _holding; private float _holdDeadline; private int _releaseActivationCount; private float _activationSeenAt = -1f; private bool _falling; private float _fallStartTime; private Quaternion _fallStartRot; private Quaternion _noseDownRot; private FatManBombTarget _fallTarget; private float _impactY; private FatManBombTarget _measuredTarget; private Bounds _targetBounds; private const float HudZoneRadius = 240f; private const float HudWarnScale = 1.25f; private const string HudIncomingText = "BOMB INCOMING"; private BombTimerHud _hud; private Camera _hudCamera; private string _hudText; private int _hudSeconds = -1; public void Init(Animation anim, Transform plane, Transform bomb, AudioSource siren) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) _plane = plane; _bomb = bomb; _siren = siren; _bayLocalPos = bomb.localPosition; _bayLocalRot = bomb.localRotation; _bombRenderers = ((Component)bomb).GetComponentsInChildren<Renderer>(true); _bakedAltitude = plane.position.y; _basePosition = ((Component)this).transform.position; _appliedAltitude = float.NaN; ApplyAltitude(); _anim = anim; if ((Object)(object)_anim != (Object)null && (Object)(object)_anim.clip != (Object)null) { _anim.cullingType = (AnimationCullingType)0; _anim.Play(((Object)_anim.clip).name); _lap = _anim[((Object)_anim.clip).name]; _lap.speed = 0f; _lap.wrapMode = (WrapMode)2; } else { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Bomber] No BomberCircle clip on the Bomber's Animation; the plane won't circle."); } } MeasureTarget(FindTarget()); _nextRaid = -1L; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)($"[Bomber] Ready: raid every {RaidPeriod():F0}s, lap {LapSeconds():F0}s, altitude {HostSettings.Altitude:F0}m, " + "siren " + (((Object)(object)_siren != (Object)null) ? "found" : "MISSING") + ".")); } } private void Update() { if ((Object)(object)_bomb == (Object)null) { return; } bool bomberEnabled = HostSettings.BomberEnabled; if ((bomberEnabled ? 1 : 0) != _shownEnabled) { SetBomberActive(bomberEnabled); } if (bomberEnabled) { ApplyAltitude(); double num = NetTime(); double num2 = LapSeconds(); if ((TrackedReference)(object)_lap != (TrackedReference)null) { double num3 = num / num2; _lap.normalizedTime = (float)(num3 - Math.Floor(num3)); } TickSchedule(num); if ((_falling || _holding) && _activationSeenAt < 0f && (Object)(object)_fallTarget != (Object)null && _fallTarget.ActivationCount != _releaseActivationCount) { _activationSeenAt = Time.time; } if (_falling) { TickFall(); } else if (_holding) { TickHold(); } } } private void SetBomberActive(bool enabled) { _shownEnabled = (enabled ? 1 : 0); _nextRaid = -1L; _sirenStarted = false; if (!enabled) { if (_falling || _holding) { ResetBomb(); } if ((Object)(object)_siren != (Object)null) { _siren.Stop(); } } if ((Object)(object)_plane != (Object)null) { ((Component)_plane).gameObject.SetActive(enabled); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)(enabled ? "[Bomber] Air raids on." : "[Bomber] Air raids off (host setting); bomber hidden.")); } } private void ApplyAltitude() { //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) //IL_0041: 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) if (!_falling && !_holding) { float altitude = HostSettings.Altitude; if (altitude != _appliedAltitude) { _appliedAltitude = altitude; ((Component)this).transform.position = _basePosition + Vector3.up * (altitude - _bakedAltitude); } } } private void FixedUpdate() { //IL_0036: 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_0051: Unknown result type (might be due to invalid IL or missing references) if (_falling && !((Object)(object)_rig == (Object)null)) { float num = Mathf.Clamp01((Time.time - _fallStartTime) / 3f); _rig.MoveRotation(Quaternion.Slerp(_fallStartRot, _noseDownRot, Mathf.SmoothStep(0f, 1f, num))); } } private void TickSchedule(double now) { double num = RaidPeriod(); double num2 = HostSettings.SirenLeadSeconds; double num3 = (double)_nextRaid * num - now; if (_nextRaid < 0 || num3 < -5.0 || num3 > num + num2) { _nextRaid = (long)Math.Ceiling((now + num2) / num); _sirenStarted = false; } double num4 = (double)_nextRaid * num; if (!_sirenStarted && now >= num4 - num2) { _sirenStarted = true; StartSiren(); } if (now >= num4) { _nextRaid++; _sirenStarted = false; ReleaseBomb(); } } private void StartSiren() { if ((Object)(object)_siren != (Object)null && (Object)(object)_siren.clip != (Object)null) { _siren.volume = Config.Bomber.SirenVolume.Value; _siren.Play(); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Bomber] Air raid siren! ABomb drops in {HostSettings.SirenLeadSeconds:F0}s."); } if (IsHost()) { FindTarget()?.EnsureBombForRaid(); } } private void ReleaseBomb() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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) //IL_00ff: 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_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: 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_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: 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_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) if (_falling || _holding) { ResetBomb(); } _fallTarget = FindTarget(); _releaseActivationCount = (((Object)(object)_fallTarget != (Object)null) ? _fallTarget.ActivationCount : 0); _activationSeenAt = -1f; Vector3 position = _bomb.position; Quaternion rotation = _bomb.rotation; MeasureTarget(_fallTarget); if ((Object)(object)_fallTarget != (Object)null && (Object)(object)_fallTarget == (Object)(object)_measuredTarget) { Bounds targetBounds = _targetBounds; Bounds val = BombBounds(); Vector3 center = ((Bounds)(ref val)).center; position.x += ((Bounds)(ref targetBounds)).center.x - center.x; position.z += ((Bounds)(ref targetBounds)).center.z - center.z; _impactY = ((Bounds)(ref targetBounds)).max.y; } else { _impactY = WaterManager.GetWaterHeight(position); } _bomb.SetParent(((Component)this).transform, true); _bomb.SetPositionAndRotation(position, rotation); SetBombVisible(visible: true); _rig = ((Component)_bomb).GetComponent<Rigidbody>(); if ((Object)(object)_rig == (Object)null) { _rig = ((Component)_bomb).gameObject.AddComponent<Rigidbody>(); } _rig.isKinematic = false; _rig.useGravity = true; _rig.interpolation = (RigidbodyInterpolation)1; _rig.linearDamping = 0f; _rig.angularDamping = 0f; _rig.linearVelocity = Vector3.zero; _rig.angularVelocity = Vector3.zero; Vector3 val2 = rotation * Vector3.left; _fallStartRot = rotation; _noseDownRot = Quaternion.FromToRotation(val2, Vector3.down) * rotation; _fallStartTime = Time.time; _falling = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Bomber] ABomb released at {position} (impact at y={_impactY:F1})."); } } private void TickFall() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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) Bounds val = BombBounds(); if (((Bounds)(ref val)).min.y <= _impactY) { OnImpact(); } else if (_bomb.position.y < _impactY - 100f) { ResetBomb(); } } private void OnImpact() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) if (IsHost()) { if ((Object)(object)_fallTarget != (Object)null && _fallTarget.TryDetonate(null)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[Bomber] ABomb hit the FatManBomb; detonating."); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[Bomber] ABomb landed but there was no FatManBomb to detonate."); } } } _falling = false; if ((Object)(object)_rig != (Object)null) { _rig.linearVelocity = Vector3.zero; _rig.angularVelocity = Vector3.zero; _rig.isKinematic = true; _rig.useGravity = false; } _holding = true; _holdDeadline = Time.time + 2f; TickHold(); } private void TickHold() { float num = Config.FatManBomb.FlashDurationSeconds.Value * 0.5f; if ((_activationSeenAt >= 0f) ? (Time.time >= _activationSeenAt + num) : (Time.time >= _holdDeadline)) { ResetBomb(); } } private void ResetBomb() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //I