using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Crest; using HarmonyLib; using RandomEncounters.API; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("RandomEncounters")] [assembly: AssemblyDescription("https://github.com/bryon82/SailwindRandomEncounters")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("raddude")] [assembly: AssemblyProduct("RandomEncounters")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("06b0b4cd-89d7-4ff2-8dc9-3eaa535b8d99")] [assembly: AssemblyFileVersion("2.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.0.0.0")] [module: UnverifiableCode] namespace RandomEncounters { internal class SeaLifeMod { private static List _whaleSpawns; private static Type _finWhaleAIType; private static Type _effectControllerType; private static FastInvokeHandler _triggerRandomAnimation; private static Component _whale0Ai; private static AssetBundle _assetBundle; private static AudioClip[] _blowholeSounds; private static AudioClip[] _breachSplashSounds; private static AudioClip[] _breachEmergeSounds; private static AudioClip[] _tailSplashSounds; private static bool _allSoundsLoaded; private static int _groupsCompleted; private const int TOTAL_GROUPS = 4; private const float MAX_DISTANCE = 650f; internal static int ActiveWhales { get; set; } internal static bool WhalesReady { get; set; } public static void PatchMod() { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown ((MonoBehaviour)RE_Plugin.SeaLifeModPluginInstance).StopAllCoroutines(); _finWhaleAIType = AccessTools.TypeByName("FinWhaleAI"); _effectControllerType = AccessTools.TypeByName("EffectController"); MethodInfo methodInfo = AccessTools.Method(_finWhaleAIType, "TriggerRandomAnimation", (Type[])null, (Type[])null); _triggerRandomAnimation = MethodInvoker.GetHandler(methodInfo, false); string[] array = new string[3] { "FindPlayer", "CheckDistanceToPlayer", "SetRandomScale" }; string[] array2 = array; foreach (string text in array2) { MethodInfo methodInfo2 = AccessTools.Method(_finWhaleAIType, text, (Type[])null, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(SeaLifeModPatches), "DoNotRun", (Type[])null, (Type[])null); RE_Plugin.HarmonyInstance.Patch((MethodBase)methodInfo2, new HarmonyMethod(methodInfo3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } MethodInfo methodInfo4 = AccessTools.Method(_effectControllerType, "LoadSounds", (Type[])null, (Type[])null); MethodInfo methodInfo5 = AccessTools.Method(typeof(SeaLifeModPatches), "DoNotRun", (Type[])null, (Type[])null); RE_Plugin.HarmonyInstance.Patch((MethodBase)methodInfo4, new HarmonyMethod(methodInfo5), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((MonoBehaviour)RE_Plugin.Instance).StartCoroutine(LoadSoundsAsync()); ((MonoBehaviour)RE_Plugin.Instance).StartCoroutine(InstantiateWhales()); } private static IEnumerator InstantiateWhales() { if ((Object)(object)RE_Plugin.SeaLifeModPluginInstance == (Object)null) { yield break; } GameObject whalePrefab = RE_Plugin.SeaLifeModPluginInstance.GetPrivateField("animalPrefab"); _whaleSpawns = new List(); yield return (object)new WaitUntil((Func)(() => (Object)(object)Refs.shiftingWorld != (Object)null && _allSoundsLoaded)); for (int i = 0; i < 5; i++) { GameObject whale = Object.Instantiate(whalePrefab, Refs.shiftingWorld); Component ai = whale.AddComponent(_finWhaleAIType); if (i == 0) { _whale0Ai = ai; } Component effectController = whale.AddComponent(_effectControllerType); effectController.SetPrivateField("blowholeSounds", _blowholeSounds); effectController.SetPrivateField("breachSplashSounds", _breachSplashSounds); effectController.SetPrivateField("breachEmergeSounds", _breachEmergeSounds); effectController.SetPrivateField("tailSplashSounds", _tailSplashSounds); whale.transform.position = Vector3.zero; whale.gameObject.SetActive(false); _whaleSpawns.Add(whale); } WhalesReady = true; } internal static void SpawnWhale(int i, Vector3 spawnPosition) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) RE_Plugin.LogDebug("Spawning FinWhale"); float num = Random.Range(0.8f, 1.4f); int num2 = Random.Range(0, 360); GameObject val = _whaleSpawns[i]; Transform transform = val.transform; transform.position = spawnPosition; transform.rotation = Quaternion.Euler(0f, (float)num2, 0f); transform.localScale = new Vector3(num, num, num); val.SetActive(true); ActiveWhales++; } internal static void TriggerEntranceAnimation() { _triggerRandomAnimation.Invoke((object)_whale0Ai, Array.Empty()); } internal static void CheckWhaleDistance() { //IL_002f: 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) if (ActiveWhales == 0) { return; } foreach (GameObject whaleSpawn in _whaleSpawns) { float num = Vector3.Distance(whaleSpawn.transform.position, ((Component)Refs.observerMirror).transform.position); if (whaleSpawn.activeInHierarchy && num > 650f) { RE_Plugin.LogDebug("Removing FinWhale"); whaleSpawn.SetActive(false); ActiveWhales--; } } } private static IEnumerator LoadSoundsAsync() { _groupsCompleted = 0; _allSoundsLoaded = false; _assetBundle = RE_Plugin.SeaLifeModPluginInstance.GetPrivateField("seaLifeBundle"); ((MonoBehaviour)RE_Plugin.Instance).StartCoroutine(LoadAudioClipsAsync("WhaleBlowMed", 6, delegate(AudioClip[] clips) { _blowholeSounds = clips; })); ((MonoBehaviour)RE_Plugin.Instance).StartCoroutine(LoadAudioClipsAsync("BreachSplashLarge", 5, delegate(AudioClip[] clips) { _breachSplashSounds = clips; })); ((MonoBehaviour)RE_Plugin.Instance).StartCoroutine(LoadAudioClipsAsync("BreachSplashSmall", 6, delegate(AudioClip[] clips) { _breachEmergeSounds = clips; })); ((MonoBehaviour)RE_Plugin.Instance).StartCoroutine(LoadAudioClipsAsync("TailSplash", 4, delegate(AudioClip[] clips) { _tailSplashSounds = clips; })); while (_groupsCompleted < 4) { yield return null; } _allSoundsLoaded = true; } private static IEnumerator LoadAudioClipsAsync(string baseName, int count, Action onComplete) { AudioClip[] clips = (AudioClip[])(object)new AudioClip[count]; for (int i = 0; i < count; i++) { string clipName = $"{baseName}{i + 1:00}"; AssetBundleRequest request = _assetBundle.LoadAssetAsync("Assets/Audio/" + clipName + ".wav"); yield return request; ref AudioClip reference = ref clips[i]; Object asset = request.asset; reference = (AudioClip)(object)((asset is AudioClip) ? asset : null); } onComplete(clips); _groupsCompleted++; } } public class SeaLifeModPatches { [HarmonyPrefix] public static bool DoNotRun() { return false; } } internal class EncounterGenerator : MonoBehaviour { private const float MIN_DISTANCE = 1000f; public static EncounterGenerator Instance { get; private set; } public void Awake() { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Instance = this; EncounterRegistry.RegisterEncounter(new FlotsamEncounter()); EncounterRegistry.RegisterEncounter(new WhalesEncounter()); EncounterRegistry.RegisterEncounter(new DenseFogEncounter()); EncounterRegistry.RegisterEncounter(new FishingBonanzaEncounter()); EncounterRegistry.RegisterEncounter(new IntenseStormEncounter()); EncounterRegistry.RegisterEncounter(new FogAndWhalesEncounter()); EncounterRegistry.RegisterEncounter(new FlotsamAndWhalesEncounter()); ((MonoBehaviour)this).StartCoroutine(ScheduleEncounter()); } public void Update() { SeaLifeMod.CheckWhaleDistance(); } private IEnumerator ScheduleEncounter() { yield return (object)new WaitUntil((Func)(() => GameState.playing)); int minTime = Mathf.Abs(Configs.generateEncounterMinTime.Value); int range = Mathf.Abs(Configs.generateEncounterTimeRange.Value); int timeToNextEncounter = minTime + Random.Range(0, range); yield return (object)new WaitForSeconds((float)timeToNextEncounter); if ((Object)(object)GameState.currentBoat != (Object)null) { Generate(); } ((MonoBehaviour)this).StartCoroutine(ScheduleEncounter()); } private void Generate() { if (!GameState.playing) { return; } if (GameState.distanceToLand <= 1000f) { RE_Plugin.LogDebug($"Too close to land (distance: {GameState.distanceToLand}), skipping encounter generation."); return; } if (GameState.sleeping) { RE_Plugin.LogDebug("Player sleeping, skipping encounter generation."); return; } if (Random.value > Mathf.Abs((float)Configs.encounterRollChance.Value / 100f)) { RE_Plugin.LogInfo("No encounter this time"); EncounterEvents.RaiseEncounterSkipped(); return; } List list = EncounterRegistry.GetAvailable().ToList(); if (list.Count == 0) { RE_Plugin.LogInfo("No encounters enabled"); return; } int num = list.Sum((Encounter e) => e.Weight); int num2 = Random.Range(0, num); int num3 = 0; foreach (Encounter item in list) { num3 += item.Weight; if (num2 < num3) { RE_Plugin.LogInfo("Encounter: " + item.Name); EncounterEvents.RaiseEncounterTriggered(item); item.Trigger(); break; } } } internal void LoadEncounter() { ((MonoBehaviour)this).StartCoroutine(EncounterLoader()); } private IEnumerator EncounterLoader() { string encounterName = ModData.GetEntry("RandomEncounters.EncounterName"); float timeRemaining = ModData.GetEntry("RandomEncounters.EncounterTimeRemaining"); int whaleCount = ModData.GetEntry("RandomEncounters.WhaleCount"); RE_Plugin.LogDebug($"Loaded encounter data: Name={encounterName}, TimeRemaining={timeRemaining}, WhaleCount={whaleCount}"); yield return (object)new WaitUntil((Func)(() => GameState.playing && !GameState.currentlyLoading)); if (!string.IsNullOrEmpty(encounterName) && timeRemaining > 0f) { Encounter enc = EncounterRegistry.GetByName(encounterName); if (enc?.IsAvailable ?? false) { RE_Plugin.LogInfo($"Restoring encounter: {encounterName} with {timeRemaining} seconds remaining."); enc.TimeRemaining = timeRemaining; enc.Trigger(); } } if (whaleCount > 0) { WhalesEncounter whalesEncounter = (WhalesEncounter)EncounterRegistry.GetByName("Whales"); if (whalesEncounter?.IsAvailable ?? false) { whalesEncounter.TriggerWasActive(whaleCount); } } } internal void SaveEncounter() { Encounter active = EncounterRegistry.GetActive(); if (active != null) { RE_Plugin.LogDebug($"Saving encounter data: Name={active.Name}, TimeRemaining={active.TimeRemaining}"); ModData.AddEntry("RandomEncounters.EncounterName", active.Name); ModData.AddEntry("RandomEncounters.EncounterTimeRemaining", active.TimeRemaining); } else { ModData.AddEntry("RandomEncounters.EncounterName", string.Empty); ModData.AddEntry("RandomEncounters.EncounterTimeRemaining", 0f); } ModData.AddEntry("RandomEncounters.WhaleCount", SeaLifeMod.ActiveWhales); } } internal class DenseFogEncounter : Encounter { [HarmonyPatch(typeof(OceanColorBlender))] private class OceanColorBlenderPatches { [HarmonyPrefix] [HarmonyPatch("ApplyPalette")] public static void ApplyFogDensity(ref OceanColorPalette palette) { if (!_fogCleared) { _originalFogDensity = ((_originalFogDensity == 0f) ? palette.fogDensity : _originalFogDensity); _currentFogDensity = ((_currentFogDensity == 0f) ? palette.fogDensity : _currentFogDensity); if (_clearFog && _currentFogDensity > _originalFogDensity) { _currentFogDensity -= 1E-05f; } if (!_clearFog && _currentFogDensity < 0.06f) { _currentFogDensity += 1E-05f; } palette.fogDensity = _currentFogDensity; if (_clearFog && _currentFogDensity <= _originalFogDensity) { _fogCleared = true; _currentFogDensity = 0f; _originalFogDensity = 0f; GameObject.Find("wind").GetComponent().SetPrivateField("timer", 0); RE_Plugin.LogDebug("Fog cleared"); } } } } [HarmonyPatch(typeof(Wind))] private class WindPatches { [HarmonyPrefix] [HarmonyPatch("SetNewGustTarget")] public static bool NoGust(ref Vector3 ___currentGustTarget, Vector3 ___currentWindTarget) { //IL_0019: 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) if (_fogCleared || _clearFog) { return true; } ___currentGustTarget = ___currentWindTarget; return false; } [HarmonyPrefix] [HarmonyPatch("SetNewWindTarget")] public static bool LightWind(ref Vector3 ___currentWindTarget) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (_fogCleared || _clearFog) { return true; } ___currentWindTarget = ((Vector3)(ref Wind.currentBaseWind)).normalized * 3f; return false; } } [HarmonyPatch(typeof(WaveSound))] private class WaveSoundPatches { [HarmonyPrefix] [HarmonyPatch("UpdateIntensity")] public static bool SetToMinVolume() { if (_fogCleared || _clearFog) { return true; } return false; } [HarmonyPostfix] [HarmonyPatch("Start")] public static void GetAudioSource(AudioSource ___audio) { if (!_waveAudioSources.ContainsKey(___audio)) { _waveAudioSources.Add(___audio, ___audio.volume); } } } [HarmonyPatch(typeof(WindSound))] private class WindSoundPatches { [HarmonyPrefix] [HarmonyPatch("Update")] public static bool SetToMinVolume() { if (_fogCleared || _clearFog) { return true; } return false; } [HarmonyPostfix] [HarmonyPatch("Start")] public static void GetAudioSource(AudioSource ___audio) { _windAudioSource = (source: ___audio, origVolume: ___audio.volume); } } private static readonly Dictionary _waveAudioSources = new Dictionary(); private static (AudioSource source, float origVolume) _windAudioSource; private static bool _clearFog = false; private static bool _fogCleared = true; private static float _currentFogDensity = 0f; private static float _originalFogDensity = 0f; private const float MAX_FOG_DENSITY = 0.06f; public override string Name => "Dense Fog"; public override int Weight => 5; public override bool IsAvailable => Configs.enableDenseFog.Value && !base.IsActive && WeatherStorms.instance.InvokePrivateMethod("GetNormalizedDistance") >= 0.75f; public override void Trigger() { Runner(Run()); } private IEnumerator Run() { base.IsActive = true; Spawn(); List waveAudioSources = _waveAudioSources.Keys.ToList(); AudioSource windAudioSource = _windAudioSource.source; float windOrigVolume = _windAudioSource.origVolume; for (float t = 0f; t < 4f; t += Time.deltaTime) { float lerpValue = t / 4f; foreach (AudioSource audioSource in waveAudioSources) { audioSource.volume = Mathf.Lerp(_waveAudioSources[audioSource], 0f, lerpValue); } if ((Object)(object)windAudioSource != (Object)null) { windAudioSource.volume = Mathf.Lerp(windOrigVolume, 0.0001f, lerpValue); } yield return null; } for (int i = 0; i < 4; i++) { Vector3 spawnPoint = GameState.currentBoat.position + GameState.currentBoat.right * (200f + Random.Range(20f, 60f) * (float)i) + GameState.currentBoat.forward * (float)Random.Range(-200, 200); Flotsam.SpawnItem(spawnPoint, (Random.Range(1, 100) > 50) ? AssetLoader.SmallWreck : AssetLoader.Hull, 1f, wreckage: true); yield return (object)new WaitForSeconds(1f); } float duration = ((base.TimeRemaining > 0f) ? base.TimeRemaining : ((float)Configs.fogDuration.Value)); float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; base.TimeRemaining = duration - elapsed; yield return null; } ClearFog(); for (float t2 = 0f; t2 < 4f; t2 += Time.deltaTime) { float lerpValue2 = t2 / 4f; foreach (AudioSource audioSource2 in waveAudioSources) { audioSource2.volume = Mathf.Lerp(0f, _waveAudioSources[audioSource2], lerpValue2); } if ((Object)(object)windAudioSource != (Object)null) { windAudioSource.volume = Mathf.Lerp(0.0001f, windOrigVolume, lerpValue2); } yield return null; } base.IsActive = false; EncounterEvents.RaiseEncounterCompleted(this); } private static void Spawn() { RE_Plugin.LogDebug("Spawning fog"); foreach (AudioSource item in _waveAudioSources.Keys.ToList()) { _waveAudioSources[item] = item.volume; } if ((Object)(object)_windAudioSource.source != (Object)null) { _windAudioSource = (source: _windAudioSource.source, origVolume: _windAudioSource.source.volume); } _clearFog = false; _fogCleared = false; } private static void ClearFog() { RE_Plugin.LogDebug("Clearing fog"); _clearFog = true; } } public abstract class Encounter { public abstract string Name { get; } public abstract int Weight { get; } public float TimeRemaining { get; set; } public bool IsActive { get; internal set; } public abstract bool IsAvailable { get; } public Coroutine Runner(IEnumerator enumerator) { return ((MonoBehaviour)EncounterGenerator.Instance).StartCoroutine(enumerator); } public abstract void Trigger(); } internal class FishingBonanzaEncounter : Encounter { [HarmonyPatch(typeof(FishingRodFish))] [HarmonyPatch("Update")] private class FishingRodFishPatches { [HarmonyPostfix] public static void IncreaseCatchChance(FishingRodFish __instance, ShipItemFishingRod ___rod, SimpleFloatingObject ___floater, ConfigurableJoint ___bobberJoint, ref float ___fishTimer) { //IL_0096: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (!EncounterRegistry.GetByName("Fishing Bonanza").IsActive || (Object)(object)__instance.currentFish != (Object)null || ((ShipItem)___rod).health <= 0f || (!Object.op_Implicit((Object)(object)((PickupableItem)___rod).held) && !RE_Plugin.IdleFishingPluginDetected && !RE_Plugin.HooksHangMorePluginDetected) || !((FloatingObjectBase)___floater).InWater) { return; } SoftJointLimit linearLimit = ___bobberJoint.linearLimit; if (((SoftJointLimit)(ref linearLimit)).limit <= 1f || ((Component)__instance).gameObject.layer == 16) { return; } ___fishTimer -= Time.deltaTime; float num = Vector3.Distance(((Component)__instance).transform.position, ((Component)___rod).transform.position); float num2 = Mathf.InverseLerp(3f, 20f, num) * 2.5f + 0.5f; if (___fishTimer <= 0f) { ___fishTimer = 1f; float num3 = (Object.op_Implicit((Object)(object)((PickupableItem)___rod).held) ? 20f : 3f); if (Random.Range(0f, 100f) < num2 * num3) { __instance.CatchFish(); } } } } private static Seagulls _seagulls; public override string Name => "Fishing Bonanza"; public override int Weight => 15; public override bool IsAvailable => Configs.enableFishingBonanza.Value && GameState.playing && Object.op_Implicit((Object)(object)GameState.currentBoat) && !base.IsActive && WeatherStorms.instance.InvokePrivateMethod("GetNormalizedDistance") >= 0.75f; public override void Trigger() { Runner(Run()); } private IEnumerator Run() { if ((Object)(object)_seagulls == (Object)null) { _seagulls = ((Component)Refs.shiftingWorld).GetComponentInChildren(true); } if ((Object)(object)_seagulls == (Object)null) { RE_Plugin.LogDebug("No seagulls found"); yield break; } GameObject seagulls = Object.Instantiate(((Component)_seagulls).gameObject, Refs.shiftingWorld); if (!seagulls.activeInHierarchy) { seagulls.SetActive(true); } seagulls.GetComponent().PlayOneShot(seagulls.GetComponent().clip); ParticleSystem seagullsPS = seagulls.GetComponent(); MainModule main = seagullsPS.main; ((MainModule)(ref main)).maxParticles = 25; ((MainModule)(ref main)).startLifetime = MinMaxCurve.op_Implicit(base.TimeRemaining); ((MainModule)(ref main)).startRotation = MinMaxCurve.op_Implicit(0f); ((MainModule)(ref main)).startRotation3D = false; RotationOverLifetimeModule rol = seagullsPS.rotationOverLifetime; ((RotationOverLifetimeModule)(ref rol)).enabled = false; ((RotationOverLifetimeModule)(ref rol)).x = MinMaxCurve.op_Implicit(0f); ((RotationOverLifetimeModule)(ref rol)).y = MinMaxCurve.op_Implicit(0f); ((RotationOverLifetimeModule)(ref rol)).z = MinMaxCurve.op_Implicit(0f); VelocityOverLifetimeModule vol = seagullsPS.velocityOverLifetime; ((VelocityOverLifetimeModule)(ref vol)).enabled = true; ((VelocityOverLifetimeModule)(ref vol)).orbitalX = MinMaxCurve.op_Implicit(0f); ((VelocityOverLifetimeModule)(ref vol)).orbitalY = MinMaxCurve.op_Implicit(0f); ((VelocityOverLifetimeModule)(ref vol)).orbitalZ = MinMaxCurve.op_Implicit(0f); ((VelocityOverLifetimeModule)(ref vol)).orbitalXMultiplier = 0f; ((VelocityOverLifetimeModule)(ref vol)).orbitalYMultiplier = 0f; ((VelocityOverLifetimeModule)(ref vol)).orbitalZMultiplier = 0f; RotationBySpeedModule rbs = seagullsPS.rotationBySpeed; ((RotationBySpeedModule)(ref rbs)).enabled = false; ParticleSystemRenderer seagullPSR = seagulls.GetComponent(); seagullPSR.alignment = (ParticleSystemRenderSpace)2; ShapeModule shape = seagullsPS.shape; ((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)18; ((ShapeModule)(ref shape)).scale = new Vector3(25f, 25f, 0.2f); EmissionModule emission = seagullsPS.emission; if (!((EmissionModule)(ref emission)).enabled) { ((EmissionModule)(ref emission)).enabled = true; } RE_Plugin.LogDebug("Starting fishing bonanza"); base.IsActive = true; float duration = ((base.TimeRemaining > 0f) ? base.TimeRemaining : ((float)Configs.fishingBonanzaDuration.Value)); float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; base.TimeRemaining = duration - elapsed; Vector3 targetPosition = GameState.currentBoat.position + GameState.currentBoat.up * 40f; seagulls.transform.position = Vector3.Lerp(seagulls.transform.position, targetPosition, 0.2f * Time.deltaTime); Vector3 euler = seagulls.transform.eulerAngles; euler.y = Mathf.LerpAngle(euler.y, GameState.currentBoat.eulerAngles.y - 90f, 0.2f * Time.deltaTime); seagulls.transform.rotation = Quaternion.Euler(euler); yield return null; } RE_Plugin.LogDebug("Stopping fishing bonanza"); base.IsActive = false; Object.Destroy((Object)(object)seagulls); base.TimeRemaining = 0f; EncounterEvents.RaiseEncounterCompleted(this); } } internal class FlotsamAndWhalesEncounter : Encounter { public override string Name => "Flotsam and Whales"; public override int Weight => 10; public override bool IsAvailable => Configs.controlSeaLifeMod.Value && (Object)(object)RE_Plugin.SeaLifeModPluginInstance != (Object)null && Configs.enableFlotsam.Value; public override void Trigger() { EncounterRegistry.GetByName("Flotsam")?.Trigger(); EncounterRegistry.GetByName("Whales")?.Trigger(); } } internal class FlotsamEncounter : Encounter { private static readonly int[] _cargos = new int[40] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 24, 26, 27, 201, 202, 206, 213, 214, 216, 219, 220, 222, 223, 224, 227, 228, 229, 230, 231, 232, 233, 234 }; private static readonly int[] _consumables = new int[4] { 104, 108, 131, 132 }; private static readonly int[] _bottles = new int[5] { 55, 56, 57, 58, 59 }; private static readonly int[] _tobaccoCrates = new int[4] { 311, 313, 315, 319 }; private static readonly int[] _teaAndCoffeeBoxes = new int[4] { 387, 388, 389, 373 }; private const int MAX_CARGO_TYPES = 4; private const int MAX_CARGOS = 3; private const int MAX_CONSUME_TYPES = 2; public override string Name => "Flotsam"; public override int Weight => 15; public override bool IsAvailable => Configs.enableFlotsam.Value; public override void Trigger() { Run(); } private void Run() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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) Vector3 spawnPoint = GameState.currentBoat.position + GameState.currentBoat.right * 200f + GameState.currentBoat.forward * (float)Random.Range(-30, 30); Spawn(spawnPoint); EncounterEvents.RaiseEncounterCompleted(this); } internal static void Spawn(Vector3 spawnPoint) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < Random.Range(1, 4); i++) { int num = Random.Range(0, _cargos.Length - 1); for (int j = 0; j < Random.Range(1, 3); j++) { RE_Plugin.LogDebug($"Cargo choice: {_cargos[num]}"); GameObject val = PrefabsDirectory.instance.directory[_cargos[num]]; int num2 = Random.Range(1, (int)val.GetComponent().amount + 1); Flotsam.SpawnItem(spawnPoint, val, num2); } } for (int k = 0; k < Random.Range(1, 2); k++) { int num3 = Random.Range(0, _consumables.Length - 1); RE_Plugin.LogDebug($"Consumable choice: {_consumables[num3]}"); GameObject val2 = PrefabsDirectory.instance.directory[_consumables[num3]]; int num4 = Random.Range(1, (int)val2.GetComponent().amount + 1); Flotsam.SpawnItem(spawnPoint, val2, num4); } for (int l = 0; l < Random.Range(5, 10); l++) { int num5 = Random.Range(0, _bottles.Length - 1); RE_Plugin.LogDebug($"Bottle choice: {_bottles[num5]}"); GameObject prefabGO = PrefabsDirectory.instance.directory[_bottles[num5]]; float amount = 0f; Flotsam.SpawnItem(spawnPoint, prefabGO, amount); } int num6 = Random.Range(0, _tobaccoCrates.Length - 1); RE_Plugin.LogDebug($"Tobacco choice: {_tobaccoCrates[num6]}"); GameObject val3 = PrefabsDirectory.instance.directory[_tobaccoCrates[num6]]; int num7 = Random.Range(1, (int)val3.GetComponent().amount + 1); Flotsam.SpawnItem(spawnPoint, val3, num7); int num8 = Random.Range(0, _teaAndCoffeeBoxes.Length - 1); RE_Plugin.LogDebug($"Tea/Coffee choice: {_teaAndCoffeeBoxes[num8]}"); GameObject val4 = PrefabsDirectory.instance.directory[_teaAndCoffeeBoxes[num8]]; int num9 = Random.Range(1, (int)val4.GetComponent().amount + 1); Flotsam.SpawnItem(spawnPoint, val4, num9); Flotsam.SpawnItem(spawnPoint, AssetLoader.SmallWreck, 1f, wreckage: true); } } internal class FogAndWhalesEncounter : Encounter { private readonly Encounter denseFogEncounter = EncounterRegistry.GetByName("Dense Fog"); public override string Name => "Dense Fog and Whales"; public override int Weight => 5; public override bool IsAvailable => Configs.controlSeaLifeMod.Value && (Object)(object)RE_Plugin.SeaLifeModPluginInstance != (Object)null && Configs.enableDenseFog.Value && !denseFogEncounter.IsActive && WeatherStorms.instance.InvokePrivateMethod("GetNormalizedDistance") >= 0.75f; public override void Trigger() { denseFogEncounter.Trigger(); Encounter byName = EncounterRegistry.GetByName("Whales"); byName.Trigger(); } } internal class Flotsam { internal static void SpawnItem(Vector3 spawnPoint, GameObject prefabGO, float amount, bool wreckage = false) { //IL_0002: 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) GameObject val = Object.Instantiate(prefabGO, spawnPoint, Quaternion.Euler((float)Random.Range(0, 360), (float)Random.Range(0, 360), (float)Random.Range(0, 360))); ShipItem component = val.GetComponent(); component.sold = true; component.amount = amount; component.health = amount; val.GetComponent().RegisterToSave(); Good component2 = val.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.RegisterAsMissionless(); } ShipItemCrate val2 = (ShipItemCrate)(object)((component is ShipItemCrate) ? component : null); if (val2 != null) { ((MonoBehaviour)EncounterGenerator.Instance).StartCoroutine(UnsealCrate(val2)); } if (wreckage) { ((GoPointerButton)val.GetComponent()).unclickable = true; val.transform.parent = Refs.shiftingWorld; } RE_Plugin.LogDebug("Prefab " + ((Object)prefabGO).name + " spawned"); } internal static IEnumerator UnsealCrate(ShipItemCrate crate) { yield return (object)new WaitForEndOfFrame(); yield return (object)new WaitForEndOfFrame(); CrateInventory crateInventory = ((Component)crate).GetComponent(); int num = (int)((ShipItem)crate).amount; for (int i = 0; i < num; i++) { RE_Plugin.LogDebug("Inserting item " + ((ShipItem)crate).amount); GameObject gameObject = Object.Instantiate(crate.GetContainedPrefab(), ((Component)crate).transform.position + new Vector3(0f, 100.5f, 0f), ((Component)crate).transform.rotation); ((ShipItem)crate).amount = ((ShipItem)crate).amount - 1f; gameObject.GetComponent().RegisterToSave(); if (Object.op_Implicit((Object)(object)gameObject.GetComponent())) { if (crate.smokedFood) { gameObject.GetComponent().smoked = 1f; gameObject.GetComponent().amount = 1.01f; } gameObject.GetComponent().dried = 1f; gameObject.GetComponent().UpdateMaterial(); } ((MonoBehaviour)EncounterGenerator.Instance).StartCoroutine(InsertItem(crateInventory, gameObject.GetComponent())); } ((ShipItem)crate).UpdateLookText(); ((ShipItem)crate).itemRigidbodyC.UpdateMass(); RE_Plugin.LogDebug("Unsealed crate."); } private static IEnumerator InsertItem(CrateInventory crateInventory, ShipItem item) { yield return (object)new WaitForEndOfFrame(); item.sold = true; crateInventory.InsertItem(item); } } internal class IntenseStormEncounter : Encounter { [HarmonyPatch(typeof(OceanUpdaterCrest))] private static class OceanUpdaterCrestPatches { [HarmonyPostfix] [HarmonyPatch("Awake")] public static void Awake(OceanUpdaterCrest __instance) { _oceanUpdaterCrest = __instance; } } private static OceanUpdaterCrest _oceanUpdaterCrest; public override string Name => "Intense Storm"; public override int Weight => 5; public override bool IsAvailable => Configs.enableIntenseStorm.Value && !base.IsActive; public override void Trigger() { Runner(Run()); } private IEnumerator Run() { base.IsActive = true; WeatherStorms weatherStorms = WeatherStorms.instance; WanderingStorm storm = weatherStorms.GetCurrentStorm(); WanderingStormLightning lightning = ((Component)((Component)storm).transform.GetChild(3)).GetComponent(); Region targetRegion = RegionBlender.instance.GetPrivateField("currentTargetRegion"); float origInertiaWindScale = _oceanUpdaterCrest.inertiaWindScale; float origWindSpeedMult = _oceanUpdaterCrest.GetPrivateField("windSpeedMult"); float origSmallWavesMult = _oceanUpdaterCrest.GetPrivateField("smallWavesMult"); float origLightningInterval = lightning.GetPrivateField("lightningInterval"); float origRainDensity = targetRegion.stormWeather.particles.rainDensity; lightning.SetPrivateField("lightningInterval", 5f); targetRegion.stormWeather.particles.rainDensity = 70f; float stormDist = Vector3.Distance(((Component)Camera.main).transform.position, ((Component)storm).transform.position); Vector3 vector = ((Component)Camera.main).transform.position - ((Component)storm).transform.position; vector.y = 0f; RE_Plugin.LogDebug(((Object)storm).name + " approaching"); while (stormDist > 1500f) { vector = ((Component)Camera.main).transform.position - ((Component)storm).transform.position; vector.y = 0f; Wind.currentBaseWind = vector * 50f; float translateSpeed = (((double)weatherStorms.InvokePrivateMethod("GetNormalizedDistance") < 0.66) ? 0.0025f : 0.125f); ((Component)storm).transform.Translate(vector * translateSpeed); yield return (object)new WaitForSeconds(0.05f); stormDist = Vector3.Distance(((Component)Camera.main).transform.position, ((Component)storm).transform.position); } RE_Plugin.LogDebug(((Object)storm).name + " arrived"); _oceanUpdaterCrest.inertiaWindScale = 0.22f; _oceanUpdaterCrest.SetPrivateField("windSpeedMult", 5f); _oceanUpdaterCrest.SetPrivateField("smallWavesMult", 0.4f); float duration = ((base.TimeRemaining > 0f) ? base.TimeRemaining : ((float)Configs.intenseStormDuration.Value)); float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; base.TimeRemaining = duration - elapsed; Wind.currentBaseWind = vector * 50f; yield return null; } RE_Plugin.LogDebug(((Object)storm).name + " dying down"); lightning.SetPrivateField("lightningInterval", origLightningInterval); Weather.instance.currentRegion.stormWeather.particles.rainDensity = origRainDensity; _oceanUpdaterCrest.inertiaWindScale = origInertiaWindScale; _oceanUpdaterCrest.SetPrivateField("windSpeedMult", origWindSpeedMult); _oceanUpdaterCrest.SetPrivateField("smallWavesMult", origSmallWavesMult); base.IsActive = false; base.TimeRemaining = 0f; EncounterEvents.RaiseEncounterCompleted(this); } } internal class WhalesEncounter : Encounter { public override string Name => "Whales"; public override int Weight => 25; public override bool IsAvailable => Configs.controlSeaLifeMod.Value && (Object)(object)RE_Plugin.SeaLifeModPluginInstance != (Object)null; public override void Trigger() { Runner(Run()); } internal void TriggerWasActive(int whaleCount) { Runner(Run(whaleCount)); } private IEnumerator Run(int whaleCount = -1) { Vector3 boatPosition = GameState.currentBoat.position; int spawnCount = Random.Range(2, 5); if (whaleCount > 0) { spawnCount = whaleCount; } WaitForSeconds spawnDelay = new WaitForSeconds(2f); for (int i = 0; i < spawnCount; i++) { Vector3 randomOffset = new Vector3((float)Random.Range(-200, 200), -8f, (float)Random.Range(-200, 200)); yield return spawnDelay; SeaLifeMod.SpawnWhale(i, boatPosition + randomOffset); } yield return spawnDelay; SeaLifeMod.TriggerEntranceAnimation(); if (whaleCount == -1) { EncounterEvents.RaiseEncounterCompleted(this); } } } [BepInPlugin("com.raddude.randomencounters", "RandomEncounters", "2.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class RE_Plugin : BaseUnityPlugin { public const string PLUGIN_GUID = "com.raddude.randomencounters"; public const string PLUGIN_NAME = "RandomEncounters"; public const string PLUGIN_VERSION = "2.0.0"; public const string SEALIFEMOD_GUID = "com.yourname.sailwind.sealifeplugin"; public const string IDLEFISHING_GUID = "ISA_IdleFishing"; public const string HOOKSHANGMORE_GUID = "com.raddude.hookshangmore"; private static ManualLogSource _logger; internal static BaseUnityPlugin SeaLifeModPluginInstance { get; private set; } internal static bool IdleFishingPluginDetected { get; private set; } internal static bool HooksHangMorePluginDetected { get; private set; } internal static RE_Plugin Instance { get; private set; } internal static Harmony HarmonyInstance { get; private set; } public static bool IsFlotsamEnabled => Configs.enableFlotsam.Value; public static bool IsSeaLifeModEnabled => (Object)(object)SeaLifeModPluginInstance != (Object)null && Configs.controlSeaLifeMod.Value; public static bool IsIntenseStormEnabled => Configs.enableIntenseStorm.Value; public static bool IsDenseFogEnabled => Configs.enableDenseFog.Value; public static bool IsFishingBonanzaEnabled => Configs.enableFishingBonanza.Value; internal static void LogDebug(string message) { _logger.LogDebug((object)message); } internal static void LogInfo(string message) { _logger.LogInfo((object)message); } internal static void LogWarning(string message) { _logger.LogWarning((object)message); } internal static void LogError(string message) { _logger.LogError((object)message); } private void Awake() { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Instance = this; _logger = ((BaseUnityPlugin)this).Logger; Configs.InitializeConfigs(); ((MonoBehaviour)this).StartCoroutine(AssetLoader.LoadAssetBundle()); HarmonyInstance = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "com.raddude.randomencounters"); foreach (KeyValuePair pluginInfo in Chainloader.PluginInfos) { BepInPlugin metadata = pluginInfo.Value.Metadata; if (Configs.controlSeaLifeMod.Value && metadata.GUID.Equals("com.yourname.sailwind.sealifeplugin")) { LogInfo("SealLifeMod mod found"); SeaLifeModPluginInstance = pluginInfo.Value.Instance; SeaLifeMod.PatchMod(); } if (metadata.GUID.Equals("ISA_IdleFishing")) { LogInfo("IdleFishing mod found"); IdleFishingPluginDetected = true; } if (metadata.GUID.Equals("com.raddude.hookshangmore")) { LogInfo("HooksHangMore mod found"); HooksHangMorePluginDetected = true; } if ((Object)(object)SeaLifeModPluginInstance != (Object)null && IdleFishingPluginDetected && HooksHangMorePluginDetected) { break; } } ((Component)this).gameObject.AddComponent(); } } internal class AssetLoader { private static readonly List assetPaths = new List { Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)RE_Plugin.Instance).Info.Location), "Assets"), Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)RE_Plugin.Instance).Info.Location)) }; public static GameObject Hull { get; private set; } public static GameObject SmallWreck { get; private set; } public static string FindAssetPath(string fileName) { foreach (string assetPath in assetPaths) { string text = Path.Combine(assetPath, fileName); if (File.Exists(text)) { return text; } } return null; } internal static IEnumerator LoadAssetBundle() { RE_Plugin.LogDebug("Loading bundle"); string bundlePath = FindAssetPath("wreckage_bundle"); if (string.IsNullOrEmpty(bundlePath)) { RE_Plugin.LogError("Asset bundle path not found"); yield break; } AssetBundleCreateRequest assetBundleRequest = AssetBundle.LoadFromFileAsync(bundlePath); yield return assetBundleRequest; AssetBundle assetBundle = assetBundleRequest.assetBundle; if ((Object)(object)assetBundle == (Object)null) { RE_Plugin.LogError("Failed to load " + bundlePath); } AssetBundleRequest request = assetBundle.LoadAllAssetsAsync(); yield return request; Object? obj = ((IEnumerable)request.allAssets).FirstOrDefault((Func)((Object a) => a.name == "hull")); Hull = (GameObject)(object)((obj is GameObject) ? obj : null); Object? obj2 = ((IEnumerable)request.allAssets).FirstOrDefault((Func)((Object a) => a.name == "small_wreck")); SmallWreck = (GameObject)(object)((obj2 is GameObject) ? obj2 : null); if ((Object)(object)Hull == (Object)null || (Object)(object)SmallWreck == (Object)null) { RE_Plugin.LogError("Failed to load all assets from the bundle"); } else { RE_Plugin.LogInfo("Assets loaded"); } } } internal class Configs { internal static ConfigEntry encounterRollChance; internal static ConfigEntry generateEncounterMinTime; internal static ConfigEntry generateEncounterTimeRange; internal static ConfigEntry enableFlotsam; internal static ConfigEntry controlSeaLifeMod; internal static ConfigEntry enableDenseFog; internal static ConfigEntry fogDuration; internal static ConfigEntry enableFishingBonanza; internal static ConfigEntry fishingBonanzaDuration; internal static ConfigEntry enableIntenseStorm; internal static ConfigEntry intenseStormDuration; internal static void InitializeConfigs() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown ConfigFile config = ((BaseUnityPlugin)RE_Plugin.Instance).Config; encounterRollChance = config.Bind("Encounter Generation Settings", "Chance an encounter occurs", 60, new ConfigDescription("Percent chance an encounter occurs.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); generateEncounterMinTime = config.Bind("Encounter Generation Settings", "Minimum encounter chance time", 900, "Minimum time in seconds to get a chance roll for an encounter, a random amount of time from the 'Variation in encounter chance time' setting will be added to this."); generateEncounterTimeRange = config.Bind("Encounter Generation Settings", "Variation in encounter chance time", 300, "A random number of seconds from 0 up to the value specified will be added to the 'Minimum encounter chance time' setting."); enableFlotsam = config.Bind("Encounter Types", "Enable flotsam encounters", true, (ConfigDescription)null); controlSeaLifeMod = config.Bind("Encounter Types", "Control SeaLifeMod spawns", true, "Use this mod to control SeaLifeMod spawns. Requires restart to take effect."); enableDenseFog = config.Bind("Encounter Types", "Enable dense fog encounters", true, (ConfigDescription)null); enableFishingBonanza = config.Bind("Encounter Types", "Enable fishing bonanza encounters", true, (ConfigDescription)null); enableIntenseStorm = config.Bind("Encounter Types", "Enable intense storm encounters", true, (ConfigDescription)null); fogDuration = config.Bind("Encounter Settings", "Fog encounter duration", 300, "In seconds, the amount of time the fog encounter lasts."); fishingBonanzaDuration = config.Bind("Encounter Settings", "Fishing bonanza duration", 300, "In seconds, the amount of time the fishing bonanza encounter lasts."); intenseStormDuration = config.Bind("Encounter Settings", "Intense storm duration", 300, "In seconds, the amount of time the intense storm encounter lasts."); } } internal static class Extensions { public static T GetPrivateField(this object obj, string field) { return (T)Traverse.Create(obj).Field(field).GetValue(); } public static void SetPrivateField(this object obj, string field, object value) { Traverse.Create(obj).Field(field).SetValue(value); } public static T InvokePrivateMethod(this object obj, string method) { return Traverse.Create(obj).Method(method, Array.Empty()).GetValue(); } public static T InvokePrivateMethod(this object obj, string method, params object[] parameters) { return Traverse.Create(obj).Method(method, parameters).GetValue(); } } internal class ModData { [HarmonyPatch(typeof(SaveLoadManager))] private class SaveLoadManagerPatches { [HarmonyPrefix] [HarmonyPatch("SaveModData")] public static void SaveModData() { EncounterGenerator.Instance.SaveEncounter(); } [HarmonyPrefix] [HarmonyPatch("LoadModData")] public static void LoadModData() { EncounterGenerator.Instance.LoadEncounter(); } } public static void AddEntry(string dataName, T data) { string value = ((!(typeof(T) == typeof(float))) ? data.ToString() : ((float)(object)data).ToString(CultureInfo.InvariantCulture)); if (GameState.modData.ContainsKey(dataName)) { GameState.modData[dataName] = value; } else { GameState.modData.Add(dataName, value); } } public static T GetEntry(string dataName) { if (!GameState.modData.ContainsKey(dataName)) { RE_Plugin.LogWarning("GetEntry: " + dataName + " not found in modData"); return default(T); } string text = GameState.modData[dataName]; if (typeof(T) == typeof(float)) { return (T)(object)float.Parse(text, CultureInfo.InvariantCulture); } return (T)Convert.ChangeType(text, typeof(T)); } } } namespace RandomEncounters.API { public class EncounterEvents { public static event Action EncounterTriggered; public static event Action EncounterCompleted; public static event Action EncounterSkipped; internal static void RaiseEncounterTriggered(Encounter enc) { EncounterEvents.EncounterTriggered?.Invoke(enc); } public static void RaiseEncounterCompleted(Encounter enc) { EncounterEvents.EncounterCompleted?.Invoke(enc); } internal static void RaiseEncounterSkipped() { EncounterEvents.EncounterSkipped?.Invoke(); } } public static class EncounterRegistry { internal static readonly List RegisteredEncounters = new List(); public static void RegisterEncounter(Encounter enc) { RegisteredEncounters.Add(enc); } internal static IEnumerable GetAvailable() { return RegisteredEncounters.Where((Encounter e) => e.IsAvailable); } internal static Encounter GetByName(string name) { return RegisteredEncounters.FirstOrDefault((Encounter e) => e.Name == name); } internal static Encounter GetActive() { return RegisteredEncounters.FirstOrDefault((Encounter e) => e.IsActive); } } }