Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of AtlasPirateShips v1.0.0
plugins/AtlasPirateShips/AtlasPirateShips.dll
Decompiled 8 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.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Networking; [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("AtlasPirateShips")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("AtlasPirateShips")] [assembly: AssemblyTitle("AtlasPirateShips")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace AtlasPirateShips { public enum PirateDifficulty { Easy, Normal, Hard } [BepInPlugin("com.atlas.pirateships", "Atlas Pirate Ships", "1.0.0")] public sealed class AtlasPirateShipsPlugin : BaseUnityPlugin { private sealed class AutoSpawnEncounterGate { internal readonly List<Ship> PirateShips = new List<Ship>(); internal int EncounterId; internal long CreatedAtTicks; internal float NextActivityCheck; internal bool LastActive; internal float PendingBonusSeconds; internal bool Fought; internal bool PlayersWon; internal bool BonusResolved; internal Vector3 GroupPosition; internal bool HasGroupPosition; } private sealed class AutoSpawnShipState { internal float Remaining; internal float GraceUntil; internal bool GraceUsedThisCycle; internal bool ActiveLastFrame; internal bool ActiveThisFrame; internal bool SerpentSuppressedLastFrame; internal bool RecoveryPausedLastFrame; internal bool EncounterPausedLastFrame; internal AutoSpawnEncounterGate ActiveEncounter; } private sealed class ShipMotionSample { public Vector3 Position; public float SampleTime; public float MovingUntil = -1f; public float LastSeen; } private readonly struct OceanWeatherCacheKey : IEquatable<OceanWeatherCacheKey> { internal readonly object BiomeArgument; internal readonly bool Ashlands; internal readonly bool DeepNorth; internal OceanWeatherCacheKey(object biomeArgument, bool ashlands, bool deepNorth) { BiomeArgument = biomeArgument; Ashlands = ashlands; DeepNorth = deepNorth; } public bool Equals(OceanWeatherCacheKey other) { if (object.Equals(BiomeArgument, other.BiomeArgument) && Ashlands == other.Ashlands) { return DeepNorth == other.DeepNorth; } return false; } public override bool Equals(object obj) { if (obj is OceanWeatherCacheKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (int)(((uint)(((BiomeArgument != null) ? BiomeArgument.GetHashCode() : 0) * 397) ^ (Ashlands ? 1u : 0u)) * 397) ^ (DeepNorth ? 1 : 0); } } public const string PluginGuid = "com.atlas.pirateships"; public const string PluginName = "Atlas Pirate Ships"; public const string PluginVersion = "1.0.0"; internal const string PirateKey = "AtlasPirateShip"; internal const string PirateTierKey = "AtlasPirateTier"; internal const string PirateHullKey = "AtlasPirateHull"; internal const string PirateSinkingKey = "AtlasPirateSinking"; internal const string PirateBrokenKey = "AtlasPirateBroken"; internal const string PiratePilotLostKey = "AtlasPiratePilotLost"; internal const string PirateCaptainKilledKey = "AtlasPirateCaptainKilled"; internal const string PirateAlertStateKey = "AtlasPirateAlertState"; internal const string PirateTargetPlayerZdoKey = "AtlasPirateTargetPlayerZdo"; internal const string PirateLaneXKey = "AtlasPirateLaneX"; internal const string PirateLaneZKey = "AtlasPirateLaneZ"; internal const string PirateLaneDirXKey = "AtlasPirateLaneDirX"; internal const string PirateLaneDirZKey = "AtlasPirateLaneDirZ"; internal const string PirateLaneUntilTicksKey = "AtlasPirateLaneUntilTicks"; internal const string PirateSailModeKey = "AtlasPirateSailMode"; internal const string PirateCrewKey = "AtlasPirateCrew"; internal const string PirateCaptainKey = "AtlasPirateCaptain"; internal const string PirateMeleeBoarderKey = "AtlasPirateMeleeBoarder"; internal const string PirateCrewWalkHoldKey = "AtlasPirateCrewWalkHold"; internal const string PirateCrewHomeGoalXKey = "AtlasPirateCrewHomeGoalX"; internal const string PirateCrewHomeGoalYKey = "AtlasPirateCrewHomeGoalY"; internal const string PirateCrewHomeGoalZKey = "AtlasPirateCrewHomeGoalZ"; internal const string PirateCrewSlotKey = "AtlasPirateCrewSlot"; internal const string PirateCrewPrefabKey = "AtlasPirateCrewPrefab"; internal const string PirateLootKey = "AtlasPirateLoot"; internal const string PirateRewardChestKey = "AtlasPirateRewardChest"; internal const string PirateRewardChestSpawnedKey = "AtlasPirateRewardChestSpawned"; internal const string PirateRewardCaptainStarsKey = "AtlasPirateRewardCaptainStars"; internal const string PirateMovementModeKey = "AtlasPirateMovementMode"; internal const string PiratePlannedMovementModeKey = "AtlasPiratePlannedMovementMode"; internal const string PirateBoardingRoleKey = "AtlasPirateBoardingRole"; internal const string PirateBoardingTransfersSentKey = "AtlasPirateBoardingTransfersSent"; internal const string PirateBoardingTransferTarget0ZdoKey = "AtlasPirateBoardingTransferTarget0Zdo"; internal const string PirateBoardingTransferTarget1ZdoKey = "AtlasPirateBoardingTransferTarget1Zdo"; internal const string PirateBoardingTransferTarget2ZdoKey = "AtlasPirateBoardingTransferTarget2Zdo"; internal const string PirateBoardingReservedTargetZdoKey = "AtlasPirateBoardingReservedTargetZdo"; internal const string PirateBoardingCooldownUntilTicksKey = "AtlasPirateBoardingCooldownUntilTicks"; internal const string PirateBoardingCooldownTargetZdoKey = "AtlasPirateBoardingCooldownTargetZdo"; internal const string PirateCrewSpawnedKey = "AtlasPirateCrewSpawned"; internal const string PirateEncounterIdKey = "AtlasPirateEncounterId"; internal const string PirateRoamUntilTicksKey = "AtlasPirateRoamUntilTicks"; internal const string PirateRoamProvokedKey = "AtlasPirateRoamProvoked"; internal const string GroupTimerRemainingKey = "AtlasPirateGroupTimerRemaining"; internal const string GroupTimerSavedTicksKey = "AtlasPirateGroupTimerSavedTicks"; internal const string GroupTimerEncounterIdKey = "AtlasPirateGroupTimerEncounterId"; internal const string GroupTimerEncounterTicksKey = "AtlasPirateGroupTimerEncounterTicks"; internal const string GroupTimerEncounterBonusKey = "AtlasPirateGroupTimerEncounterBonus"; internal const string GroupTimerEncounterFoughtKey = "AtlasPirateGroupTimerEncounterFought"; internal const string PirateSpawnHunterKey = "AtlasPirateSpawnHunter"; internal const string PirateCaptainStarsKey = "AtlasPirateCaptainStars"; internal const string PirateFleetIdKey = "AtlasPirateFleetId"; internal const string PirateShipIdKey = "AtlasPirateShipId"; internal const string PirateCrewReleasedKey = "AtlasPirateCrewReleased"; internal const string PirateCrewRecoveryStateKey = "AtlasPirateCrewRecoveryState"; internal const string PirateForcedNoLootDeathKey = "AtlasPirateForcedNoLootDeath"; internal const string PirateCrewBoardedKey = "AtlasPirateCrewBoarded"; internal const string PirateCrewBoardingTargetPrefabKey = "AtlasPirateCrewBoardingTargetPrefab"; internal const string PirateCrewBoardingTargetZdoKey = "AtlasPirateCrewBoardingTargetZdo"; internal const string PirateCrewBoardingLocalXKey = "AtlasPirateCrewBoardingLocalX"; internal const string PirateCrewBoardingLocalYKey = "AtlasPirateCrewBoardingLocalY"; internal const string PirateCrewBoardingLocalZKey = "AtlasPirateCrewBoardingLocalZ"; internal const string PirateCrewBoardingTransferActiveKey = "AtlasPirateCrewBoardingTransferActive"; internal const string PirateCrewBoardingTransferStartTicksKey = "AtlasPirateCrewBoardingTransferStartTicks"; internal const string PirateCrewBoardingTransferStartXKey = "AtlasPirateCrewBoardingTransferStartX"; internal const string PirateCrewBoardingTransferStartYKey = "AtlasPirateCrewBoardingTransferStartY"; internal const string PirateCrewBoardingTransferStartZKey = "AtlasPirateCrewBoardingTransferStartZ"; internal const string PirateCrewBoardingLaunchLocalXKey = "AtlasPirateCrewBoardingLaunchLocalX"; internal const string PirateCrewBoardingLaunchLocalYKey = "AtlasPirateCrewBoardingLaunchLocalY"; internal const string PirateCrewBoardingLaunchLocalZKey = "AtlasPirateCrewBoardingLaunchLocalZ"; internal const string PirateLastDamageByPlayerKey = "AtlasPirateLastDamageByPlayer"; internal const string PirateKilledByPlayerKey = "AtlasPirateKilledByPlayer"; internal const string PirateSpawnFadeKey = "AtlasPirateSpawnFade"; internal const string PirateSpawnFadeStartTicksKey = "AtlasPirateSpawnFadeStartTicks"; internal const string PirateLanternReadyTicksKey = "AtlasPirateLanternReadyTicks"; internal const string PirateCombatReadyKey = "AtlasPirateCombatReady"; internal const string PirateFactionKey = "AtlasPirateFaction"; internal const string PirateBattleKey = "AtlasPirateBattle"; internal const string PirateBattleOriginXKey = "AtlasPirateBattleOriginX"; internal const string PirateBattleOriginZKey = "AtlasPirateBattleOriginZ"; internal const string PirateCombatSideKey = "AtlasPirateCombatSide"; internal const string PirateTargetShipZdoKey = "AtlasPirateTargetShipZdo"; internal const string PirateAttackVictimZdoKey = "AtlasPirateAttackVictimZdo"; internal const string PlayerRecoveringGearKey = "AtlasPirateRecoveringGear"; internal const string PlayerHasModKey = "AtlasPirateShipsInstalled"; internal const string PirateAttackUntilTicksKey = "AtlasPirateAttackUntilTicks"; internal const string PirateExpectedCrewKey = "AtlasPirateExpectedCrew"; internal const string PirateCrewDeathsKey = "AtlasPirateCrewDeaths"; internal const string PirateCrewGenerationKey = "AtlasPirateCrewGeneration"; internal const string PirateLastCrewKilledByPlayerKey = "AtlasPirateLastCrewKilledByPlayer"; internal const string PirateHullDefeatedKey = "AtlasPirateHullDefeated"; internal const string PirateHullKilledByPlayerKey = "AtlasPirateHullKilledByPlayer"; internal const string PirateDataVersionKey = "AtlasPirateDataVersion"; internal const int PirateDataVersion = 10; private static readonly FieldInfo PlayerDebugFlyField = AccessTools.Field(typeof(Player), "m_debugFly"); internal ConfigEntry<PirateDifficulty> DifficultyPreset; internal ConfigEntry<bool> EnableAutoSpawns; internal ConfigEntry<float> SpawnIntervalSeconds; internal ConfigEntry<float> VictoryBonusMinutes; internal ConfigEntry<float> SpawnIntervalMinSeconds; internal ConfigEntry<float> ClearDayTimerSpeedMultiplier; internal ConfigEntry<float> FogTimerSpeedMultiplier; internal ConfigEntry<float> NightTimerSpeedMultiplier; internal ConfigEntry<float> NightFogTimerSpeedMultiplier; internal ConfigEntry<float> HeavyRainTimerSpeedMultiplier; internal ConfigEntry<float> ThunderstormTimerSpeedMultiplier; internal ConfigEntry<float> OceanEntryGraceSeconds; internal ConfigEntry<float> SpawnDistanceMin; internal ConfigEntry<float> SpawnDistanceMax; internal ConfigEntry<bool> EnableSpawnFade; internal ConfigEntry<float> SpawnFadeDurationSeconds; internal ConfigEntry<bool> EnableFactionSails; internal ConfigEntry<bool> EnableFactionSailEmblems; internal ConfigEntry<bool> EnableShipAlertIcons; internal ConfigEntry<float> AggroVisionRadius; internal ConfigEntry<float> ShipSpeedMultiplier; internal ConfigEntry<float> PirateRangedAttackRange; internal ConfigEntry<float> PirateCultistFlameReach; internal ConfigEntry<float> PirateCultistAIAttackRange; internal ConfigEntry<float> SinkDurationSeconds; internal ConfigEntry<float> SinkForce; internal ConfigEntry<bool> StrictOceanOnly; internal ConfigEntry<bool> EnableFleetSpawns; internal ConfigEntry<float> FleetSpawnChance; internal ConfigEntry<bool> EnablePirateBattles; internal ConfigEntry<float> PirateBattleChance; internal ConfigEntry<float> HigherHullChance; internal ConfigEntry<float> RamBehaviorChance; internal ConfigEntry<float> PirateBattleRamBehaviorChance; internal ConfigEntry<float> BoardingBehaviorChance; internal ConfigEntry<float> MeleeBoarderChance; internal ConfigEntry<float> SpawnRoamSeconds; internal ConfigEntry<float> RaftHearingReduction; internal ConfigEntry<float> GiveUpWithoutHitSeconds; internal ConfigEntry<float> KarveHearingReduction; internal ConfigEntry<float> TimerMultiplierTier1; internal ConfigEntry<float> TimerMultiplierTier2To3; internal ConfigEntry<float> TimerMultiplierTier4To5; internal ConfigEntry<float> TimerMultiplierTier6To7; internal ConfigEntry<float> HeavyEncounterMinutesPerTier; internal ConfigEntry<float> DeathRecoveryPauseMinutes; internal ConfigEntry<float> FleetExtraMinutesPerShip; internal ConfigEntry<float> BattleCooldownMultiplier; internal ConfigEntry<bool> EnablePirateBattleMusic; internal ConfigEntry<string> PirateBattleMusicFile; internal ConfigEntry<string> CustomHullSizes; internal ConfigEntry<float> PirateBattleMusicVolume; internal ConfigEntry<float> SpawnRoamWakeDistance; internal ConfigEntry<float> PirateHearingRadius; internal ConfigEntry<float> SuspicionGiveUpSeconds; internal ConfigEntry<float> PirateBattleAggroRadius; internal ConfigEntry<float> PirateBattleLeashRadius; internal ConfigEntry<float> PirateCrewPlayerShipDamageMultiplier; internal ConfigEntry<float> PirateCrewPlayerShipMaxHitFraction; internal ConfigEntry<float> PirateCrewPirateShipDamageMultiplier; internal ConfigEntry<float> PirateCrewPirateShipMaxHitFraction; internal ConfigEntry<float> SurtlingProjectileSpeedMultiplier; internal ConfigEntry<float> FryslingProjectileSpeedMultiplier; internal ConfigEntry<float> WarlockProjectileSpeedMultiplier; internal ConfigEntry<float> WarlockProjectileGravityMultiplier; internal ConfigEntry<float> SeaSerpentSuppressionRadius; internal ConfigEntry<bool> VerboseLogging; private const float AutoSpawnGroupRadius = 250f; private readonly HashSet<Player> _serpentSuppressedPlayers = new HashSet<Player>(); private readonly HashSet<Player> _nextSerpentSuppressedPlayers = new HashSet<Player>(); private readonly HashSet<Character> _trackedSeaSerpents = new HashSet<Character>(); private readonly List<Character> _staleSeaSerpents = new List<Character>(); private readonly Dictionary<Ship, AutoSpawnShipState> _autoSpawnShipStates = new Dictionary<Ship, AutoSpawnShipState>(); private readonly List<Ship> _eligibleSpawnShips = new List<Ship>(); private readonly Dictionary<Ship, Player> _shipSpawnTargets = new Dictionary<Ship, Player>(); private readonly HashSet<Ship> _serpentSuppressedShips = new HashSet<Ship>(); private readonly HashSet<Ship> _recoveringShips = new HashSet<Ship>(); private int _corpseRunStatusHash; private float _nextRecoveryPublish; private long _recoveryPauseUntilTicks; private readonly HashSet<Ship> _visitedSpawnShips = new HashSet<Ship>(); private readonly List<Ship> _spawnGroupBuffer = new List<Ship>(); private readonly List<Ship> _spawnGroupQueue = new List<Ship>(); private readonly List<Ship> _readySpawnShips = new List<Ship>(); private readonly List<Ship> _staleSpawnShips = new List<Ship>(); private readonly List<Ship> _naturalSpawnedShipsBuffer = new List<Ship>(); private readonly Dictionary<Ship, long> _shipTimerAuthorityPeer = new Dictionary<Ship, long>(); private readonly List<KeyValuePair<Player, Ship>> _eligiblePlayerShips = new List<KeyValuePair<Player, Ship>>(); private float _nextGroupTimerPersist; private bool _lastPersistedGroupEncounterFought; private readonly Dictionary<Ship, ShipMotionSample> _shipMotionSamples = new Dictionary<Ship, ShipMotionSample>(); private readonly List<Ship> _shipMotionPruneBuffer = new List<Ship>(); private float _nextShipMotionPrune; private const float UnderwaySpeedThreshold = 1f; private const float UnderwaySampleSeconds = 1f; private const float UnderwayHoldSeconds = 10f; private float _nextGroupTimerHeartbeat; private float _lastPersistedGroupRemaining = -1f; private int _lastPersistedGroupEncounterId = -1; private ZRoutedRpc _registeredRoutedRpc; private const string TriggerTimerRpcName = "AtlasPirates_TriggerTimer"; private const string CleanupRpcName = "AtlasPirates_Cleanup"; private const float EncounterInitializationSeconds = 5f; private Harmony _harmony; private ZNetScene _trackedWorldScene; private float _nextMarkerScan; private bool _initialMarkerRepairDone; private float _nextSerpentScan; private float _nextSerpentRegistryFallbackScan; private bool _serpentRegistryPrimed; private bool _loggedNoWorld; private bool _manualCleanupBlocksSpawns; private int _cachedOceanEnvironmentPeriod = int.MinValue; private readonly Dictionary<OceanWeatherCacheKey, string> _cachedOceanEnvironmentNames = new Dictionary<OceanWeatherCacheKey, string>(); private readonly HashSet<string> _loggedTimerConditionsThisPeriod = new HashSet<string>(StringComparer.Ordinal); private bool _environmentReflectionResolved; private MethodInfo _getAvailableEnvironmentsMethod; private MethodInfo _selectWeightedEnvironmentMethod; private MethodInfo _getCurrentEnvironmentMethod; private MethodInfo _isNightMethod; private FieldInfo _environmentDurationField; private object _oceanEnvironmentBiomeValue; private bool _loggedOceanEnvironmentLookupFailure; private MethodInfo _worldBiomeMethod; private MethodInfo _worldBiomeSectorMethod; private MethodInfo _worldIsAshlandsMethod; private MethodInfo _worldIsDeepNorthMethod; private MethodInfo _zoneBiomeMethod; private FieldInfo _waterField; private const string SectionGeneral = "1 - General"; private const string SectionTimer = "2 - Encounter Timer"; private const string SectionEncounters = "3 - Encounters"; private const string SectionSpawning = "4 - Spawning"; private const string SectionDetection = "5 - Detection and Chase"; private const string SectionCombat = "6 - Combat"; private const string SectionVisuals = "7 - Visuals and Music"; private const string SectionCompatibility = "8 - Compatibility"; private const string SectionDiagnostics = "9 - Diagnostics"; private static readonly HashSet<string> OldConfigSections = new HashSet<string> { "Difficulty", "Spawning", "Visuals", "Encounter", "Combat", "Music", "Compatibility", "Diagnostics" }; private const float MinimumTimerSeconds = 30f; private const float TierMaximumCutSeconds = 150f; private const float MinimumTier1TimerSeconds = 60f; private bool _loggedPreEikthyrHold; private int _cachedTier = -1; private float _cachedTierUntil; private const float SideSpawnChance = 0.05f; private const float BehindSpawnChance = 0.05f; private bool _lastSpawnPointBehind; private const float AheadSpawnHalfAngle = 20f; private const float AheadSpawnFallbackHalfAngle = 45f; private bool _pendingLaneValid; private Vector3 _pendingLaneMeet; private Vector3 _pendingLaneBack; private Vector3 _pendingLaneHeading; private const float LanePatrolSeconds = 90f; private const float LaneMaxSideOffset = 35f; internal static AtlasPirateShipsPlugin Instance { get; private set; } internal PirateDifficulty Difficulty { get { if (DifficultyPreset != null) { return DifficultyPreset.Value; } return PirateDifficulty.Normal; } } internal float DifficultyTimerMultiplier { get { if (Difficulty != PirateDifficulty.Easy) { if (Difficulty != PirateDifficulty.Hard) { return 1f; } return 0.8f; } return 1.3f; } } internal float DifficultyDetectionMultiplier { get { if (Difficulty != PirateDifficulty.Easy) { if (Difficulty != PirateDifficulty.Hard) { return 1f; } return 1.15f; } return 0.85f; } } internal float DifficultyGiveUpMultiplier { get { if (Difficulty != PirateDifficulty.Easy) { if (Difficulty != PirateDifficulty.Hard) { return 1f; } return 1.5f; } return 0.75f; } } private float DifficultyCurrentTierChance { get { if (Difficulty != PirateDifficulty.Easy) { if (Difficulty != PirateDifficulty.Hard) { return 0.6f; } return 0.8f; } return 0.4f; } } private float DifficultyGroupChanceMultiplier { get { if (Difficulty != PirateDifficulty.Easy) { if (Difficulty != PirateDifficulty.Hard) { return 1f; } return 1.5f; } return 0.5f; } } internal bool IsVerboseLoggingEnabled { get { if (VerboseLogging != null) { return VerboseLogging.Value; } return false; } } internal static bool IsDebugFlying(Player player) { if ((Object)(object)player == (Object)null) { return false; } try { if (PlayerDebugFlyField != null && PlayerDebugFlyField.GetValue(player) is bool result) { return result; } ZNetView component = ((Component)player).GetComponent<ZNetView>(); ZDO val = (((Object)(object)component == (Object)null) ? null : component.GetZDO()); return val != null && val.GetBool("DebugFly", false); } catch { return false; } } private void Awake() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown Instance = this; BindConfig(); _harmony = new Harmony("com.atlas.pirateships"); PirateHarmonyPatches.Apply(_harmony, ((BaseUnityPlugin)this).Logger); PirateBattleMusic.BeginLoad(this); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Atlas Pirate Ships 1.0.0 loaded."); } private void OnDestroy() { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogDebug((object)("Harmony cleanup during plugin unload failed: " + ex.Message)); } PirateSailAppearance.ClearTextureCache(); PirateShipController.ClearCachedVisualTemplates(); if (Instance == this) { Instance = null; } } private void LateUpdate() { PirateAlertIcons.LateUpdate(this); } private void Update() { PirateBattleMusic.Update(); if (!IsWorldReady()) { return; } bool flag = IsServer(); EnsureRoutedRpcsRegistered(); PublishLocalRecovery(); if (!_initialMarkerRepairDone) { RestorePirateMarkers(); _initialMarkerRepairDone = true; _nextMarkerScan = Time.time + 180f; } else if (flag && Time.time >= _nextMarkerScan) { _nextMarkerScan = Time.time + 180f; RestorePirateMarkers(); } if (_manualCleanupBlocksSpawns) { PauseAllAutoSpawnShipStates(); return; } if (Time.time >= _nextSerpentScan) { _nextSerpentScan = Time.time + 2f; UpdateSeaSerpentSpawnSuppression(); } if (!EnableAutoSpawns.Value) { PauseAllAutoSpawnShipStates(); } else { UpdateLocalAutoSpawnGroups(); } } private void EnsureRoutedRpcsRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && instance != _registeredRoutedRpc) { instance.Register<ZDOID>("AtlasPirates_TriggerTimer", (Action<long, ZDOID>)RPC_TriggerTimer); instance.Register("AtlasPirates_Cleanup", (Action<long>)RPC_Cleanup); _registeredRoutedRpc = instance; } } internal bool CanUseDebugCommands() { if (!IsServer()) { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.LocalPlayerIsAdminOrHost(); } return false; } return true; } private ConfigEntry<float> BindFloat(string section, string key, float defaultValue, float minimum, float maximum, string description) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown return ((BaseUnityPlugin)this).Config.Bind<float>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(minimum, maximum), Array.Empty<object>())); } private void BindConfig() { DifficultyPreset = ((BaseUnityPlugin)this).Config.Bind<PirateDifficulty>("1 - General", "DifficultyPreset", PirateDifficulty.Normal, "Overall difficulty. Normal is the standard balance.\nEasy: 40% of encounters at your current tier (the rest from older, easier tiers), half the chance of fleets and Pirate Battles, 30% more time between encounters, 15% shorter spotting and hearing range, pirates give up after 45 s without landing a hit.\nHard: 80% of encounters at your current tier, 1.5x the chance of fleets and Pirate Battles, 20% less time between encounters, 15% longer spotting and hearing range, pirates give up after 90 s without a hit.\nThe preset scales the settings below, so your own values still apply on top of it."); EnableAutoSpawns = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "EnableAutoSpawns", true, "Pirate encounters happen on their own while you sail, once Eikthyr is defeated. Turn off to only get pirates from console commands."); SpawnIntervalMinSeconds = BindFloat("2 - Encounter Timer", "SpawnIntervalMinSeconds", 210f, 60f, 86400f, "Shortest sailing time between encounters, in seconds (default 3.5 minutes). Each wait is rolled between this and SpawnIntervalSeconds, then scaled by the world tier multipliers below. The timer only counts while you sail and pauses while an encounter is still going."); SpawnIntervalSeconds = BindFloat("2 - Encounter Timer", "SpawnIntervalSeconds", 480f, 60f, 86400f, "Longest sailing time between encounters, in seconds (default 8 minutes), before the world tier multiplier. Each tier's longest wait is then 2.5 minutes shorter."); TimerMultiplierTier1 = BindFloat("2 - Encounter Timer", "TimerMultiplierTier1", 0.8f, 0.25f, 4f, "Timer multiplier at world Tier 1 (only Eikthyr defeated). Below 1 = more often. The shortest wait is then cut by 2 minutes (never below 1 minute) and the longest by 2.5 minutes, so the default 0.8 gives about 1-3.9 minutes."); TimerMultiplierTier2To3 = BindFloat("2 - Encounter Timer", "TimerMultiplierTier2To3", 1f, 0.25f, 4f, "Timer multiplier at world Tiers 2-3. The shortest wait is then cut by 1:40 at Tier 2 and 1:20 at Tier 3, and the longest by 2.5 minutes, so the default 1.0 gives about 1.8-5.5 minutes (Tier 2) and 2.2-5.5 minutes (Tier 3)."); TimerMultiplierTier4To5 = BindFloat("2 - Encounter Timer", "TimerMultiplierTier4To5", 1.15f, 0.25f, 4f, "Timer multiplier at world Tiers 4-5. The shortest wait is then cut by 1:00 at Tier 4 and 0:40 at Tier 5, and the longest by 2.5 minutes, so the default 1.15 gives about 3-6.7 minutes (Tier 4) and 3.4-6.7 minutes (Tier 5)."); TimerMultiplierTier6To7 = BindFloat("2 - Encounter Timer", "TimerMultiplierTier6To7", 1.3f, 0.25f, 4f, "Timer multiplier at world Tiers 6-7. The shortest wait is then cut by 0:20 at Tier 6 (none at Tier 7), and the longest by 2.5 minutes, so the default 1.3 gives about 4.2-7.9 minutes (Tier 6) and 4.6-7.9 minutes (Tier 7)."); ClearDayTimerSpeedMultiplier = BindFloat("2 - Encounter Timer", "ClearDayTimerSpeedMultiplier", 1f, 0.1f, 5f, "How fast the timer counts down in clear daytime weather. 1.0 = normal speed."); FogTimerSpeedMultiplier = BindFloat("2 - Encounter Timer", "FogTimerSpeedMultiplier", 1.2f, 0.1f, 5f, "How fast the timer counts down in daytime fog or mist. 1.2 = 20% faster."); NightTimerSpeedMultiplier = BindFloat("2 - Encounter Timer", "NightTimerSpeedMultiplier", 1.2f, 0.1f, 5f, "How fast the timer counts down at night. 1.2 = 20% faster."); NightFogTimerSpeedMultiplier = BindFloat("2 - Encounter Timer", "NightFogTimerSpeedMultiplier", 1.5f, 0.1f, 5f, "How fast the timer counts down in fog at night (used instead of stacking the night and fog values)."); HeavyRainTimerSpeedMultiplier = BindFloat("2 - Encounter Timer", "HeavyRainTimerSpeedMultiplier", 1f, 0.1f, 5f, "How fast the timer counts down in heavy rain, day or night. 1.0 = normal speed."); ThunderstormTimerSpeedMultiplier = BindFloat("2 - Encounter Timer", "ThunderstormTimerSpeedMultiplier", 0.8f, 0.1f, 5f, "How fast the timer counts down in thunderstorms, day or night. 0.8 = 20% slower."); HeavyEncounterMinutesPerTier = BindFloat("2 - Encounter Timer", "HeavyEncounterMinutesPerTier", 0.5f, 0f, 30f, "Extra wait before the next encounter after a real fight, per encounter tier above 3 (Tier 4 +0.5 min, Tier 7 +2 min at the default). Encounters you avoided add nothing."); FleetExtraMinutesPerShip = BindFloat("2 - Encounter Timer", "FleetExtraMinutesPerShip", 0.5f, 0f, 30f, "After a real fight with a fleet, extra minutes per ship on top of HeavyEncounterMinutesPerTier (a 3-ship fleet adds 1.5)."); BattleCooldownMultiplier = BindFloat("2 - Encounter Timer", "BattleCooldownMultiplier", 2f, 0f, 10f, "After a real fight in a Pirate Battle, the extra wait for its largest fleet is multiplied by this (2 = doubled)."); VictoryBonusMinutes = BindFloat("2 - Encounter Timer", "VictoryBonusMinutes", 1f, 0f, 30f, "Extra minutes before the next encounter when players sank at least one of the encounter's pirate ships, at any tier. A reward for winning, on top of any heavy-encounter wait. 0 = off."); DeathRecoveryPauseMinutes = BindFloat("2 - Encounter Timer", "DeathRecoveryPauseMinutes", 10f, 0f, 120f, "After you die, the timer pauses for up to this many minutes while you go back for your gear. It ends early once your gear is recovered. 0 = off."); OceanEntryGraceSeconds = BindFloat("2 - Encounter Timer", "OceanEntryGraceSeconds", 20f, 0f, 3600f, "A ship that has just started sailing (or just entered the Ocean) can't be picked for an encounter for this many seconds. The timer keeps counting meanwhile."); SeaSerpentSuppressionRadius = BindFloat("2 - Encounter Timer", "SeaSerpentSuppressionRadius", 300f, 25f, 5000f, "A living Sea Serpent within this many meters pauses pirate encounters, so serpents get their turn. Only serpents loaded in your game count. When the pause ends the timer restarts with a fresh roll."); EnableFleetSpawns = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Encounters", "EnableFleetSpawns", true, "Encounters can be fleets of 2-4 pirate ships hunting together."); FleetSpawnChance = BindFloat("3 - Encounters", "FleetSpawnChance", 0.25f, 0f, 1f, "Chance an encounter is a fleet, from Tier 4 on. Lower tiers use less: Tier 1 a fixed 10%, Tiers 2 and 3 72% and 88% of this. Fleet size follows your boat (Raft 2 ships, Karve 2-3, Longship and Drakkar 2-4)."); EnablePirateBattles = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Encounters", "EnablePirateBattles", true, "From Tier 2, encounters can be Pirate Battles: 2-3 rival pirate fleets fighting each other, who turn on you if you join in."); PirateBattleChance = BindFloat("3 - Encounters", "PirateBattleChance", 0.1f, 0f, 1f, "Chance an encounter is a Pirate Battle, from Tier 4 on. Tier 1 never has one; Tiers 2 and 3 use 40% and 80% of this."); HigherHullChance = BindFloat("3 - Encounters", "HigherHullChance", 0.05f, 0f, 1f, "Chance a pirate ship is one boat size bigger than yours (full chance on a Raft, 60% of it on a Karve or Longship, never against a Drakkar)."); BoardingBehaviorChance = BindFloat("3 - Encounters", "BoardingBehaviorChance", 0.35f, 0f, 1f, "Chance that one ship in an encounter (or in each Pirate Battle fleet) tries to board you, sending crew leaping onto your deck."); MeleeBoarderChance = BindFloat("3 - Encounters", "MeleeBoarderChance", 0.5f, 0f, 1f, "Chance for each boarder on a boarding ship to be a melee fighter instead of an archer or caster. 0 = boarders are always ranged crew."); RamBehaviorChance = BindFloat("3 - Encounters", "RamBehaviorChance", 0.6f, 0f, 1f, "Chance a pirate ship rams you rather than sailing alongside and shooting (broadside)."); PirateBattleRamBehaviorChance = BindFloat("3 - Encounters", "PirateBattleRamBehaviorChance", 0.25f, 0f, 1f, "Chance a pirate ship rams a rival pirate ship in a Pirate Battle rather than fighting broadside."); PirateBattleAggroRadius = BindFloat("3 - Encounters", "PirateBattleAggroRadius", 220f, 120f, 5000f, "How far away, in meters, Pirate Battle ships spot rival pirate ships."); PirateBattleLeashRadius = BindFloat("3 - Encounters", "PirateBattleLeashRadius", 400f, 100f, 10000f, "How far, in meters, Pirate Battle ships roam from where the battle started before turning back."); SpawnDistanceMin = BindFloat("4 - Spawning", "SpawnDistanceMin", 170f, 70f, 250f, "Closest distance from you, in meters, at which new pirates appear. The default is just beyond the range at which they hear a ship under full sail (110-150 m). Pirates more than 300 m away are removed, so keep this well below that."); SpawnDistanceMax = BindFloat("4 - Spawning", "SpawnDistanceMax", 210f, 80f, 260f, "Farthest distance from you, in meters, at which new pirates appear."); StrictOceanOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Spawning", "StrictOceanOnly", true, "Pirates only appear in Ocean water, and pirates with no target keep to the Ocean."); SpawnRoamSeconds = BindFloat("4 - Spawning", "SpawnRoamSeconds", 10f, 0f, 120f, "New pirates roam for this many seconds before they can target you, and can't hear you meanwhile. Attacking them, or getting within SpawnRoamWakeDistance, ends it early. 0 = off."); SpawnRoamWakeDistance = BindFloat("4 - Spawning", "SpawnRoamWakeDistance", 50f, 0f, 150f, "Distance, in meters, at which a roaming new pirate notices a Raft and ends its roaming early. Each bigger boat adds 10 m (Karve 60, Longship 70, Drakkar 80). 0 = only an attack ends it."); EnableSpawnFade = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Spawning", "EnableSpawnFade", true, "Pirates appear out of a mist effect instead of popping in."); SpawnFadeDurationSeconds = BindFloat("4 - Spawning", "SpawnFadeDurationSeconds", 1.5f, 0.75f, 8f, "How long, in seconds, the ship and crew take to fade in (the mist before and after is extra)."); AggroVisionRadius = BindFloat("5 - Detection and Chase", "AggroVisionRadius", 70f, 50f, 5000f, "Distance, in meters, at which pirates spot a Raft and attack. Each bigger boat adds 10 m (Drakkar 100 m). Once they spot you they keep chasing well beyond this."); PirateHearingRadius = BindFloat("5 - Detection and Chase", "PirateHearingRadius", 120f, 0f, 500f, "Distance, in meters, at which pirates hear a ship under full sail and come looking, without attacking until they spot you. Each bigger boat adds 10 m, and Rafts and Karves are quieter (Raft 110, Karve 120, Longship 140, Drakkar 150 at the defaults). Half sail is heard from 70% of this, rowing from 35%, a stopped ship not at all. 0 = pirates never hear you."); RaftHearingReduction = BindFloat("5 - Detection and Chase", "RaftHearingReduction", 10f, 0f, 200f, "Meters taken off the hearing distance for a Raft, so small boats are easier to sneak past. 0 = off."); KarveHearingReduction = BindFloat("5 - Detection and Chase", "KarveHearingReduction", 10f, 0f, 200f, "Meters taken off the hearing distance for a Karve. 0 = off."); SuspicionGiveUpSeconds = BindFloat("5 - Detection and Chase", "SuspicionGiveUpSeconds", 50f, 10f, 300f, "How long, in seconds, pirates who heard you keep searching after they last heard you. They then go back to roaming and can't turn suspicious again for 30 s."); GiveUpWithoutHitSeconds = BindFloat("5 - Detection and Chase", "GiveUpWithoutHitSeconds", 60f, 0f, 600f, "A pirate ship chasing you gives up and sails off after this many seconds without landing a hit (on you, your ship, or a ram) or getting closer. The clock restarts on every hit and every time it gets 20 m closer than before, so a pirate still gaining on you keeps coming. Scaled by DifficultyPreset (Easy 45 s, Hard 90 s at the default). 0 = never gives up."); ShipSpeedMultiplier = BindFloat("5 - Detection and Chase", "ShipSpeedMultiplier", 1f, 0.1f, 3f, "Pirate ship sailing speed. Above 1 makes pirates harder to escape."); PirateCrewPlayerShipDamageMultiplier = BindFloat("6 - Combat", "PirateCrewPlayerShipDamageMultiplier", 0.5f, 0f, 100f, "Damage pirate crew attacks do to your boat. Also scaled by the pirates' tier (x1.0 at Tiers 1-3, then 0.1 less per tier down to x0.6 at Tier 7) and your boat (Raft x0.7, Karve x0.8, Longship x0.9, Drakkar x1.0). Rams are normal collision damage and not affected."); PirateCrewPlayerShipMaxHitFraction = BindFloat("6 - Combat", "PirateCrewPlayerShipMaxHitFraction", 0.1f, 0f, 1f, "Most of your boat's max health one pirate hit can take (0.1 = 10%)."); PirateCrewPirateShipDamageMultiplier = BindFloat("6 - Combat", "PirateCrewPirateShipDamageMultiplier", 0.35f, 0f, 100f, "Damage pirate crew attacks do to rival pirate ships in Pirate Battles. Pirates never damage their own or allied ships."); PirateCrewPirateShipMaxHitFraction = BindFloat("6 - Combat", "PirateCrewPirateShipMaxHitFraction", 0.07f, 0f, 1f, "Most of a rival pirate ship's max health one pirate hit can take (0.07 = 7%)."); PirateRangedAttackRange = BindFloat("6 - Combat", "PirateRangedAttackRange", 22f, 6f, 40f, "Range, in meters, from which pirate archers and casters are willing to attack. Cultists use their own setting below."); PirateCultistFlameReach = BindFloat("6 - Combat", "PirateCultistFlameReach", 8f, 4f, 16f, "How far, in meters, a pirate Cultist's flame breath reaches. World Cultists are unchanged."); PirateCultistAIAttackRange = BindFloat("6 - Combat", "PirateCultistAIAttackRange", 10f, 4f, 20f, "Farthest distance, in meters, from which pirate Cultists use their flame breath."); SurtlingProjectileSpeedMultiplier = BindFloat("6 - Combat", "SurtlingProjectileSpeedMultiplier", 1.45f, 0.5f, 3f, "Speed of pirate Surtlings' fireballs (1 = vanilla), so they can reach across the water."); FryslingProjectileSpeedMultiplier = BindFloat("6 - Combat", "FryslingProjectileSpeedMultiplier", 1.35f, 0.5f, 3f, "Speed of pirate Fryslings' projectiles (1 = vanilla)."); WarlockProjectileSpeedMultiplier = BindFloat("6 - Combat", "WarlockProjectileSpeedMultiplier", 1.2f, 0.5f, 3f, "Speed of pirate Charred Warlocks' projectiles (1 = vanilla)."); WarlockProjectileGravityMultiplier = BindFloat("6 - Combat", "WarlockProjectileGravityMultiplier", 0.3f, 0f, 1.5f, "Gravity on pirate Charred Warlocks' projectiles (1 = vanilla). Lower = flatter shots over the water."); SinkDurationSeconds = BindFloat("6 - Combat", "SinkDurationSeconds", 12f, 2f, 600f, "How long, in seconds, a defeated pirate ship takes to sink before it disappears."); SinkForce = BindFloat("6 - Combat", "SinkForce", 0.85f, 0.05f, 20f, "How fast a defeated pirate ship goes under while sinking."); EnableFactionSails = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Visuals and Music", "EnableFactionSails", true, "Pirate sails are colored by their crew's faction."); EnableFactionSailEmblems = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Visuals and Music", "EnableFactionSailEmblems", true, "Pirate sails carry their faction's emblem."); EnableShipAlertIcons = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Visuals and Music", "EnableShipAlertIcons", true, "Show vanilla-style alert icons above pirate ships within 150 m while you look at them: yellow while a ship is searching for a player, red when it has spotted one. The red icon fades 5 s after the pirates start attacking."); EnablePirateBattleMusic = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Visuals and Music", "EnablePirateBattleMusic", true, "Play pirate battle music once a pirate ship spots you. It keeps playing while pirates are after you, on your deck, or any pirate ship is within 100 m, and ends 5 s after that. Raid music still takes priority."); PirateBattleMusicFile = ((BaseUnityPlugin)this).Config.Bind<string>("7 - Visuals and Music", "PirateBattleMusicFile", "treasure_hunter.ogg", "Song file in the AtlasPirateShips plugin folder: .ogg (recommended), .wav or .mp3. Replace it to use your own music."); PirateBattleMusicVolume = BindFloat("7 - Visuals and Music", "PirateBattleMusicVolume", 1f, 0f, 2f, "Battle music volume. 1 = the same level as Valheim's boss music; the in-game music volume slider still applies."); CustomHullSizes = ((BaseUnityPlugin)this).Config.Bind<string>("8 - Compatibility", "CustomHullSizes", string.Empty, "Sizes for ships added by other mods, so pirates treat them like the matching vanilla boat (spotting and hearing distance, encounter size, how many boarders fit). Comma-separated PrefabName:Size pairs, where Size is Raft, Karve, Longship or Drakkar (or 1-4). Example: BigCargoShip:Drakkar, SmallSkiff:Raft. Ship names containing raft, karve, longship, vikingship or ashlands are recognised automatically; any other ship counts as a Raft. The vanilla ships themselves cannot be remapped."); PirateDefinitions.SetCustomHullSizes(CustomHullSizes.Value); CustomHullSizes.SettingChanged += delegate { PirateDefinitions.SetCustomHullSizes(CustomHullSizes.Value); }; VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("9 - Diagnostics", "VerboseLogging", false, "Write detailed pirate AI, movement and network information to BepInEx/LogOutput.log. Useful for bug reports; leave off for normal play."); MoveSettingsFromOldSections(); ConfigEntry<int> val = ((BaseUnityPlugin)this).Config.Bind<int>("Internal", "SettingsRevision", 0, "Internal settings-migration marker. Do not edit."); if (val.Value < 1) { if (Mathf.Approximately(SpawnIntervalMinSeconds.Value, 420f)) { SpawnIntervalMinSeconds.Value = 300f; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated SpawnIntervalMinSeconds from the old 420 s default to the new 300 s default."); } val.Value = 1; } if (val.Value < 2) { if (Mathf.Approximately(SpawnIntervalSeconds.Value, 1200f)) { SpawnIntervalSeconds.Value = 720f; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated SpawnIntervalSeconds from the old 1200 s default to the new 720 s default."); } val.Value = 2; } if (val.Value < 3) { MigrateDefault(SpawnIntervalMinSeconds, 300f, 210f); MigrateDefault(SpawnIntervalSeconds, 720f, 480f); MigrateDefault(AggroVisionRadius, 120f, 70f); MigrateDefault(SpawnRoamWakeDistance, 60f, 50f); val.Value = 3; } if (val.Value < 4) { MigrateDefault(FogTimerSpeedMultiplier, 1.5f, 1.2f); MigrateDefault(NightFogTimerSpeedMultiplier, 1.75f, 1.5f); MigrateDefault(HeavyRainTimerSpeedMultiplier, 0.8f, 1f); MigrateDefault(ThunderstormTimerSpeedMultiplier, 0.6f, 0.8f); val.Value = 4; } if (val.Value < 5) { MigrateDefault(SeaSerpentSuppressionRadius, 200f, 300f); val.Value = 5; } if (val.Value < 6) { MigrateDefault(BoardingBehaviorChance, 0.2f, 0.35f); val.Value = 6; } if (val.Value < 7) { MigrateDefault(SpawnDistanceMin, 130f, 170f); MigrateDefault(SpawnDistanceMax, 170f, 210f); val.Value = 7; } if (val.Value < 8) { MigrateDefault(SpawnRoamSeconds, 15f, 10f); val.Value = 8; } if (val.Value < 9) { MigrateDefault(GiveUpWithoutHitSeconds, 30f, 40f); val.Value = 9; } if (val.Value < 10) { MigrateDefault(GiveUpWithoutHitSeconds, 40f, 60f); val.Value = 10; } if (val.Value < 11) { MigrateDefault(SpawnIntervalSeconds, 480f, 600f); val.Value = 11; } if (val.Value < 12) { MigrateDefault(SpawnIntervalSeconds, 600f, 480f); val.Value = 12; } NormalizeConfigRelationships(); } private void MoveSettingsFromOldSections() { try { if (!(AccessTools.Property(typeof(ConfigFile), "OrphanedEntries")?.GetValue(((BaseUnityPlugin)this).Config) is Dictionary<ConfigDefinition, string> { Count: not 0 } dictionary)) { return; } Dictionary<string, ConfigEntryBase> dictionary2 = new Dictionary<string, ConfigEntryBase>(); foreach (ConfigDefinition key in ((BaseUnityPlugin)this).Config.Keys) { dictionary2[key.Key] = ((BaseUnityPlugin)this).Config[key]; } int num = 0; int num2 = 0; foreach (KeyValuePair<ConfigDefinition, string> item in dictionary.ToList()) { if (OldConfigSections.Contains(item.Key.Section)) { if (dictionary2.TryGetValue(item.Key.Key, out var value) && value.Definition.Section != item.Key.Section) { value.SetSerializedValue(item.Value); num++; } else { num2++; } dictionary.Remove(item.Key); } } if (num + num2 > 0) { ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Moved " + num + " settings into the new config sections" + ((num2 > 0) ? (" and removed " + num2 + " retired setting(s)") : "") + ".")); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not carry settings over from the old config sections (they use defaults instead): " + ex.Message)); } } private void NormalizeConfigRelationships() { if (SpawnIntervalSeconds.Value < SpawnIntervalMinSeconds.Value) { SpawnIntervalSeconds.Value = SpawnIntervalMinSeconds.Value; ((BaseUnityPlugin)this).Logger.LogWarning((object)"SpawnIntervalSeconds was below SpawnIntervalMinSeconds and has been raised to match it."); } float num = Mathf.Min(260f, SpawnDistanceMin.Value + 10f); if (SpawnDistanceMax.Value < num) { SpawnDistanceMax.Value = num; ((BaseUnityPlugin)this).Logger.LogWarning((object)"SpawnDistanceMax was too close to SpawnDistanceMin and has been raised to preserve the 10 m placement band."); } if (PirateBattleLeashRadius.Value < PirateBattleAggroRadius.Value) { PirateBattleLeashRadius.Value = PirateBattleAggroRadius.Value; ((BaseUnityPlugin)this).Logger.LogWarning((object)"PirateBattleLeashRadius was below PirateBattleAggroRadius and has been raised to match it."); } if (Mathf.Clamp01(PirateBattleChance.Value) + Mathf.Clamp01(FleetSpawnChance.Value) > 1.0001f) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"PirateBattleChance + FleetSpawnChance exceeds 1.0. Pirate Battles keep their configured chance and the normal-fleet group is naturally truncated by the shared encounter roll."); } } internal bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } internal bool IsHeadlessDedicatedServer() { if (Application.isBatchMode) { return IsServer(); } return false; } internal void RegisterSeaSerpentCandidate(GameObject candidate) { if ((Object)(object)candidate == (Object)null) { return; } string name = ((Object)candidate).name; if (!string.IsNullOrEmpty(name) && name.StartsWith("Serpent", StringComparison.OrdinalIgnoreCase)) { Character component = candidate.GetComponent<Character>(); if ((Object)(object)component != (Object)null) { _trackedSeaSerpents.Add(component); } } } private static bool IsSeaSerpentCharacter(Character character) { if ((Object)(object)character == (Object)null) { return false; } string text = (((Object)(object)((Component)character).gameObject == (Object)null) ? string.Empty : ((Object)((Component)character).gameObject).name); if (!string.IsNullOrEmpty(text)) { return text.StartsWith("Serpent", StringComparison.OrdinalIgnoreCase); } return false; } private void RefreshSeaSerpentRegistryFromScene() { Character[] array = Object.FindObjectsByType<Character>((FindObjectsSortMode)0); foreach (Character val in array) { if (IsSeaSerpentCharacter(val)) { _trackedSeaSerpents.Add(val); } } _serpentRegistryPrimed = true; _nextSerpentRegistryFallbackScan = Time.time + 60f; } private bool IsWorldReady() { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || Player.GetAllPlayers() == null) { if ((Object)(object)instance == (Object)null) { _trackedWorldScene = null; _initialMarkerRepairDone = false; _nextMarkerScan = 0f; ClearAutoSpawnShipStates(); _serpentSuppressedPlayers.Clear(); _nextSerpentSuppressedPlayers.Clear(); _trackedSeaSerpents.Clear(); _staleSeaSerpents.Clear(); _serpentRegistryPrimed = false; _nextSerpentRegistryFallbackScan = 0f; _manualCleanupBlocksSpawns = false; ResetOceanEnvironmentTimerCache(); _shipMotionSamples.Clear(); } if (!_loggedNoWorld && Time.time > 10f) { _loggedNoWorld = true; ((BaseUnityPlugin)this).Logger.LogDebug((object)"Waiting for a loaded Valheim world."); } return false; } _loggedNoWorld = false; if ((Object)(object)_trackedWorldScene != (Object)(object)instance) { _trackedWorldScene = instance; _initialMarkerRepairDone = false; _nextMarkerScan = 0f; ClearAutoSpawnShipStates(); _serpentSuppressedPlayers.Clear(); _nextSerpentSuppressedPlayers.Clear(); _trackedSeaSerpents.Clear(); _staleSeaSerpents.Clear(); _serpentRegistryPrimed = false; _nextSerpentRegistryFallbackScan = 0f; _manualCleanupBlocksSpawns = false; ResetOceanEnvironmentTimerCache(); _shipMotionSamples.Clear(); } return true; } private int GetCorpseRunStatusHash() { if (_corpseRunStatusHash != 0 || (Object)(object)ObjectDB.instance == (Object)null) { return _corpseRunStatusHash; } foreach (StatusEffect statusEffect in ObjectDB.instance.m_StatusEffects) { if ((Object)(object)statusEffect != (Object)null && (((Object)statusEffect).name == "CorpseRun" || statusEffect.m_name == "$se_corpserun_name")) { _corpseRunStatusHash = statusEffect.NameHash(); return _corpseRunStatusHash; } } _corpseRunStatusHash = StringExtensionMethods.GetStableHashCode("CorpseRun"); return _corpseRunStatusHash; } private bool LocalPlayerHasCorpseRun(Player player) { int corpseRunStatusHash = GetCorpseRunStatusHash(); SEMan val = (((Object)(object)player == (Object)null) ? null : ((Character)player).GetSEMan()); if (corpseRunStatusHash != 0 && val != null) { return val.HaveStatusEffect(corpseRunStatusHash); } return false; } internal void StartDeathRecoveryPause() { float num = ((DeathRecoveryPauseMinutes == null) ? 10f : Mathf.Clamp(DeathRecoveryPauseMinutes.Value, 0f, 120f)); if (!(num <= 0f) && !((Object)(object)ZNet.instance == (Object)null)) { _recoveryPauseUntilTicks = ZNet.instance.GetTime().Ticks + (long)(num * 60f * 10000000f); _nextRecoveryPublish = 0f; LogVerbose("Local player died; pirate timer paused for up to " + num.ToString("0.#") + " minutes or until their gear is recovered."); } } private bool LocalPlayerRecoveringGear() { if (_recoveryPauseUntilTicks > 0 && (Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.GetTime().Ticks < _recoveryPauseUntilTicks; } return false; } private void PublishLocalRecovery() { if (Time.time < _nextRecoveryPublish) { return; } _nextRecoveryPublish = Time.time + 1f; Player localPlayer = Player.m_localPlayer; if (_recoveryPauseUntilTicks > 0 && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && (!LocalPlayerRecoveringGear() || LocalPlayerHasCorpseRun(localPlayer))) { _recoveryPauseUntilTicks = 0L; LogVerbose("Gear recovery pause ended."); } ZNetView val = (((Object)(object)localPlayer == (Object)null) ? null : ((Component)localPlayer).GetComponent<ZNetView>()); ZDO val2 = (((Object)(object)val == (Object)null) ? null : val.GetZDO()); if (val2 != null && val2.IsOwner()) { if (!val2.GetBool("AtlasPirateShipsInstalled", false)) { val2.Set("AtlasPirateShipsInstalled", true); } bool flag = LocalPlayerRecoveringGear(); if (val2.GetBool("AtlasPirateRecoveringGear", false) != flag) { val2.Set("AtlasPirateRecoveringGear", flag); } } } private bool IsPlayerRecoveringGear(Player player) { if ((Object)(object)player == (Object)null) { return false; } if ((Object)(object)player == (Object)(object)Player.m_localPlayer) { return LocalPlayerRecoveringGear(); } ZNetView component = ((Component)player).GetComponent<ZNetView>(); ZDO val = (((Object)(object)component == (Object)null) ? null : component.GetZDO()); if (val != null) { return val.GetBool("AtlasPirateRecoveringGear", false); } return false; } private void MigrateDefault(ConfigEntry<float> entry, float oldDefault, float newDefault) { if (entry != null && Mathf.Approximately(entry.Value, oldDefault)) { entry.Value = newDefault; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Migrated " + ((ConfigEntryBase)entry).Definition.Key + " from the old " + oldDefault.ToString("0.##") + " default to the new " + newDefault.ToString("0.##") + " default.")); } } private float RollAutoSpawnDelaySeconds() { GetTierTimerRange(GetCurrentTier(), out var min, out var max); return Random.Range(min, max) * DifficultyTimerMultiplier; } private void GetTierTimerRange(int tier, out float min, out float max) { float num = Mathf.Max(60f, (SpawnIntervalMinSeconds != null) ? SpawnIntervalMinSeconds.Value : 210f); float num2 = Mathf.Max(num, (SpawnIntervalSeconds != null) ? SpawnIntervalSeconds.Value : 480f); float spawnTimerTierMultiplier = GetSpawnTimerTierMultiplier(tier); float num3 = ((tier <= 1) ? 60f : 30f); max = Mathf.Max(num3, num2 * spawnTimerTierMultiplier - 150f); min = Mathf.Clamp(num * spawnTimerTierMultiplier - GetTierMinimumCutSeconds(tier), Mathf.Min(num3, max), max); } private static float GetTierMinimumCutSeconds(int tier) { return Mathf.Max(0f, 120f - 20f * (float)(Mathf.Clamp(tier, 1, 7) - 1)); } private float GetHeavyEncounterCooldownSeconds(List<Ship> spawnedShips) { if (spawnedShips == null || spawnedShips.Count == 0) { return 0f; } int num = 0; bool flag = false; int num2 = 0; Dictionary<int, int> dictionary = new Dictionary<int, int>(); for (int i = 0; i < spawnedShips.Count; i++) { Ship val = spawnedShips[i]; ZNetView val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent<ZNetView>()); ZDO val3 = (((Object)(object)val2 == (Object)null) ? null : val2.GetZDO()); if (val3 != null) { num2++; num = Mathf.Max(num, val3.GetInt("AtlasPirateTier", 0)); flag |= val3.GetBool("AtlasPirateBattle", false); int key = val3.GetInt("AtlasPirateFleetId", 0); dictionary[key] = ((!dictionary.TryGetValue(key, out var value)) ? 1 : (value + 1)); } } float num3 = ((HeavyEncounterMinutesPerTier == null) ? 0.5f : Mathf.Max(0f, HeavyEncounterMinutesPerTier.Value)); float num4 = ((FleetExtraMinutesPerShip == null) ? 0.5f : Mathf.Max(0f, FleetExtraMinutesPerShip.Value)); float num5 = (float)Mathf.Max(0, num - 3) * num3; float num6; if (!flag) { num6 = ((num2 < 2) ? num5 : (num5 + (float)num2 * num4)); } else { int num7 = 0; foreach (int value2 in dictionary.Values) { num7 = Mathf.Max(num7, value2); } num6 = (num5 + (float)num7 * num4) * ((BattleCooldownMultiplier == null) ? 2f : Mathf.Max(0f, BattleCooldownMultiplier.Value)); } return num6 * 60f; } private float GetSpawnTimerTierMultiplier(int tier) { ConfigEntry<float> val = ((tier <= 1) ? TimerMultiplierTier1 : ((tier <= 3) ? TimerMultiplierTier2To3 : ((tier <= 5) ? TimerMultiplierTier4To5 : TimerMultiplierTier6To7))); if (val != null) { return Mathf.Clamp(val.Value, 0.25f, 4f); } return 1f; } internal int TriggerNaturalSpawnTimerNow(Player player, out bool forwardedToAnotherPeer) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) forwardedToAnotherPeer = false; if ((Object)(object)player == (Object)null || ((Character)player).IsDead()) { return 0; } Ship playerShip = PirateReflection.GetPlayerShip(player); if ((Object)(object)playerShip == (Object)null || IsPirateShip(playerShip)) { return 0; } CollectAutoSpawnEligibleShips(); if (!_eligibleSpawnShips.Contains(playerShip)) { return 0; } BuildEligibleGroup(playerShip); int count = _spawnGroupBuffer.Count; long groupTimerAuthority = GetGroupTimerAuthority(_spawnGroupBuffer); if (groupTimerAuthority != 0L && groupTimerAuthority != PirateOwnership.LocalPeerId) { ZNetView component = ((Component)playerShip).GetComponent<ZNetView>(); ZDO val = (((Object)(object)component == (Object)null) ? null : component.GetZDO()); if (ZRoutedRpc.instance == null || val == null) { return 0; } ZRoutedRpc.instance.InvokeRoutedRPC(groupTimerAuthority, "AtlasPirates_TriggerTimer", new object[1] { val.m_uid }); forwardedToAnotherPeer = true; return count; } ExpireGroupTimer(_spawnGroupBuffer); if (VerboseLogging != null && VerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("pirates_spawn expired the natural encounter timer for " + count + " sailing ship" + ((count == 1) ? "" : "s") + "; normal natural-spawn logic will handle the encounter.")); } return count; } private void RPC_TriggerTimer(long sender, ZDOID shipId) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNetScene.instance == (Object)null || shipId == ZDOID.None) { return; } GameObject val = ZNetScene.instance.FindInstance(shipId); Ship val2 = (((Object)(object)val == (Object)null) ? null : val.GetComponent<Ship>()); if ((Object)(object)val2 == (Object)null) { return; } CollectAutoSpawnEligibleShips(); if (_eligibleSpawnShips.Contains(val2)) { BuildEligibleGroup(val2); if (GetGroupTimerAuthority(_spawnGroupBuffer) == PirateOwnership.LocalPeerId) { ExpireGroupTimer(_spawnGroupBuffer); LogVerbose("pirates_spawn request from another player expired this sailing group's natural encounter timer."); } } } private void BuildEligibleGroup(Ship seed) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) _spawnGroupBuffer.Clear(); _visitedSpawnShips.Clear(); _spawnGroupQueue.Clear(); _spawnGroupQueue.Add(seed); _visitedSpawnShips.Add(seed); float num = 62500f; int num2 = 0; while (num2 < _spawnGroupQueue.Count) { Ship val = _spawnGroupQueue[num2++]; if ((Object)(object)val == (Object)null) { continue; } _spawnGroupBuffer.Add(val); Vector3 position = ((Component)val).transform.position; for (int i = 0; i < _eligibleSpawnShips.Count; i++) { Ship val2 = _eligibleSpawnShips[i]; if (!((Object)(object)val2 == (Object)null) && !_visitedSpawnShips.Contains(val2)) { Vector3 val3 = ((Component)val2).transform.position - position; val3.y = 0f; if (((Vector3)(ref val3)).sqrMagnitude <= num) { _visitedSpawnShips.Add(val2); _spawnGroupQueue.Add(val2); } } } } } private long GetGroupTimerAuthority(List<Ship> group) { long num = 0L; for (int i = 0; i < group.Count; i++) { Ship val = group[i]; if ((Object)(object)val != (Object)null && _shipTimerAuthorityPeer.TryGetValue(val, out var value) && value != 0L && (num == 0L || value < num)) { num = value; } } return num; } private void ExpireGroupTimer(List<Ship> group) { float num = Mathf.Max(0f, (OceanEntryGraceSeconds != null) ? OceanEntryGraceSeconds.Value : 20f); for (int i = 0; i < group.Count; i++) { Ship val = group[i]; if (!((Object)(object)val == (Object)null)) { if (_autoSpawnShipStates.TryGetValue(val, out var value) && value != null) { value.Remaining = 0f; continue; } _autoSpawnShipStates[val] = new AutoSpawnShipState { Remaining = 0f, GraceUntil = Time.time + num, GraceUsedThisCycle = true, ActiveLastFrame = false, ActiveThisFrame = false, SerpentSuppressedLastFrame = false, EncounterPausedLastFrame = false, ActiveEncounter = null }; } } } private void ResetOceanEnvironmentTimerCache() { _cachedOceanEnvironmentPeriod = int.MinValue; _cachedOceanEnvironmentNames.Clear(); _loggedTimerConditionsThisPeriod.Clear(); _loggedOceanEnvironmentLookupFailure = false; } private void ResolveEnvironmentReflection() { if (_environmentReflectionResolved) { return; } _environmentReflectionResolved = true; Type typeFromHandle = typeof(EnvMan); _getAvailableEnvironmentsMethod = typeFromHandle.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "GetAvailableEnvironments" && method.GetParameters().Length == 1); _selectWeightedEnvironmentMethod = typeFromHandle.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "SelectWeightedEnvironment" && method.GetParameters().Length == 1); _getCurrentEnvironmentMethod = typeFromHandle.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "GetCurrentEnvironment" && method.GetParameters().Length == 0); _isNightMethod = typeFromHandle.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "IsNight" && method.GetParameters().Length == 0 && method.ReturnType == typeof(bool)); _environmentDurationField = typeFromHandle.GetField("m_environmentDuration", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (!(_getAvailableEnvironmentsMethod != null)) { return; } Type biomeType = _getAvailableEnvironmentsMethod.GetParameters()[0].ParameterType; if (biomeType.IsEnum) { try { _oceanEnvironmentBiomeValue = Enum.Parse(biomeType, "Ocean", ignoreCase: true); } catch { _oceanEnvironmentBiomeValue = null; } } Type typeFromHandle2 = typeof(WorldGenerator); _worldBiomeSectorMethod = typeFromHandle2.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "GetBiomeSector" && method.GetParameters().Length == 1 && method.GetParameters()[0].ParameterType == typeof(Vector3) && biomeType.IsAssignableFrom(method.ReturnType)); _worldIsAshlandsMethod = typeFromHandle2.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "IsAshlands" && method.ReturnType == typeof(bool) && method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(float) && method.GetParameters()[1].ParameterType == typeof(float)); _worldIsDeepNorthMethod = typeFromHandle2.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => (method.Name == "IsDeepnorth" || method.Name == "IsDeepNorth") && method.ReturnType == typeof(bool) && method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(float) && method.GetParameters()[1].ParameterType == typeof(float)); } private static string ReadEnvironmentName(object environment) { if (environment == null) { return string.Empty; } FieldInfo field = environment.GetType().GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field != null)) { return string.Empty; } return (field.GetValue(environment) as string) ?? string.Empty; } private string GetCurrentEnvironmentFallbackName(EnvMan envMan) { ResolveEnvironmentReflection(); if (_getCurrentEnvironmentMethod == null) { return string.Empty; } try { object obj = (_getCurrentEnvironmentMethod.IsStatic ? null : envMan); return ReadEnvironmentName(_getCurrentEnvironmentMethod.Invoke(obj, null)); } catch { return string.Empty; } } private bool IsPirateTimerNight(EnvMan envMan) { ResolveEnvironmentReflection(); if (_isNightMethod == null) { return false; } try { object obj = (_isNightMethod.IsStatic ? null : envMan); object obj2 = _isNightMethod.Invoke(obj, null); bool flag = default(bool); int num; if (obj2 is bool) { flag = (bool)obj2; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private object GetOceanEnvironmentBiomeArgument(Vector3 oceanPosition, out bool positionSpecific) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) positionSpecific = false; ResolveEnvironmentReflection(); if (_getAvailableEnvironmentsMethod == null) { return null; } Type parameterType = _getAvailableEnvironmentsMethod.GetParameters()[0].ParameterType; if (parameterType.IsEnum) { return _oceanEnvironmentBiomeValue; } object instance = WorldGenerator.instance; if (instance == null || _worldBiomeSectorMethod == null) { return null; } try { object obj = (_worldBiomeSectorMethod.IsStatic ? null : instance); object obj2 = _worldBiomeSectorMethod.Invoke(obj, new object[1] { oceanPosition }); if (obj2 != null && parameterType.IsInstanceOfType(obj2)) { positionSpecific = true; return obj2; } } catch (Exception ex) { LogVerbose("Ocean BiomeSector lookup failed: " + ex.Message); } return null; } private static bool ReadBooleanField(object instance, string fieldName) { if (instance == null || string.IsNullOrEmpty(fieldName)) { return false; } FieldInfo field = instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null || field.FieldType != typeof(bool)) { return false; } object value = field.GetValue(instance); bool flag = default(bool); int num; if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static string ReadEnvironmentEntryName(object entry) { if (entry == null) { return string.Empty; } Type type = entry.GetType(); FieldInfo field = type.GetField("m_env", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); string text = ((field == null) ? string.Empty : ReadEnvironmentName(field.GetValue(entry))); if (!string.IsNullOrEmpty(text)) { return text; } FieldInfo field2 = type.GetField("m_environment", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj; if (!(field2 == null)) { obj = field2.GetValue(entry) as string; if (obj == null) { return string.Empty; } } else { obj = string.Empty; } return (string)obj; } private bool GetWorldRegionFlag(MethodInfo method, Vector3 position, bool deepNorth) { //IL_004c: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (method == null) { return false; } try { object obj = (method.IsStatic ? null : WorldGenerator.instance); object[] parameters = ((!deepNorth) ? new object[2] { position.x, position.z } : new object[2] { position.x, position.y }); object obj2 = method.Invoke(obj, parameters); bool flag = default(bool); int num; if (obj2 is bool) { flag = (bool)obj2; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private string ApplyRegionalEnvironmentOverride(object available, string selectedName, bool ashlands, bool deepNorth) { if (!(available is IEnumerable enumerable) || (!ashlands && !deepNorth)) { return selectedName; } foreach (object item in enumerable) { bool num = ashlands && ReadBooleanField(item, "m_ashlandsOverride"); bool flag = deepNorth && ReadBooleanField(item, "m_deepnorthOverride"); if (num || flag) { string text = ReadEnvironmentEntryName(item); if (!string.IsNullOrEmpty(text)) { selectedName = text; } } } return selectedName; } private string GetCurrentOceanEnvironmentName(Vector3 oceanPosition) { //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) EnvMan instance = EnvMan.instance; if ((Object)(object)instance == (Object)null) { return string.Empty; } ResolveEnvironmentReflection(); try { if (_getAvailableEnvironmentsMethod != null && _selectWeightedEnvironmentMethod != null && _environmentDurationField != null && (Object)(object)ZNet.instance != (Object)null) { double num = Math.Max(1.0, Convert.ToDouble(_environmentDurationField.GetValue(instance))); int num2 = (int)(ZNet.instance.GetTimeSeconds() / num); if (_cachedOceanEnvironmentPeriod != num2) { _cachedOceanEnvironmentPeriod = num2; _cachedOceanEnvironmentNames.Clear(); _loggedTimerConditionsThisPeriod.Clear(); } bool positionSpecific; object oceanEnvironmentBiomeArgument = GetOceanEnvironmentBiomeArgument(oceanPosition, out positionSpecific); if (oceanEnvironmentBiomeArgument != null) { bool ashlands = positionSpecific && GetWorldRegionFlag(_worldIsAshlandsMethod, oceanPosition, deepNorth: false); bool deepNorth = positionSpecific && GetWorldRegionFlag(_worldIsDeepNorthMethod, oceanPosition, deepNorth: true); OceanWeatherCacheKey key = new OceanWeatherCacheKey(oceanEnvironmentBiomeArgument, ashlands, deepNorth); if (_cachedOceanEnvironmentNames.TryGetValue(key, out var value) && !string.IsNullOrEmpty(value)) { return value; } object obj = _getAvailableEnvironmentsMethod.Invoke(instance, new object[1] { oceanEnvironmentBiomeArgument }); if (obj != null) { string text = string.Empty; State state = Random.state; try { Random.InitState(num2); object obj2 = (_selectWeightedEnvironmentMethod.IsStatic ? null : instance); text = ReadEnvironmentName(_selectWeightedEnvironmentMethod.Invoke(obj2, new object[1] { obj })); text = ApplyRegionalEnvironmentOverride(obj, text, ashlands, deepNorth); } finally { Random.state = state; } if (!string.IsNullOrEmpty(text)) { if (_cachedOceanEnvironmentNames.Count >= 64) { _cachedOceanEnvironmentNames.Clear(); } _cachedOceanEnvironmentNames[key] = text; _loggedOceanEnvironmentLookupFailure = false; return text; } } } } } catch (Exception ex) { if (!_loggedOceanEnvironmentLookupFailure && VerboseLogging != null && VerboseLogging.Value) { _loggedOceanEnvironmentLookupFailure = true; ((BaseUnityPlugin)this).Logger.LogDebug((object)("Could not derive position-specific Ocean weather for pirate timer; using current EnvMan weather fallback: " + ex.Message)); } } return GetCurrentEnvironmentFallbackName(instance); } private float GetAutoSpawnTimerSpeedMultiplier(Vector3 oceanPosition) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) string currentOceanEnvironmentName = GetCurrentOceanEnvironmentName(oceanPosition); string text = (string.IsNullOrEmpty(currentOceanEnvironmentName) ? string.Empty : currentOceanEnvironmentName.Replace(" ", string.Empty).Replace("_", string.Empty).Replace("-", string.Empty) .ToLowerInvariant()); bool flag = (Object)(object)EnvMan.instance != (Object)null && IsPirateTimerNight(EnvMan.instance); bool num = text == "nofogts" || text.Contains("thunder") || text.Contains("seastorm"); bool flag2 = text.Contains("lightrain"); bool flag3 = !flag2 && text.Contains("rain"); bool flag4 = !num && (text.Contains("mist") || text.Contains("fog")); string text2; float num2; if (num) { num2 = ThunderstormTimerSpeedMultiplier.Value; text2 = "thunderstorm"; } else if (flag2) { num2 = 1f; text2 = "light rain"; } else if (flag3) { num2 = HeavyRainTimerSpeedMultiplier.Value; text2 = "heavy rain"; } else if (flag4 && flag) { num2 = NightFogTimerSpeedMultiplier.Value; text2 = "night + fog"; } else if (flag4) { num2 = FogTimerSpeedMultiplier.Value; text2 = "fog"; } else if (flag) { num2 = NightTimerSpeedMultiplier.Value; text2 = "night"; } else { num2 = ClearDayTimerSpeedMultiplier.Value; text2 = "clear/default day"; } num2 = Mathf.Clamp(num2, 0.1f, 5f); if (VerboseLogging != null && VerboseLogging.Value) { string item = text2 + "|" + num2.ToString("0.000") + "|" + (currentOceanEnvironmentName ?? string.Empty); if (_loggedTimerConditionsThisPeriod.Add(item)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Pirate encounter timer environment: " + text2 + " (Ocean weather '" + (string.IsNullOrEmpty(currentOceanEnvironmentName) ? "unknown" : currentOceanEnvironmentName) + "') => " + num2.ToString("0.00") + "x countdown speed.")); } } return num2; } private void ClearAutoSpawnShipStates() { _recoveryPauseUntilTicks = 0L; _autoSpawnShipStates.Clear(); _eligibleSpawnShips.Clear(); _shipSpawnTargets.Clear(); _serpentSuppressedShips.Clear(); _recoveringShips.Clear(); _visitedSpawnShips.Clear(); _spawnGroupBuffer.Clear(); _spawnGroupQueue.Clear(); _readySpawnShips.Clear(); _staleSpawnShips.Clear(); } private void PauseAllAutoSpawnShipStates() { foreach (AutoSpawnShipState value in _autoSpawnShipStates.Values) { if (value != null) { value.ActiveLastFrame = false; value.ActiveThisFrame = false; } } } private int CollectAutoSpawnEligibleShips() { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) _eligibleSpawnShips.Clear(); _shipSpawnTargets.Clear(); _serpentSuppressedShips.Clear(); _recoveringShips.Clear(); _shipTimerAuthorityPeer.Clear(); _eligiblePlayerShips.Clear(); List<Player> allPlayers = Player.GetAllPlayers(); if (allPlayers == null || allPlayers.Count == 0) { return 0; } for (int i = 0; i < allPlayers.Count; i++) { Player val = allPlayers[i]; if ((Object)(object)val == (Object)null || ((Character)val).IsDead() || !PirateOwnership.PlayerHasMod(val)) { continue; } Ship playerShip = PirateReflection.GetPlayerShip(val); if (!((Object)(object)playerShip == (Object)null) && !IsPirateShip(playerShip) && (IsShipUnderway(playerShip) || IsOcean(((Component)val).transform.position))) { if (!_shipSpawnTargets.ContainsKey(playerShip)) { _shipSpawnTargets[playerShip] = val; _eligibleSpawnShips.Add(playerShip); } _eligiblePlayerShips.Add(new KeyValuePair<Player, Ship>(val, playerShip)); long playerPeerId = PirateOwnership.GetPlayerPeerId(val); if (playerPeerId != 0L && (!_shipTimerAuthorityPeer.TryGetValue(playerShip, out var value) || playerPeerId < value)) { _shipTimerAuthorityPeer[playerShip] = playerPeerId; } if (IsPlayerSeaSerpentSuppressed(val)) { _serpentSuppressedShips.Add(playerShip); } if (IsPlayerRecoveringGear(val)) { _recoveringShips.Add(playerShip); } } } PruneShipMotionSamples(); return _eligibleSpawnShips.Count; } internal bool IsShipUnderway(Ship ship) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_00b8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ship == (Object)null) { return false; } float time = Time.time; if (!_shipMotionSamples.TryGetValue(ship, out var value)) { value = new ShipMotionSample { Position = ((Component)ship).transform.position, SampleTime = time }; _shipMotionSamples[ship] = value; } value.LastSeen = time; float num = time - value.SampleTime; if (num >= 1f) { Vector3 position = ((Component)ship).transform.position; Vector3 val = position - value.Position; val.y = 0f; float num2 = 1f * num; if (num <= 3f && ((Vector3)(ref val)).sqrMagnitude >= num2 * num2) { value.MovingUntil = time + 10f; } value.Position = position; value.SampleTime = time; } return time < value.MovingUntil; } private void PruneShipMotionSamples() { if (Time.time < _nextShipMotionPrune) { return; } _nextShipMotionPrune = Time.time + 10f; _shipMotionPruneBuffer.Clear(); foreach (KeyValuePair<Ship, ShipMotionSample> shipMotionSample in _shipMotionSamples) { if ((Object)(object)shipMotionSample.Key == (Object)null || Time.time - shipMotionSample.Value.LastSeen > 30f) { _shipMotionPruneBuffer.Add(shipMotionSample.Key); } } for (int i = 0; i < _shipMotionPruneBuffer.Count; i++) { _shipMotionSamples.Remove(_shipMotionPruneBuffer[i]); } } private void UpdateLocalAutoSpawnGroups() { //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) foreach (AutoSpawnShipState value in _autoSpawnShipStates.Values) { if (value != null) { value.ActiveThisFrame = false; } } if (CollectAutoSpawnEligibleShips() <= 0) { FinalizeAutoSpawnShipFrame(); return; } _visitedSpawnShips.Clear(); float num = 62500f; for (int i = 0; i < _eligibleSpawnShips.Count; i++) { Ship val = _eligibleSpawnShips[i]; if ((Object)(object)val == (Object)null || _visitedSpawnShips.Contains(val)) { continue; } _spawnGroupBuffer.Clear(); _spawnGroupQueue.Clear(); _spawnGroupQueue.Add(val); _visitedSpawnShips.Add(val); int num2 = 0; while (num2 < _spawnGroupQueue.Count) { Ship val2 = _spawnGroupQueue[num2++]; if ((Object)(object)val2 == (Object)null) { continue; } _spawnGroupBuffer.Add(val2); Vector3 position = ((Component)val2).transform.position; for (int j = 0; j < _eligibleSpawnShips.Count; j++) { Ship val3 = _eligibleSpawnShips[j]; if (!((Object)(object)val3 == (Object)null) && !_visitedSpawnShips.Contains(val3)) { Vector3 val4 = ((Component)val3).transform.position - position; val4.y = 0f; if (((Vector3)(ref val4)).sqrMagnitude <= num) { _visitedSpawnShips.Add(val3); _spawnGroupQueue.Add(val3); } } } } long groupTimerAuthority = GetGroupTimerAuthority(_spawnGroupBuffer); if (groupTimerAuthority != 0L && groupTimerAuthority != PirateOwnership.LocalPeerId) { for (int k = 0; k < _spawnGroupBuffer.Count; k++) { Ship val5 = _spawnGroupBuffer[k]; if ((Object)(object)val5 != (Object)null) { _autoSpawnShipStates.Remove(val5); } } } else { ProcessAutoSpawnGroup(_spawnGroupBuffer); PersistGroupTimerIfNeeded(_spawnGroupBuffer); } } FinalizeAutoSpawnShipFrame(); } private void ProcessAutoSpawnGroup(List<Ship> group) { //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_080b: Unknown result type (might be due to invalid IL or missing references) //IL_0804: Unknown result type (might be due to invalid IL or missing references) //IL_0810: Unknown result type (might be due to invalid IL or missing references) //IL_0813: Unknown result type (might be due to invalid IL or missing references) if (group == null || group.Count == 0) { return; } float num = Mathf.Max(0f, (OceanEntryGraceSeconds != null) ? OceanEntryGraceSeconds.Value : 20f); float num2 = float.MaxValue; bool flag = false; bool flag2 = false; for (int i = 0; i < group.Count; i++) { Ship val = group[i]; if (!((Object)(object)val == (Object)null) && _autoSpawnShipStates.TryGetValue(val, out var value) && value != null) { flag2 = true; if (value.ActiveLastFrame) { flag = true; num2 = Mathf.Min(num2, Mathf.Max(0f, value.Remaining)); } } } if (!flag && flag2) { for (int j = 0; j < group.Count; j++) { Ship val2 = group[j]; if (!((Object)(object)val2 == (Object)null) && _autoSpawnShipStates.TryGetValue(val2, out var value2) && value2 != null) { num2 = Mathf.Min(num2, Mathf.Max(0f, value2.Remaining)); } } } AutoSpawnEncounterGate encounter = null; if (!flag2) { if (TryReadPersistedGroupTimer(group, out var remaining, out encounter)) { num2 = remaining; LogVerbose("Local pirate encounter group resumed another player's timer at " + (num2 / 60f).ToString("0.0") + " minutes."); } else { num2 = RollAutoSpawnDelaySeconds(); if (VerboseLogging != null && VerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("New local pirate encounter group started with " + (num2 / 60f).ToString("0.0") + " minutes on its timer (" + group.Count + " ship" + ((group.Count == 1) ? "" : "s") + ").")); } } } for (int k = 0; k < group.Count; k++) { Ship val3 = group[k]; if ((Object)(object)val3 == (Object)null) { continue; } if (!_autoSpawnShipStates.TryGetValue(val3, out var value3) || value3 == null) { value3 = new AutoSpawnShipState { Remaining = num2, GraceUntil = Time.time + num, GraceUsedThisCycle = true, ActiveLastFrame = false, ActiveThisFrame = true, SerpentSuppressedLastFrame = false, ActiveEncounter = encounter }; _autoSpawnShipStates[val3] = value3; } else { if (!value3.ActiveLastFrame && !value3.GraceUsedThisCycle) { value3.GraceUntil = Time.time + num; value3.GraceUsedThisCycle = true; } value3.Remaining = num2; value3.ActiveThisFrame = true; } } bool flag3 = false; bool flag4 = false; AutoSpawnEncounterGate autoSpawnEncounterGate = null; for (int l = 0; l < group.Count; l++) { Ship val4 = group[l]; if ((Object)(object)val4 == (Object)null || !_autoSpawnShipStates.TryGetValue(val4, out var value4) || value4 == null) { continue; } flag4 |= value4.EncounterPausedLastFrame; if (value4.ActiveEncounter != null) { value4.ActiveEncounter.GroupPosition = ((Component)val4).transform.position; value4.ActiveEncounter.HasGroupPosition = true; } if (value4.ActiveEncounter != null && IsAutoSpawnEncounterActive(value4.ActiveEncounter)) { flag3 = true; continue; } if (value4.ActiveEncounter != null) { autoSpawnEncounterGate = value4.ActiveEncounter; } value4.ActiveEncounter = null; } if (!flag3 && autoSpawnEncounterGate != null && !autoSpawnEncounterGate.BonusResolved) { autoSpawnEncounterGate.BonusResolved = true; if (autoSpawnEncounterGate.PendingBonusSeconds > 0f) { if (autoSpawnEncounterGate.Fought) { num2 += autoSpawnEncounterGate.PendingBonusSeconds; LogVerbose("Encounter was a real fight: +" + (autoSpawnEncounterGate.PendingBonusSeconds / 60f).ToString("0.#") + " minutes heavy-encounter wait added to the pirate timer."); } else { LogVerbose("Encounter ended without a fight; no heavy-encounter wait added."); } } float num3 = ((VictoryBonusMinutes == null) ? 60f : (Mathf.Max(0f, VictoryBonusMinutes.Value) * 60f)); if (autoSpawnEncounterGate.PlayersWon && num3 > 0f) { num2 += num3; LogVerbose("Players won the encounter: +" + (num3 / 60f).ToString("0.#") + " minutes victory wait added to the pirate timer."); } } if (flag3) { if (!flag4 && VerboseLogging != null && VerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Natural pirate encounter is still active for a local sailing group; that group's next encounter timer is paused."); } for (int m = 0; m < group.Count; m++) { Ship val5 = group[m]; if ((Object)(object)val5 != (Object)null && _autoSpawnShipStates.TryGetValue(val5, out var value5) && value5 != null) { value5.Remaining = num2; value5.EncounterPausedLastFrame = true; } } return; } if (flag4 && VerboseLogging != null && VerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Natural pirate encounter ended or unloaded for a local sailing group; that group's next encounter timer resumed at " + (num2 / 60f).ToString("0.0") + " minutes.")); } for (int n = 0; n < group.Count; n++) { Ship val6 = group[n]; if ((Object)(object)val6 != (Object)null && _autoSpawnShipStates.TryGetValue(val6, out var value6) && value6 != null) { value6.EncounterPausedLastFrame = false; } } bool flag5 = false; bool flag6 = false; for (int num4 = 0; num4 < group.Count; num4++) { Ship val7 = group[num4]; if (!((Object)(object)val7 == (Object)null) && _autoSpawnShipStates.TryGetValue(val7, out var value7) && value7 != null) { flag5 |= _serpentSuppressedShips.Contains(val7); flag6 |= value7.SerpentSuppressedLastFrame; } } if (flag5) { if (!flag6 && VerboseLogging != null && VerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Living Sea Serpent detected near a local sailing group; that group's pirate timer paused."); } for (int num5 = 0; num5 < group.Count; num5++) { Ship val8 = group[num5]; if ((Object)(object)val8 != (Object)null && _autoSpawnShipStates.TryGetValue(val8, out var value8) && value8 != null) { value8.SerpentSuppressedLastFrame = true; } } return; } if (flag6) { num2 = RollAutoSpawnDelaySeconds(); if (VerboseLogging != null && VerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Sea Serpent suppression ended for a local sailing group; pirate timer restarted at " + (num2 / 60f).ToString("0.0") + " minutes.")); } } bool flag7 = false; bool flag8 = false; for (int num6 = 0; num6 < group.Count; num6++) { Ship val9 = group[num6]; if (!((Object)(object)val9 == (Object)null) && _autoSpawnShipStates.TryGetValue(val9, out var value9) && value9 != null) { flag7 |= _recoveringShips.Contains(val9); flag8 |= value9.RecoveryPausedLastFrame; } } if (flag7) { if (!flag8) { LogVerbose("A player in a local sailing group is recovering their gear after a death; that group's pirate timer paused."); } for (int num7 = 0; num7 < group.Count; num7++) { Ship val10 = group[num7]; if ((Object)(object)val10 != (Object)null && _autoSpawnShipStates.TryGetValue(val10, out var value10) && value10 != null) { value10.Remaining = num2; value10.RecoveryPausedLastFrame = true; } } return; } if (flag8) { LogVerbose("Gear recovery pause ended for a local sailing group; its pirate timer resumed at " + (num2 / 60f).ToString("0.0") + " minutes."); } if (GetCurrentTierCached() < 1) { if (!_loggedPreEikthyrHold) { _loggedPreEikthyrHold = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Natural pirate encounters start once Eikthyr is defeated; the encounter timer waits until then."); } for (int num8 = 0; num8 < group.Count; num8++) { Ship val11 = group[num8]; if ((Object)(object)val11 != (Object)null && _autoSpawnShipStates.TryGetValue(val11, out var value11) && value11 != null) { value11.Remaining = num2; } } return; } Vector3 oceanPosition = (((Object)(object)group[0] == (Object)null) ? Vector3.zero : ((Component)group[0]).transform.position); float autoSpawnTimerSpeedMultiplier = GetAutoSpawnTimerSpeedMultiplier(oceanPosition); num2 = Mathf.Max(0f, num2 - Time.deltaTime * autoSpawnTimerSpeedMultiplier); for (int num9 = 0; num9 < group.Count; num9++) { Ship val12 = group[num9]; if ((Object)(object)val12 != (Object)null && _autoSpawnShipStates.TryGetValue(val12, out var value12) && value12 != null) { value12.Remaining = num2; value12.SerpentSuppressedLastFrame = false; value12.RecoveryPausedLastFrame = false; } } if (num2 > 0f) { return; } _readySpawnShips.Clear(); for (int num10 = 0; num10 < group.Count; num10++) { Ship val13 = group[num10]; if (!((Object)(object)val13 == (Object)null) && _shipSpawnTargets.ContainsKey(val13) && _autoSpawnShipStates.TryGetValue(val13, out var value13) && value13 != null && Time.time >= value13.GraceUntil) { _readySpawnShips.Add(val13); } } if (_readySpawnShips.Count == 0) { return; } bool flag9 = false; Ship val14 = null; Player val15 = null; for (int num11 = 1; num11 <= 3; num11++) { if (flag9) { break; } Ship val16 = _readySpawnShips[Random.Range(0, _readySpawnShips.Count)]; Player value14; Player val17 = (_shipSpawnTargets.TryGetValue(val16, out value14) ? value14 : null); _naturalSpawnedShipsBuffer.Clear(); flag9 = TrySpawnForPlayer(val17, testSpawn: false, requireOceanSpawn: false, 0, null, null, _naturalSpawnedShipsBuffer); if (flag9) { val14 = val16; val15 = val17; if (VerboseLogging != null && VerboseLogging.Value && num11 > 1) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Natural pirate encounter succeeded on placement attempt " + num11 + "/" + 3 + ".")); } } else if (VerboseLogging != null && VerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Natural pirate encounter placement attempt " + num11 + "/" + 3 + " failed; trying again before the safety cooldown.")); } } AutoSpawnEncounterGate autoSpawnEncounterGate2 = null; if (flag9 && _naturalSpawnedShipsBuffer.Count > 0) { autoSpawnEncounterGate2 = new AutoSpawnEncounterGate { EncounterId = Random.Range(1, int.MaxValue), CreatedAtTicks = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetTime().Ticks : 0) }; long num12 = (((Object)(object)val15 != (Object)null && (Object)(object)val15 != (Object)(object)Player.m_localPlayer) ? PirateOwnership.GetPlayerPeerId(val15) : 0); bool flag10 = num12 != 0L && num12 != PirateOwnership.LocalPeerId && PirateOwnership.PlayerHasMod(val15); for (int num13 = 0; num13 < _naturalSpawnedShipsBuffer.Count; num13++) { Ship val18 = _naturalSpawnedShipsBuffer[num13]; if (!((Object)(object)val18 == (Object)null)) { autoSpawnEncounterGate2.PirateShips.Add(val18); ZNetView component = ((Component)val18).GetComponent<ZNetView>(); ZDO val19 = (((Object)(object)component == (Object)null) ? null : component.GetZDO()); if (val19 != null && val19.IsOwner()) { val19.Set("AtlasPirateEncounterId", autoSpawnEncounterGate2.EncounterId); } if (flag10) { PirateOwnership.TransferOwnership(component, num12); } } } if (flag10) { LogVerbose("Natural pirate encounter handed to the target player's game for simulation."); } } float num14; if (flag9 || GetCurrentTier() < 1) { num14 = RollAutoSpawnDelaySeconds(); if (flag9 && autoSpawnEncounterGate2 != null) { autoSpawnEncounterGate2.PendingBonusSeconds = GetHeavyEncounterCooldownSeconds(_naturalSpawnedShipsBuffer); if (autoSpawnEncounterGate2.PendingBonusSeconds > 0f) { LogVerbose("Heavy encounter: +" + (autoSpawnEncounterGate2.PendingBonusSeconds / 60f).ToString("0.#") + " minutes pending, added if the encounter becomes a fight."); } } if (VerboseLogging != null && VerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Local pirate encounter group timer reset to " + (num14 / 60f).ToString("0.0") + " minutes after an encounter attempt affecting " + group.Count + " ship" + ((group.Count == 1) ? "" : "s") + (((Object)(object)val14 != (Object)null) ? (" (successful target selected from " + group.Count + " eligible ship" + ((group.Count == 1) ? "" : "s") + ").") : "."))); } } else { num14 = 60f; if (VerboseLogging != null && VerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Automatic pirate encounter could not be placed after " + 3 + " attempts for the local sailing group; retrying in 60 seconds.")); } } for (int num15 = 0; num15 < group.Count; num15++) { Ship val20 = group[num15]; if ((Object)(object)val20 != (Object)null && _autoSpawnShipStates.TryGetValue(val20, out var value15) && value15 != null) { value15.Remaining = num14; value15.ActiveEncounter = autoSpawnEncounterGate2; value15.EncounterPausedLastFrame = false; if (flag9) { value15.GraceUsedThisCycle = false; } } } } private bool IsAutoSpawnEncounterActive(AutoSpawnEncounterGate encounter) { if (encounter == null) { return false; } if (Time.time < encounter.NextActivityCheck) { return encounter.LastActive; } encounter.NextActivityCheck = Time.time + 1f; encounter.LastActive = EvaluateAutoSpawnEncounterActive(encounter); if (!encounter.Fought && WasEncounterFought(encounter)) { encounter.Fought = true; LogVerbose("Natural pirate encounter turned into a fight."); } if (!encounter.PlayersWon && PirateShipController.WasNaturalEncounterShipSunkByPlayers(encounter.EncounterId)) { encounter.PlayersWon = true; LogVerbose("Players sank a ship of this natural pirate encounter."); } return encounter.LastActive; } private bool WasEncounterFought(AutoSpawnEncounterGate encounter) { //IL_004b: 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) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (PirateShipController.WasNaturalEncounterFought(encounter.EncounterId)) { return true; } if (LocalPlayerRecoveringGear()) { return true; } if (encounter.HasGroupPosition) { List<Player> allPlayers = Player.GetAllPlayers(); int num = 0; while (allPlayers != null && num < allPlayers.Count) { Player val = allPlayers[num]; if ((Object)(object)val != (Object)null && IsPlayerRecoveringGear(val)) { Vector3 val2 = ((Component)val).transform.position - encounter.GroupPosition; if (((Vector3)(ref val2)).sqrMagnitude <= 250000f) { return true; } } num++; } } return false; } private bool EvaluateAutoSpawnEncounterActive(AutoSpawnEncounterGate encounter) { if (encounter.CreatedAtTicks > 0 && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.GetTime().Ticks - encounter.CreatedAtTicks < 50000000) { return true; } for (int i = 0; i < encounter.PirateShips.Count; i++) { Ship val = encounter.PirateShips[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val).gameObject.activeInHierarchy && IsPirateShip(val)) { PirateShipController component = ((Component)val).GetComponent<PirateShipController>(); if ((Object)(object)component != (Object)null && component.HasLivingCrewForEncounterTimer()) { return true; } } } if (encounter.EncounterId != 0) { return PirateShipController.IsNaturalEncounterActive(encounter.EncounterId); } return false; } private bool TryReadPersistedGroupTimer(List<Ship> group, out float remaining, out AutoSpawnEncounterGate encounter) { remaining = 0f; encounter = null; long num = 0L; int num2 = 0; long createdAtTicks = 0L; float pendingBonusSeconds = 0f; bool fought = false; for (int i = 0; i < _eligiblePlayerShips.Count; i++) { KeyValuePair<Player, Ship> keyValuePair = _eligiblePlayerShips[i]; if (!((Object)(object)keyValuePair.Key == (Object)null) && group.Contains(keyValuePair.Value)) { ZNetView component = ((Component)keyValuePair.Key).GetComponent<ZNetView>(); ZDO val = (((Object)(object)component == (Object)null) ? null : component.GetZDO()); long num3 = ((val == null) ? 0 : val.GetLong("AtlasPirateGroupTimerSavedTicks", 0L)); if (num3 > num) { num = num3; remaining = Mathf.Max(0f, val.GetFloat("AtlasPirateGroupTimerRemaining", 0f)); num2 = val.GetInt("AtlasPirateGroupTimerEncounterId", 0); createdAtTicks = val.GetLong("AtlasPirateGroupTimerEncounterTicks", 0L); pendingBonusSeconds = val.GetFloat("AtlasPirateGroupTimerEncounterBonus", 0f); fought = val.GetBool("AtlasPirateGroupTimerEncounterFought", false); } } } if (num <= 0) { return false; } if (num2 != 0) { encounter = new AutoSpawnEncounterGate { EncounterId = num2, CreatedAtTicks = createdAtTicks, PendingBonusSeconds = pendingBonusSeconds, Fought = fought }; } return true; } private void PersistGroupTimerIfNeeded(List<Ship> group) { Player localPlayer =