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 OceanAdventures v1.0.0
BepInEx/plugins/OceanAdventures/OceanAdventures.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using OceanAdventures.Config; using OceanAdventures.Core; using OceanAdventures.Runtime; using Splatform; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace OceanAdventures { [BepInPlugin("com.oceanadventures.core", "OceanAdventures", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class OceanAdventuresPlugin : BaseUnityPlugin { public const string PluginGuid = "com.oceanadventures.core"; public const string PluginName = "OceanAdventures"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource Log; private Harmony? harmony; private OceanAdventuresRuntime? runtime; private void Awake() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; OceanAdventuresConfig oceanadventuresConfig = new OceanAdventuresConfig(((BaseUnityPlugin)this).Config); harmony = new Harmony("com.oceanadventures.core"); try { harmony.PatchAll(typeof(OceanAdventuresShipPatches).Assembly); Log.LogInfo((object)("Ghostship physics patch installed: native Ship physics bypassed only for registered ghostships (registered at runtime: " + OceanAdventuresShipPatches.RegisteredCount + ").")); Log.LogInfo((object)"Ghostship lifecycle patch installed: native keep lists preserved; active server ghosts added when necessary."); } catch (Exception ex) { Log.LogError((object)("Required OceanAdventures patches could not be installed; encounters and contracts are disabled for this session: " + ex)); try { harmony.UnpatchSelf(); } catch (Exception ex2) { Log.LogError((object)("Partial OceanAdventures patches could not be removed: " + ex2)); } harmony = null; ((Behaviour)this).enabled = false; return; } GameObject val = new GameObject("OceanAdventures.Runtime"); Object.DontDestroyOnLoad((Object)(object)val); runtime = val.AddComponent<OceanAdventuresRuntime>(); runtime.Initialize(oceanadventuresConfig); try { CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new OceanAdventuresPreviewCommand(runtime)); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new OceanAdventuresTestEventCommand(runtime)); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new OceanAdventuresClearEventsCommand(runtime)); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new OceanAdventuresGhostStatusCommand(runtime)); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new OceanAdventuresEventStatusCommand(runtime)); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new OceanAdventuresRewardStatusCommand(runtime)); } catch (Exception ex3) { Log.LogWarning((object)("OceanAdventures test commands could not be registered: " + ex3.Message)); } Log.LogInfo((object)"OceanAdventures 1.0.0 loaded."); } private void OnDestroy() { Harmony? obj = harmony; if (obj != null) { obj.UnpatchSelf(); } if ((Object)(object)runtime != (Object)null) { Object.Destroy((Object)(object)((Component)runtime).gameObject); } } } } namespace OceanAdventures.Runtime { internal enum ObjectiveUpdateResult { Updated, ContractMissing, SaveFailed } internal sealed class ContractService { private readonly WorldStateStore store; private readonly OceanAdventuresConfig config; private readonly MapMarkerService markers; private readonly RewardService rewards; private NetworkBridge? network; private readonly HashSet<string> receivedProfiles = new HashSet<string>(); private readonly Dictionary<string, float> nextMissingRecipeWarning = new Dictionary<string, float>(); public int OfferAvailabilityMask { get { if (!config.ContractsEnabled) { return 0; } return Enum.GetValues(typeof(EncounterType)).Cast<EncounterType>().Where(config.IsEnabled) .Aggregate(0, (int mask, EncounterType type) => mask | (1 << (int)type)); } } public bool HasAuthoritativeProfile(object player) { string playerId = ValheimReflection.GetPlayerId(player); if (CharacterIdentityRules.IsStable(playerId)) { if (!ValheimReflection.IsServer()) { return receivedProfiles.Contains(playerId); } return true; } return false; } public void SyncLocalMarker() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) MonoBehaviour localPlayer = LocalPlayerService.GetLocalPlayer(); ContractRecord contractRecord = (((Object)(object)localPlayer != (Object)null && HasAuthoritativeProfile(localPlayer)) ? GetProfile(localPlayer).ActiveContract : null); if (contractRecord == null) { markers.ClearMarker(); } else { markers.SetMarker(contractRecord.GetTarget()); } } public void ClearLocalState() { markers.ClearMarker(); receivedProfiles.Clear(); } public ContractService(WorldStateStore store, OceanAdventuresConfig config, MapMarkerService markers, RewardService rewards) { this.store = store; this.config = config; this.markers = markers; this.rewards = rewards; } public void AttachNetwork(NetworkBridge bridge) { network = bridge; } public IReadOnlyList<ContractOffer> GetOffers(object player) { if (!config.ContractsEnabled || !HasAuthoritativeProfile(player)) { return Array.Empty<ContractOffer>(); } PlayerVoyageProfile profile = store.GetProfile(ValheimReflection.GetPlayerId(player)); return ContractCatalog.CreateOffers(profile.OfferSeed + profile.CompletedContracts, config.IsEnabled); } public PlayerVoyageProfile GetProfile(object player) { return store.GetProfile(ValheimReflection.GetPlayerId(player)); } public ObjectiveUpdateResult TryUpdateObjective(string ownerId, string contractId, Vector3 target) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (!ValheimReflection.IsServer()) { return ObjectiveUpdateResult.ContractMissing; } if (string.IsNullOrEmpty(ownerId)) { return ObjectiveUpdateResult.Updated; } ContractRecord activeContract = store.GetProfile(ownerId).ActiveContract; if (activeContract == null || activeContract.Id != contractId) { return ObjectiveUpdateResult.ContractMissing; } if (!ContractRules.TryMoveTarget(activeContract, target, store.SaveNow)) { return ObjectiveUpdateResult.SaveFailed; } MonoBehaviour val = ((IEnumerable<MonoBehaviour>)ValheimReflection.FindByTypeName("Player")).FirstOrDefault((Func<MonoBehaviour, bool>)((MonoBehaviour candidate) => ValheimReflection.GetPlayerId(candidate) == ownerId)); if ((Object)(object)val == (Object)null) { return ObjectiveUpdateResult.Updated; } if (IsLocalCharacter(val)) { markers.SetMarker(target); } network?.SendPlayerSnapshot(val); return ObjectiveUpdateResult.Updated; } public void PublishProfile(string ownerId) { if (ValheimReflection.IsServer() && !string.IsNullOrEmpty(ownerId)) { MonoBehaviour val = ((IEnumerable<MonoBehaviour>)ValheimReflection.FindByTypeName("Player")).FirstOrDefault((Func<MonoBehaviour, bool>)((MonoBehaviour candidate) => ValheimReflection.GetPlayerId(candidate) == ownerId)); if ((Object)(object)val != (Object)null) { network?.SendPlayerSnapshot(val); } } } public bool Accept(object player, EncounterType type, Vector3 origin) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) if (!CharacterIdentityRules.IsStable(ValheimReflection.GetPlayerId(player))) { return false; } if (!config.ContractsEnabled || !config.IsEnabled(type) || !Enum.IsDefined(typeof(EncounterType), type)) { return false; } if (!ValheimReflection.IsServer()) { network?.SendAccept(type, origin); return true; } PlayerVoyageProfile profile = GetProfile(player); if (!LedgerAccess.CanUse(player)) { Notify(player, OceanAdventuresNoticeCode.LedgerOutOfRange); return false; } if (profile.ActiveContract != null) { Notify(player, OceanAdventuresNoticeCode.AlreadyActiveContract); return false; } if (!GetOffers(player).Any((ContractOffer offer) => offer.Type == type)) { Notify(player, OceanAdventuresNoticeCode.OfferNoLongerAvailable); return false; } Vector3 departure = ValheimReflection.GetPosition(player); Vector3 forward = ValheimReflection.GetForward(player); MonoBehaviour val = (from ship in ValheimReflection.FindByTypeName("Ship").Where(ValheimReflection.IsEligibleShip).Where(ValheimReflection.IsOnNavigableWater) where Vector3.Distance(((Component)ship).transform.position, departure) <= 100f orderby Vector3.Distance(((Component)ship).transform.position, departure) select ship).FirstOrDefault(); if ((Object)(object)val != (Object)null) { departure = ((Component)val).transform.position; forward = ((Component)val).transform.forward; } forward = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f) * forward; float distance = Random.Range(config.MinimumContractDistance, config.MaximumContractSearchDistance); if (!ValheimReflection.TryFindWaterPosition(departure, forward, distance, out var position, requireWaterCorridor: false, ValheimReflection.RequiredEncounterLandClearance(type), diagnostics: false, ValheimReflection.FallbackEncounterLandClearance(type), config.MinimumContractDistance, config.MaximumContractSearchDistance)) { Notify(player, OceanAdventuresNoticeCode.NoOpenWaterRoute); return false; } profile.ActiveContract = new ContractRecord { Id = Guid.NewGuid().ToString("N"), Type = type, OwnerId = profile.PlayerId, CreatedUtcTicks = DateTime.UtcNow.Ticks, State = EncounterState.Dormant }; profile.ActiveContract.SetTarget(position); if (!store.SaveNow()) { profile.ActiveContract = null; Notify(player, OceanAdventuresNoticeCode.ContractSaveFailed); return false; } if (IsLocalCharacter(player)) { markers.SetMarker(position); } Notify(player, OceanAdventuresNoticeCode.ContractAccepted); return true; } public bool Cancel(object player) { if (!CharacterIdentityRules.IsStable(ValheimReflection.GetPlayerId(player))) { return false; } if (!ValheimReflection.IsServer()) { network?.SendCancel(); return true; } PlayerVoyageProfile profile = GetProfile(player); if (!LedgerAccess.CanUse(player)) { Notify(player, OceanAdventuresNoticeCode.LedgerOutOfRange); return false; } if (profile.ActiveContract == null) { Notify(player, OceanAdventuresNoticeCode.NoActiveContract); return false; } ContractRecord activeContract = profile.ActiveContract; profile.ActiveContract = null; if (!store.SaveNow()) { profile.ActiveContract = activeContract; Notify(player, OceanAdventuresNoticeCode.ContractSaveFailed); return false; } if (IsLocalCharacter(player)) { markers.ClearMarker(); } Notify(player, OceanAdventuresNoticeCode.ContractCancelled); return true; } public bool Complete(object player, EncounterType type, string encounterContractId) { if (!CharacterIdentityRules.IsStable(ValheimReflection.GetPlayerId(player))) { return false; } if (!ValheimReflection.IsServer()) { return false; } if (type == EncounterType.GhostShip || type == EncounterType.Shipwreck || type == EncounterType.Smugglers) { return false; } PlayerVoyageProfile profile = GetProfile(player); if (!ContractRules.CanComplete(profile.ActiveContract, type, encounterContractId)) { Player val = (Player)((player is Player) ? player : null); if (val != null) { network?.SendNotice(val, OceanAdventuresNoticeCode.ContractNoLongerAvailable); } return false; } if (VanillaRewardCatalog.Entries.Count == 0) { if (!nextMissingRecipeWarning.TryGetValue(profile.PlayerId, out var value) || Time.time >= value) { nextMissingRecipeWarning[profile.PlayerId] = Time.time + 30f; OceanAdventuresPlugin.Log.LogWarning((object)"Contract completion is waiting because the Vanilla equipment catalog was not captured; the contract and reward journal were not changed."); Player val2 = (Player)((player is Player) ? player : null); if (val2 != null) { network?.SendNotice(val2, OceanAdventuresNoticeCode.RewardCatalogUnavailable); } } return false; } List<string> knownEquipmentRecipes = new List<string>(profile.KnownEquipmentRecipes); List<RewardGrant> list = rewards.CreateContractRewards(player, profile); if (list.Count > 0) { list.AddRange(rewards.CreateEncounterLoot(type, config.SmugglerCrewSize, profile, profile.ActiveContract.Id)); } if (list.Count == 0) { profile.KnownEquipmentRecipes = knownEquipmentRecipes; if (!nextMissingRecipeWarning.TryGetValue(profile.PlayerId, out var value2) || Time.time >= value2) { nextMissingRecipeWarning[profile.PlayerId] = Time.time + 30f; OceanAdventuresPlugin.Log.LogWarning((object)"Contract completion is waiting for a personally known Vanilla equipment recipe; no reward or completion was committed."); Player val3 = (Player)((player is Player) ? player : null); if (val3 != null) { network?.SendNotice(val3, OceanAdventuresNoticeCode.NoKnownEquipmentRecipe); } } return false; } nextMissingRecipeWarning.Remove(profile.PlayerId); ContractRecord activeContract = profile.ActiveContract; int count = profile.Rewards.Count; RewardService.Append(profile, list); profile.ActiveContract = null; profile.CompletedContracts++; if (!store.SaveNow()) { profile.ActiveContract = activeContract; profile.CompletedContracts--; profile.KnownEquipmentRecipes = knownEquipmentRecipes; profile.Rewards.RemoveRange(count, list.Count); Player val4 = (Player)((player is Player) ? player : null); if (val4 != null) { network?.SendNotice(val4, OceanAdventuresNoticeCode.RewardSaveFailed); } return false; } if (IsLocalCharacter(player)) { markers.ClearMarker(); } network?.SendPlayerSnapshot(player); OceanAdventuresPlugin.Log.LogInfo((object)("Contract reward queued: " + string.Join(", ", list.Select((RewardGrant item) => item.Prefab + " x" + item.Amount + ((item.Quality > 1) ? (" (quality " + item.Quality + ")") : ""))) + ".")); return true; } public bool TryPrepareChest(Player player, EncounterType type, string contractId, out List<RewardGrant> remaining) { remaining = new List<RewardGrant>(); if (!ValheimReflection.IsServer() || (type != EncounterType.Shipwreck && type != EncounterType.Smugglers && type != EncounterType.GhostShip)) { return false; } PlayerVoyageProfile profile = GetProfile(player); ContractRecord activeContract = profile.ActiveContract; if (!ContractRules.CanComplete(activeContract, type, contractId)) { return false; } if (activeContract.ChestRewardsPrepared) { remaining = activeContract.ChestRemaining.Select(CopyGrant).ToList(); return true; } if (VanillaRewardCatalog.Entries.Count == 0) { NotifyChestIssue(player, OceanAdventuresNoticeCode.RewardCatalogUnavailable); return false; } List<string> knownEquipmentRecipes = new List<string>(profile.KnownEquipmentRecipes); List<RewardGrant> list = rewards.CreateContractRewards(player, profile); if (list.Count == 0) { profile.KnownEquipmentRecipes = knownEquipmentRecipes; NotifyChestIssue(player, OceanAdventuresNoticeCode.NoKnownEquipmentRecipe); return false; } list.AddRange(rewards.CreateEncounterLoot(type, config.SmugglerCrewSize, profile, activeContract.Id)); activeContract.ChestRewardsPrepared = true; activeContract.ChestRemaining = list.Select(CopyGrant).ToList(); if (!store.SaveNow()) { activeContract.ChestRewardsPrepared = false; activeContract.ChestRemaining.Clear(); profile.KnownEquipmentRecipes = knownEquipmentRecipes; NotifyChestIssue(player, OceanAdventuresNoticeCode.RewardSaveFailed); return false; } remaining = activeContract.ChestRemaining.Select(CopyGrant).ToList(); return true; } public bool SaveChestRemaining(string ownerId, string contractId, List<RewardGrant> remaining) { if (!ValheimReflection.IsServer()) { return false; } ContractRecord activeContract = store.GetProfile(ownerId).ActiveContract; if (activeContract == null || activeContract.Id != contractId || !activeContract.ChestRewardsPrepared) { return false; } List<RewardGrant> chestRemaining = activeContract.ChestRemaining; activeContract.ChestRemaining = remaining.Select(CopyGrant).ToList(); if (store.SaveNow()) { return true; } activeContract.ChestRemaining = chestRemaining; return false; } public bool CompleteChest(Player player, EncounterType type, string contractId) { if (!ValheimReflection.IsServer()) { return false; } PlayerVoyageProfile profile = GetProfile(player); ContractRecord activeContract = profile.ActiveContract; if (!ContractRules.CanComplete(activeContract, type, contractId) || !activeContract.ChestRewardsPrepared || activeContract.ChestRemaining.Count != 0) { return false; } profile.ActiveContract = null; profile.CompletedContracts++; if (!store.SaveNow()) { profile.ActiveContract = activeContract; profile.CompletedContracts--; Notify(player, OceanAdventuresNoticeCode.RewardSaveFailed); return false; } if (IsLocalCharacter(player)) { markers.ClearMarker(); } network?.SendPlayerSnapshot(player); return true; } private static RewardGrant CopyGrant(RewardGrant item) { return new RewardGrant { Prefab = item.Prefab, Amount = item.Amount, Quality = item.Quality }; } private void NotifyChestIssue(Player player, OceanAdventuresNoticeCode code) { string playerId = ValheimReflection.GetPlayerId(player); if (!nextMissingRecipeWarning.TryGetValue(playerId, out var value) || !(Time.time < value)) { nextMissingRecipeWarning[playerId] = Time.time + 30f; Notify(player, code); } } private static bool IsLocalCharacter(object player) { MonoBehaviour localPlayer = LocalPlayerService.GetLocalPlayer(); if ((Object)(object)localPlayer != (Object)null) { return ValheimReflection.GetPlayerId(localPlayer) == ValheimReflection.GetPlayerId(player); } return false; } private void Notify(object player, OceanAdventuresNoticeCode code) { Player val = (Player)((player is Player) ? player : null); if (val != null) { network?.SendNotice(val, code); } } public void HandleServerRequest(long sender, object player, OceanAdventuresNetworkOp op, ZPackage package) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) switch (op) { case OceanAdventuresNetworkOp.Accept: { EncounterType type = (EncounterType)package.ReadInt(); package.ReadSingle(); package.ReadSingle(); package.ReadSingle(); Accept(player, type, ValheimReflection.GetPosition(player)); break; } case OceanAdventuresNetworkOp.Cancel: Cancel(player); break; } network?.SendSnapshot(sender, ValheimReflection.GetPlayerId(player)); } public void WriteSnapshot(ZPackage package, string playerId) { PlayerVoyageProfile profile = store.GetProfile(playerId); package.Write(playerId); package.Write(profile.CompletedContracts); package.Write(profile.OfferSeed); package.Write(profile.ActiveContract != null); if (profile.ActiveContract != null) { package.Write(profile.ActiveContract.Id); package.Write((int)profile.ActiveContract.Type); package.Write((int)profile.ActiveContract.State); package.Write(profile.ActiveContract.TargetX); package.Write(profile.ActiveContract.TargetY); package.Write(profile.ActiveContract.TargetZ); } } public void ApplySnapshot(ZPackage package) { string text = package.ReadString(); MonoBehaviour localPlayer = LocalPlayerService.GetLocalPlayer(); if ((Object)(object)localPlayer == (Object)null || text != ValheimReflection.GetPlayerId(localPlayer)) { return; } int num = package.ReadInt(); int offerSeed = package.ReadInt(); if (num >= 0) { ContractRecord contractRecord = null; if (package.ReadBool()) { contractRecord = new ContractRecord { Id = package.ReadString(), OwnerId = text, Type = (EncounterType)package.ReadInt(), State = (EncounterState)package.ReadInt(), TargetX = package.ReadSingle(), TargetY = package.ReadSingle(), TargetZ = package.ReadSingle() }; } if (contractRecord == null || (contractRecord.Id.Length != 0 && contractRecord.Id.Length <= 64 && Enum.IsDefined(typeof(EncounterType), contractRecord.Type) && Enum.IsDefined(typeof(EncounterState), contractRecord.State) && Finite(contractRecord.TargetX) && Finite(contractRecord.TargetY) && Finite(contractRecord.TargetZ))) { PlayerVoyageProfile profile = store.GetProfile(text); profile.CompletedContracts = num; profile.OfferSeed = offerSeed; profile.ActiveContract = contractRecord; receivedProfiles.Add(text); SyncLocalMarker(); } } } private static bool Finite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } internal sealed class EncounterDirector { private sealed class ActiveEncounter { public string Id = string.Empty; public EncounterType Type; public EncounterState State; public string OwnerId = string.Empty; public string ContractId = string.Empty; public bool ContractActivated; public Component? Ship; public Vector3 SourceShipPosition; public Vector3 SourceShipForward = Vector3.forward; public Quaternion SceneRotation = Quaternion.identity; public float SourceShipSpeed; public float SourceSampleTime; public float SinkingStartedAt; public float SalvageClaimedAt; public GameObject? RewardChest; public ZDOID RewardChestId; public bool RewardChestReady; public bool ChestRewardsPrepared; public int RewardChestAttempts; public float NextRewardChestAttempt; public List<RewardGrant> ChestRemaining = new List<RewardGrant>(); public float NextStormCommitAttempt; public bool StormCommitFailureNotified; public Vector3 StormLastShipPosition; public bool StormHasShipSample; public bool StormGatePending; public GameObject? ServerVisual; public Vector3 Position; public Vector3 Target; public Vector3 GhostRouteTarget; public object? GhostZdo; public GameObject? PendingGhostSpawn; public ZDOID GhostNetworkId; public float StartedAt; public float StormWeatherStartedAt; public float LastSeenParticipant; public int Progress; public Vector3[] StormRoute = Array.Empty<Vector3>(); public Vector3[] CargoPositions = Array.Empty<Vector3>(); public readonly WreckCargoState Cargo = new WreckCargoState(); public float NextWreckCheck; public float SurvivorMissingSince = -1f; public bool SmugglerDefeatAnnounced; public int LastSmugglerDefeatedCount; public SmugglerCrewState? SmugglerCrew; public float[] SmugglerMissingSince = Array.Empty<float>(); public float NextSmugglerCheck; public bool GhostCargoInitialized; public bool GhostCargoEverHadItems; public bool GhostCargoWasOpened; public bool GhostWaitingForCargo; public bool GhostCargoUnavailable; public int GhostCargoAttempts; public float NextGhostCargoAttempt; public float NextGhostCompletionAttempt; public readonly GhostRecoveryBudget GhostRecovery = new GhostRecoveryBudget(); public float NextSourceRebindAttempt; public float NextGhostRouteRefresh; public bool SourceShipDetachedLogged; public float LastBroadcastAt = -999f; public Vector3 LastBroadcastPosition; public EncounterState LastBroadcastState; public readonly List<GameObject> Combatants = new List<GameObject>(); public readonly List<ZDOID> SceneActorIds = new List<ZDOID>(); } private sealed class QuietZone { public Vector3 Center; public float Until; } private sealed class FreeShipPlan { public Component Ship; public List<EncounterType> Types = new List<EncounterType>(); } private readonly OceanAdventuresConfig config; private readonly WorldStateStore store; private readonly ContractService contracts; private readonly NetworkBridge network; private readonly VisualRegistry visuals; private readonly RewardService rewards; private readonly StormWeatherService weather; private readonly Dictionary<string, ActiveEncounter> active = new Dictionary<string, ActiveEncounter>(); private readonly List<ActiveEncounter> encounterTickBuffer = new List<ActiveEncounter>(); private float nextFreeEvent; private float nextFreeRoll; private EncounterType? lastFreeType; private float nextShipScan; private bool stormRunning; private readonly Dictionary<string, float> contractRetryAfter = new Dictionary<string, float>(); private readonly Dictionary<string, float> nextReadOnlyNotice = new Dictionary<string, float>(); private readonly List<QuietZone> localQuietZones = new List<QuietZone>(); private readonly Dictionary<string, float> nextQuietNotice = new Dictionary<string, float>(); private readonly Dictionary<string, float> nextRouteNotice = new Dictionary<string, float>(); public EncounterDirector(OceanAdventuresConfig config, WorldStateStore store, ContractService contracts, NetworkBridge network, VisualRegistry visuals, RewardService rewards, StormWeatherService weather) { EncounterDirector encounterDirector = this; this.config = config; this.store = store; this.contracts = contracts; this.network = network; this.visuals = visuals; this.rewards = rewards; this.weather = weather; float num = EncounterPacing.RestoredRemainingSeconds(store.Current.NextFreeEligibleUtcTicks, DateTime.UtcNow.Ticks, config.FreeEncounterIntervalSeconds); nextFreeEvent = Time.time + Mathf.Max(EncounterPacing.InitialGraceSeconds(config.FreeEncounterIntervalSeconds), num); nextFreeRoll = nextFreeEvent; if (store.Current.LastFreeTypeCode > 0) { lastFreeType = (EncounterType)(store.Current.LastFreeTypeCode - 1); } NetworkBridge networkBridge = network; networkBridge.SnapshotRequested = (Action<long>)Delegate.Combine(networkBridge.SnapshotRequested, (Action<long>)delegate(long peer) { network.SendWeather(peer, weather.ActiveId); foreach (ActiveEncounter value in encounterDirector.active.Values) { network.SendEncounter(peer, OceanAdventuresNetworkOp.EncounterStart, Snapshot(value)); } }); } public void Tick(bool allowNewEncounters) { if (!ValheimReflection.IsServer()) { return; } if (allowNewEncounters && Time.time >= nextShipScan) { nextShipScan = Time.time + 1f; localQuietZones.RemoveAll((QuietZone zone) => zone.Until <= Time.time); List<Component> list = ValheimReflection.FindByTypeName("Ship").Where(ValheimReflection.IsEligibleShip).Where(ValheimReflection.IsOnNavigableWater) .Cast<Component>() .ToList(); List<Component> list2 = new List<Component>(); Ship boat = default(Ship); foreach (Component ship in list) { ref Ship reference = ref boat; Component obj = ship; reference = (Ship)(object)((obj is Ship) ? obj : null); Player val = ((boat != null) ? (from candidate in Player.GetAllPlayers() where (Object)(object)candidate != (Object)null && !((Character)candidate).IsDead() && CharacterIdentityRules.IsStable(ValheimReflection.GetPlayerId(candidate)) && boat.IsPlayerInBoat(candidate) orderby Vector3.Distance(((Component)candidate).transform.position, ship.transform.position) select candidate).FirstOrDefault() : null); if (!((Object)(object)val == (Object)null)) { ActivateContractIfReady(ship, val); list2.Add(ship); } } TrySpawnFreeEncounter(list2); } encounterTickBuffer.Clear(); encounterTickBuffer.AddRange(active.Values); foreach (ActiveEncounter item in encounterTickBuffer) { try { UpdateEncounter(item); } catch (Exception ex) { OceanAdventuresPlugin.Log.LogError((object)("Encounter update failed; aborting safely: " + ex)); EndEncounter(item, EncounterState.Aborted); } } } public bool TryActivateContract(object player, string contractId) { //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) if (ValheimReflection.IsServer() && network.IsRegistered && config.ContractsEnabled) { Player character = default(Player); ref Player reference = ref character; object obj = player; reference = (Player)((obj is Player) ? obj : null); if (character != null && !((Character)character).IsDead() && CharacterIdentityRules.IsStable(ValheimReflection.GetPlayerId(character))) { ContractRecord activeContract = contracts.GetProfile(player).ActiveContract; if (activeContract == null || activeContract.Id != contractId || activeContract.State != EncounterState.Dormant || !config.IsEnabled(activeContract.Type)) { return false; } if (!store.CanCommit) { NoticeReadOnly(character); return false; } if (contractRetryAfter.TryGetValue(activeContract.Id, out var value) && Time.time < value) { network.SendNotice(character, OceanAdventuresNoticeCode.VoyageRecovering); return false; } if (activeContract.Type == EncounterType.StormFront && stormRunning) { network.SendNotice(character, OceanAdventuresNoticeCode.StormAlreadyActive); return false; } MonoBehaviour val = ((IEnumerable<MonoBehaviour>)(from candidate in ValheimReflection.FindByTypeName("Ship").Where(ValheimReflection.IsEligibleShip).Where(delegate(MonoBehaviour candidate) { Ship val2 = (Ship)(object)((candidate is Ship) ? candidate : null); return val2 != null && val2.IsPlayerInBoat(character); }) .Where(ValheimReflection.IsOnNavigableWater) orderby Vector3.Distance(((Component)candidate).transform.position, ValheimReflection.GetPosition(player)) select candidate)).FirstOrDefault((Func<MonoBehaviour, bool>)((MonoBehaviour candidate) => Vector3.Distance(((Component)candidate).transform.position, ValheimReflection.GetPosition(player)) <= 45f)); if ((Object)(object)val == (Object)null || Vector3.Distance(((Component)val).transform.position, activeContract.GetTarget()) > 120f) { network.SendNotice(character, OceanAdventuresNoticeCode.ShipNotAtContractMarker); return false; } if (!((activeContract.Type == EncounterType.GhostShip) ? ValheimReflection.TrySnapToNavigableWater(activeContract.GetTarget(), out var snapped) : ValheimReflection.TrySnapToWater(activeContract.GetTarget(), out snapped))) { network.SendNotice(character, OceanAdventuresNoticeCode.ContractTargetNotWater); return false; } activeContract.SetTarget(snapped); if (ConflictsWithActiveScene((Component)(object)val, snapped, activeContract.Id)) { network.SendNotice(character, OceanAdventuresNoticeCode.AnotherEncounterActive); return false; } bool num = StartEncounter(activeContract.Type, activeContract.OwnerId, activeContract.Id, (Component)(object)val, snapped); if (num) { contractRetryAfter.Remove(activeContract.Id); return num; } contractRetryAfter[activeContract.Id] = EncounterPacing.PreserveRetryDeadline(Time.time, 15f, contractRetryAfter.TryGetValue(activeContract.Id, out var value2) ? value2 : 0f); network.SendNotice(character, OceanAdventuresNoticeCode.VoyageStartUnavailable); return num; } } return false; } public bool StartTestEncounter(EncounterType type, object player) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) Stopwatch stopwatch = Stopwatch.StartNew(); if (!network.IsRegistered) { ValheimReflection.ShowMessage("OceanAdventures network is not ready. Wait for the world connection before testing an encounter."); return false; } if (!CharacterIdentityRules.IsStable(ValheimReflection.GetPlayerId(player))) { ValheimReflection.ShowMessage("Wait until your character has joined the world before starting an OceanAdventures event."); return false; } if (!ValheimReflection.IsServer()) { ValheimReflection.ShowMessage("Run oceanadventures_test_event on the server."); return false; } if (!config.IsEnabled(type)) { ValheimReflection.ShowMessage("This OceanAdventures encounter is disabled in the server configuration."); return false; } if (!store.CanCommit) { ValheimReflection.ShowMessage("OceanAdventures world rewards are read-only. Repair the profile before starting a real test event."); return false; } if (type == EncounterType.StormFront && stormRunning) { ValheimReflection.ShowMessage("A OceanAdventures storm is already active."); return false; } Vector3 playerPosition = ValheimReflection.GetPosition(player); MonoBehaviour ship = ((IEnumerable<MonoBehaviour>)(from candidate in ValheimReflection.FindByTypeName("Ship").Where(ValheimReflection.IsEligibleShip).Where(delegate(MonoBehaviour candidate) { object obj = player; Player val = (Player)((obj is Player) ? obj : null); if (val != null && !((Character)val).IsDead()) { Ship val2 = (Ship)(object)((candidate is Ship) ? candidate : null); if (val2 != null) { return val2.IsPlayerInBoat(val); } } return false; }) orderby Vector3.Distance(((Component)candidate).transform.position, playerPosition) select candidate)).FirstOrDefault((Func<MonoBehaviour, bool>)((MonoBehaviour candidate) => Vector3.Distance(((Component)candidate).transform.position, playerPosition) <= 45f)); if ((Object)(object)ship == (Object)null) { ValheimReflection.ShowMessage("Board a compatible ship before starting a test encounter."); return false; } if (!ValheimReflection.IsOnNavigableWater((Component)(object)ship)) { ValheimReflection.ShowMessage("Sail into navigable water before starting this OceanAdventures test encounter."); return false; } if (active.Values.Any((ActiveEncounter encounter) => (Object)(object)encounter.Ship == (Object)(object)ship)) { ValheimReflection.ShowMessage("Finish or abort the current OceanAdventures encounter first."); return false; } Vector3 course = Vector3.zero; if (!((type == EncounterType.GhostShip) ? GhostSeaNavigation.TryFindSpawn((Component)(object)ship, out var spawn, out course) : ValheimReflection.TryFindWaterPosition(((Component)ship).transform.position, ((Component)ship).transform.forward, 28f, out spawn, requireWaterCorridor: true, ValheimReflection.RequiredEncounterLandClearance(type), diagnostics: true, ValheimReflection.FallbackEncounterLandClearance(type)))) { OceanAdventuresPlugin.Log.LogInfo((object)("Manual " + type.ToString() + " test timing: placement search failed after " + stopwatch.Elapsed.TotalMilliseconds.ToString("0.0") + " ms.")); ValheimReflection.ShowMessage("No safe water route was found near the ship."); return false; } double totalMilliseconds = stopwatch.Elapsed.TotalMilliseconds; Stopwatch stopwatch2 = Stopwatch.StartNew(); bool flag = StartEncounter(type, string.Empty, string.Empty, (Component)(object)ship, spawn, (type == EncounterType.GhostShip) ? new Vector3?(course) : ((Vector3?)null), diagnostics: true); double totalMilliseconds2 = stopwatch2.Elapsed.TotalMilliseconds; OceanAdventuresPlugin.Log.LogInfo((object)("Manual " + type.ToString() + " test timing: placement=" + totalMilliseconds.ToString("0.0") + " ms, validation/spawn=" + totalMilliseconds2.ToString("0.0") + " ms, total=" + stopwatch.Elapsed.TotalMilliseconds.ToString("0.0") + " ms.")); if (!flag) { ValheimReflection.ShowMessage("No safe OceanAdventures scene fits here. Reposition your ship and try again; check the log if it persists."); return false; } ValheimReflection.ShowMessage("OceanAdventures test encounter spawned: " + type); return true; } public void Shutdown() { CleanupBatch cleanupBatch = new CleanupBatch(); foreach (ActiveEncounter encounter in active.Values.ToList()) { cleanupBatch.Run("encounter " + encounter.Id, delegate { EndEncounter(encounter, EncounterState.Aborted); }); } active.Clear(); localQuietZones.Clear(); nextQuietNotice.Clear(); cleanupBatch.Run("orphaned ghosts", delegate { ValheimReflection.DestroyMarkedNetworkObjects("OceanAdventuresGhostShip"); }); cleanupBatch.Run("unloaded ghosts", delegate { ValheimReflection.DestroyMarkedNetworkZdos("OceanAdventuresGhostShip"); }); cleanupBatch.Run("orphaned scene actors", delegate { ValheimReflection.DestroyMarkedNetworkObjects("OceanAdventuresSceneActor"); }); cleanupBatch.Run("unloaded scene actors", delegate { ValheimReflection.DestroyMarkedNetworkZdos("OceanAdventuresSceneActor"); }); cleanupBatch.Run("visual registry", visuals.Clear); ReportCleanup(cleanupBatch, "shutdown"); } public int AbortAllEncounters() { List<ActiveEncounter> list = active.Values.ToList(); CleanupBatch cleanupBatch = new CleanupBatch(); foreach (ActiveEncounter encounter in list) { cleanupBatch.Run("encounter " + encounter.Id, delegate { EndEncounter(encounter, EncounterState.Aborted, manuallyCleared: true); }); } int orphanedGhosts = 0; int unloadedGhosts = 0; int orphanedActors = 0; int unloadedActors = 0; cleanupBatch.Run("orphaned ghosts", delegate { orphanedGhosts = ValheimReflection.DestroyMarkedNetworkObjects("OceanAdventuresGhostShip"); }); cleanupBatch.Run("unloaded ghosts", delegate { unloadedGhosts = ValheimReflection.DestroyMarkedNetworkZdos("OceanAdventuresGhostShip"); }); cleanupBatch.Run("orphaned scene actors", delegate { orphanedActors = ValheimReflection.DestroyMarkedNetworkObjects("OceanAdventuresSceneActor"); }); cleanupBatch.Run("unloaded scene actors", delegate { unloadedActors = ValheimReflection.DestroyMarkedNetworkZdos("OceanAdventuresSceneActor"); }); cleanupBatch.Run("visual registry", visuals.Clear); ReportCleanup(cleanupBatch, "abort all"); int num = list.Count + orphanedGhosts + unloadedGhosts + orphanedActors + unloadedActors; if (num > 0) { nextFreeEvent = Mathf.Max(nextFreeEvent, EncounterPacing.NextFreeEligibility(Time.time, 180f)); PersistFreePacing(); } return num; } public unsafe void LogGhostStatus() { //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_03df: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Unknown result type (might be due to invalid IL or missing references) //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_041f: Unknown result type (might be due to invalid IL or missing references) //IL_0424: Unknown result type (might be due to invalid IL or missing references) //IL_045d: Unknown result type (might be due to invalid IL or missing references) //IL_0463: Unknown result type (might be due to invalid IL or missing references) //IL_0374: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_039b: Unknown result type (might be due to invalid IL or missing references) //IL_060b: Unknown result type (might be due to invalid IL or missing references) //IL_0610: Unknown result type (might be due to invalid IL or missing references) //IL_06cd: Unknown result type (might be due to invalid IL or missing references) //IL_06d2: Unknown result type (might be due to invalid IL or missing references) //IL_06d6: Unknown result type (might be due to invalid IL or missing references) //IL_06db: Unknown result type (might be due to invalid IL or missing references) //IL_070a: Unknown result type (might be due to invalid IL or missing references) //IL_070f: Unknown result type (might be due to invalid IL or missing references) List<ActiveEncounter> list = active.Values.Where((ActiveEncounter encounter) => encounter.Type == EncounterType.GhostShip).ToList(); if (list.Count == 0) { OceanAdventuresPlugin.Log.LogInfo((object)"Ghostship status: no active ghostship encounter."); ValheimReflection.ShowMessage("No active OceanAdventures ghostship encounter."); return; } bool flag4 = default(bool); bool flag6 = default(bool); bool flag8 = default(bool); float num6 = default(float); foreach (ActiveEncounter item in list) { bool flag = (Object)(object)item.ServerVisual != (Object)null; bool flag2 = visuals.IsNetworked(item.Id); bool flag3 = visuals.IsObjectAlive(item.Id); GameObject val = visuals.GetObject(item.Id); object obj = item.GhostZdo ?? ValheimReflection.GetNetworkZdo(val); string text = ((ValheimReflection.Invoke(obj, "GetPosition") is Vector3 val2) ? ((object)(*(Vector3*)(&val2))/*cast due to .constrained prefix*/).ToString() : "missing"); object obj2; Vector3 val4; if (!(ValheimReflection.Invoke(obj, "GetRotation") is Quaternion val3)) { obj2 = "missing"; } else { val4 = ((Quaternion)(ref val3)).eulerAngles; obj2 = ((object)(*(Vector3*)(&val4))/*cast due to .constrained prefix*/).ToString(); } string text2 = (string)obj2; object obj3 = ValheimReflection.Invoke(obj, "IsOwner"); int num; if (obj3 is bool) { flag4 = (bool)obj3; num = 1; } else { num = 0; } bool flag5 = (byte)((uint)num & (flag4 ? 1u : 0u)) != 0; string networkOwnerId = ValheimReflection.GetNetworkOwnerId(obj); obj3 = ValheimReflection.Invoke(obj, "GetBool", "OceanAdventuresGhostActive", false); int num2; if (obj3 is bool) { flag6 = (bool)obj3; num2 = 1; } else { num2 = 0; } bool flag7 = (byte)((uint)num2 & (flag6 ? 1u : 0u)) != 0; obj3 = ValheimReflection.ReadMember(obj, "Persistent"); int num3; if (obj3 is bool) { flag8 = (bool)obj3; num3 = 1; } else { num3 = 0; } bool flag9 = (byte)((uint)num3 & (flag8 ? 1u : 0u)) != 0; Rigidbody val5 = (((Object)(object)val == (Object)null) ? null : val.GetComponent<Rigidbody>()); Container val6 = (((Object)(object)val == (Object)null) ? null : val.GetComponentInChildren<Container>(true)); Ship val7 = (((Object)(object)val == (Object)null) ? null : val.GetComponent<Ship>()); float surface; string text3 = (((Object)(object)val != (Object)null && ValheimReflection.TryGetWaterSurface(val.transform.position, out surface)) ? surface.ToString("0.00") : "unavailable"); Vector3 val8 = (((Object)(object)val5 == (Object)null) ? Vector3.zero : val5.worldCenterOfMass); float num4 = (((Object)(object)val5 == (Object)null) ? (-10000f) : Floating.GetLiquidLevel(new Vector3(val8.x, 30f, val8.z), 0f, (LiquidType)0)); float num5 = ((num4 > -1000f && !float.IsNaN(num4) && !float.IsInfinity(num4)) ? num4 : 30f); string text4 = (((Object)(object)val != (Object)null && Heightmap.GetHeight(val.transform.position, ref num6)) ? (num5 - num6).ToString("0.00") : "unavailable"); string text5 = (((Object)(object)val5 != (Object)null && (Object)(object)val7 != (Object)null) ? (val5.worldCenterOfMass.y - num5 - val7.m_waterLevelOffset - -1.25f).ToString("0.00") : "unavailable"); int num7; if (item.State == EncounterState.Active && (Object)(object)val != (Object)null) { val4 = item.GhostRouteTarget - item.Position; if (((Vector3)(ref val4)).sqrMagnitude > 1f) { num7 = (GhostSeaNavigation.IsClearLeg(item.Position, item.GhostRouteTarget, val) ? 1 : 0); goto IL_03aa; } } num7 = 0; goto IL_03aa; IL_03aa: bool flag10 = (byte)num7 != 0; ManualLogSource log = OceanAdventuresPlugin.Log; string[] array = new string[61]; array[0] = "Ghostship status: state="; array[1] = item.State.ToString(); array[2] = ", position="; val4 = item.Position; array[3] = ((object)(*(Vector3*)(&val4))/*cast due to .constrained prefix*/).ToString(); array[4] = ", route="; val4 = item.GhostRouteTarget; array[5] = ((object)(*(Vector3*)(&val4))/*cast due to .constrained prefix*/).ToString(); array[6] = ", sourcePosition="; val4 = item.SourceShipPosition; array[7] = ((object)(*(Vector3*)(&val4))/*cast due to .constrained prefix*/).ToString(); array[8] = ", sourceSpeed="; array[9] = item.SourceShipSpeed.ToString("0.00"); array[10] = ", sourceDistance="; array[11] = Vector3.Distance(item.Position, item.SourceShipPosition).ToString("0.0"); array[12] = ", commandedSpeed="; array[13] = ((item.State == EncounterState.Active) ? 3f : 0f).ToString("0.00"); array[14] = ", visual="; array[15] = flag.ToString(); array[16] = ", networked="; array[17] = flag2.ToString(); array[18] = ", alive="; array[19] = flag3.ToString(); array[20] = ", recoveries="; array[21] = item.GhostRecovery.Attempts.ToString(); array[22] = ", cargoOpened="; array[23] = item.GhostCargoWasOpened.ToString(); array[24] = ", cargoObserved="; array[25] = item.GhostCargoEverHadItems.ToString(); array[26] = ", cargoLoading="; array[27] = item.GhostWaitingForCargo.ToString(); array[28] = ", zdoPosition="; array[29] = text; array[30] = ", zdoRotation="; array[31] = text2; array[32] = ", zdoOwner="; array[33] = flag5.ToString(); array[34] = ", zdoOwnerId="; array[35] = networkOwnerId; array[36] = ", zdoActive="; array[37] = flag7.ToString(); array[38] = ", zdoPersistent="; array[39] = flag9.ToString(); array[40] = ", bodyKinematic="; array[41] = ((Object)(object)val5 != (Object)null && val5.isKinematic).ToString(); array[42] = ", waterSurface="; array[43] = text3; array[44] = ", centerOfMassY="; object obj4; if (!((Object)(object)val5 == (Object)null)) { val4 = val5.worldCenterOfMass; obj4 = val4.y.ToString("0.00"); } else { obj4 = "missing"; } array[45] = (string)obj4; array[46] = ", nativeWaterOffset="; array[47] = (((Object)(object)val7 == (Object)null) ? "missing" : val7.m_waterLevelOffset.ToString("0.00")); array[48] = ", measuredWater="; array[49] = ((num4 > -1000f) ? num4.ToString("0.00") : "fallback"); array[50] = ", measuredDepth="; array[51] = text4; array[52] = ", COMminusBuoyancyTarget="; array[53] = text5; array[54] = ", nextLegClear="; array[55] = flag10.ToString(); array[56] = ", bodyRotation="; object obj5; if (!((Object)(object)val5 == (Object)null)) { Quaternion rotation = val5.rotation; val4 = ((Quaternion)(ref rotation)).eulerAngles; obj5 = ((object)(*(Vector3*)(&val4))/*cast due to .constrained prefix*/).ToString(); } else { obj5 = "missing"; } array[57] = (string)obj5; array[58] = ", bodyVelocity="; object obj6; if (!((Object)(object)val5 == (Object)null)) { val4 = ReadBodyVelocity(val5); obj6 = ((object)(*(Vector3*)(&val4))/*cast due to .constrained prefix*/).ToString(); } else { obj6 = "missing"; } array[59] = (string)obj6; array[60] = "."; log.LogInfo((object)string.Concat(array)); Player localPlayer = Player.m_localPlayer; if (GhostHoldInteractionPatch.TryFindBadIcon((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null, out string issue)) { OceanAdventuresPlugin.Log.LogWarning((object)("Ghostship inventory UI preflight (player): " + issue)); } if ((Object)(object)val6 != (Object)null && GhostHoldInteractionPatch.TryFindBadIcon(val6.GetInventory(), out string issue2)) { OceanAdventuresPlugin.Log.LogWarning((object)("Ghostship inventory UI preflight (hold): " + issue2)); } } ValheimReflection.ShowMessage("Ghostship status written to the server log."); } public void LogEventStatus() { int num = Mathf.CeilToInt(Mathf.Max(0f, nextFreeEvent - Time.time)); int num2 = Mathf.CeilToInt(Mathf.Max(0f, nextFreeRoll - Time.time)); string[] array = active.Values.Select((ActiveEncounter encounter) => encounter.Type.ToString() + "/" + encounter.State).ToArray(); Player local = Player.m_localPlayer; ActiveEncounter activeEncounter = (((Object)(object)local == (Object)null) ? null : active.Values.FirstOrDefault((ActiveEncounter encounter) => NearScene(((Component)local).transform.position, encounter.Position))); int num3 = ((!((Object)(object)local == (Object)null)) ? (from zone in localQuietZones where zone.Until > Time.time && NearScene(((Component)local).transform.position, zone.Center) select Mathf.CeilToInt(zone.Until - Time.time)).DefaultIfEmpty(0).Max() : 0); OceanAdventuresPlugin.Log.LogInfo((object)("Encounter pacing: preset=" + config.EffectivePreset.ToString() + ", active=" + ((array.Length == 0) ? "none" : string.Join(", ", array)) + ", quietRemaining=" + num + "s, rollRemaining=" + num2 + "s, localQuietRemaining=" + num3 + "s, lastFreeType=" + (lastFreeType?.ToString() ?? "none") + ".")); if (activeEncounter != null) { ValheimReflection.ShowMessage("Nearby OceanAdventures encounter: " + activeEncounter.Type.ToString() + "/" + activeEncounter.State.ToString() + "."); } else if (num3 > 0) { ValheimReflection.ShowMessage("This sea area rests for " + num3 + "s. Sail farther or wait."); } else if (active.Values.Any((ActiveEncounter encounter) => string.IsNullOrEmpty(encounter.OwnerId))) { ValheimReflection.ShowMessage("A free OceanAdventures encounter is active elsewhere at sea."); } else { ValheimReflection.ShowMessage("Next free OceanAdventures roll in " + Mathf.Max(num, num2) + "s (at sea)."); } } private void ActivateContractIfReady(Component ship, object player) { //IL_0075: 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_0115: 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_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) if (!config.ContractsEnabled) { return; } ContractRecord activeContract = contracts.GetProfile(player).ActiveContract; if (activeContract == null || activeContract.State != EncounterState.Dormant || !config.IsEnabled(activeContract.Type) || (contractRetryAfter.TryGetValue(activeContract.Id, out var value) && Time.time < value) || Vector3.Distance(ship.transform.position, activeContract.GetTarget()) > 90f) { return; } if (!store.CanCommit) { Player val = (Player)((player is Player) ? player : null); if (val != null) { NoticeReadOnly(val); } } else { if (activeContract.Type == EncounterType.StormFront && stormRunning) { return; } if (localQuietZones.Any((QuietZone zone) => NearScene(ship.transform.position, zone.Center))) { Player val2 = (Player)((player is Player) ? player : null); if (val2 != null) { NoticeQuiet(val2); } } else { if (!ValheimReflection.IsOnNavigableWater(ship)) { return; } if (!((activeContract.Type == EncounterType.GhostShip) ? ValheimReflection.TrySnapToNavigableWater(activeContract.GetTarget(), out var snapped) : ValheimReflection.TrySnapToWater(activeContract.GetTarget(), out snapped))) { Player val3 = (Player)((player is Player) ? player : null); if (val3 != null) { NoticeRouteUnavailable(val3, OceanAdventuresNoticeCode.ContractTargetNotWater); } return; } activeContract.SetTarget(snapped); if (ConflictsWithActiveScene(ship, snapped, activeContract.Id)) { return; } if (!StartEncounter(activeContract.Type, activeContract.OwnerId, activeContract.Id, ship, snapped)) { contractRetryAfter[activeContract.Id] = EncounterPacing.PreserveRetryDeadline(Time.time, 15f, contractRetryAfter.TryGetValue(activeContract.Id, out var value2) ? value2 : 0f); Player val4 = (Player)((player is Player) ? player : null); if (val4 != null) { NoticeRouteUnavailable(val4, OceanAdventuresNoticeCode.VoyageStartUnavailable); } } else { contractRetryAfter.Remove(activeContract.Id); } } } } private void TrySpawnFreeEncounter(IReadOnlyList<Component> crewedShips) { //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) if (!store.CanCommit || Time.time < nextFreeEvent || Time.time < nextFreeRoll || active.Values.Any((ActiveEncounter encounter) => string.IsNullOrEmpty(encounter.OwnerId))) { return; } List<Component> list = crewedShips.Where((Component val) => !active.Values.Any((ActiveEncounter encounter) => (Object)(object)encounter.Ship == (Object)(object)val || NearScene(val.transform.position, encounter.Position)) && !localQuietZones.Any((QuietZone zone) => NearScene(val.transform.position, zone.Center))).ToList(); if (list.Count == 0) { return; } nextFreeRoll = Time.time + config.FreeEncounterRollIntervalSeconds; if (Random.value >= config.FreeEncounterRollChance) { return; } int num = Random.Range(0, list.Count); List<FreeShipPlan> list2 = new List<FreeShipPlan>(); for (int num2 = 0; num2 < list.Count; num2++) { Component ship = list[(num + num2) % list.Count]; bool navigableWater = ValheimReflection.IsOnNavigableWater(ship); List<EncounterType> enabled = (from EncounterType type in Enum.GetValues(typeof(EncounterType)) where config.IsEnabled(type) && (type != EncounterType.StormFront || !stormRunning) && (navigableWater || type == EncounterType.GhostShip) select type).ToList(); List<EncounterType> list3 = EncounterPacing.Candidates(enabled, lastFreeType); if (list3.Count != 0) { list2.Add(new FreeShipPlan { Ship = ship, Types = EncounterPacing.OrderedCandidates(enabled, lastFreeType, Random.Range(0, list3.Count)) }); } } foreach (EncounterAttemptSlot item in EncounterPacing.AttemptSlots(list2.Select((FreeShipPlan plan) => plan.Types.Count).ToList())) { FreeShipPlan freeShipPlan = list2[item.ShipIndex]; Component ship2 = freeShipPlan.Ship; EncounterType encounterType = freeShipPlan.Types[item.TypeOffset]; Vector3 course = Vector3.zero; if (((encounterType == EncounterType.GhostShip) ? GhostSeaNavigation.TryFindSpawn(ship2, out var spawn, out course) : ValheimReflection.TryFindWaterPosition(ship2.transform.position, ship2.transform.forward, 22f, out spawn, requireWaterCorridor: true, ValheimReflection.RequiredEncounterLandClearance(encounterType), diagnostics: false, ValheimReflection.FallbackEncounterLandClearance(encounterType))) && (StartEncounter(encounterType, string.Empty, string.Empty, ship2, spawn, (encounterType == EncounterType.GhostShip) ? new Vector3?(course) : ((Vector3?)null)) || Time.time < nextFreeEvent)) { return; } } nextFreeEvent = Mathf.Max(nextFreeEvent, Time.time + 30f); } private void NoticeReadOnly(Player player) { string playerId = ValheimReflection.GetPlayerId(player); if (!nextReadOnlyNotice.TryGetValue(playerId, out var value) || !(Time.time < value)) { nextReadOnlyNotice[playerId] = Time.time + 60f; network.SendNotice(player, OceanAdventuresNoticeCode.WorldProfileReadOnly); } } private void NoticeQuiet(Player player) { string playerId = ValheimReflection.GetPlayerId(player); if (!nextQuietNotice.TryGetValue(playerId, out var value) || !(Time.time < value)) { nextQuietNotice[playerId] = Time.time + 60f; network.SendNotice(player, OceanAdventuresNoticeCode.SeaRestingAfterEncounter); } } private void NoticeRouteUnavailable(Player player, OceanAdventuresNoticeCode code) { string playerId = ValheimReflection.GetPlayerId(player); if (!nextRouteNotice.TryGetValue(playerId, out var value) || !(Time.time < value)) { nextRouteNotice[playerId] = Time.time + 60f; network.SendNotice(player, code); } } private static bool NearScene(Vector3 point, Vector3 scene) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = point.x - scene.x; float num2 = point.z - scene.z; return EncounterPacing.IsNearScene(num * num + num2 * num2); } private bool ConflictsWithActiveScene(Component ship, Vector3 target, string contractId) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) return active.Values.Any((ActiveEncounter item) => (Object)(object)item.Ship == (Object)(object)ship || NearScene(target, item.Position) || (!string.IsNullOrEmpty(contractId) && item.ContractId == contractId)); } private bool PersistFreePacing() { if (!ValheimReflection.IsServer() || !store.CanCommit) { return false; } long nextFreeEligibleUtcTicks = store.Current.NextFreeEligibleUtcTicks; int lastFreeTypeCode = store.Current.LastFreeTypeCode; float num = Mathf.Clamp(nextFreeEvent - Time.time, 0f, config.FreeEncounterIntervalSeconds); store.Current.NextFreeEligibleUtcTicks = DateTime.UtcNow.AddSeconds(num).Ticks; store.Current.LastFreeTypeCode = (int)(lastFreeType.HasValue ? (lastFreeType.Value + 1) : EncounterType.GhostShip); if (store.SaveNow()) { return true; } store.Current.NextFreeEligibleUtcTicks = nextFreeEligibleUtcTicks; store.Current.LastFreeTypeCode = lastFreeTypeCode; OceanAdventuresPlugin.Log.LogWarning((object)"Free-encounter pacing could not be saved; a new free scene will not be published without a durable cooldown."); return false; } private unsafe bool StartEncounter(EncounterType type, string ownerId, string contractId, Component ship, Vector3 target, Vector3? plannedGhostCourse = null, bool diagnostics = false) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_014f: 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_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_04d7: Unknown result type (might be due to invalid IL or missing references) //IL_04dc: Unknown result type (might be due to invalid IL or missing references) //IL_04de: Unknown result type (might be due to invalid IL or missing references) //IL_04e3: Unknown result type (might be due to invalid IL or missing references) //IL_05cc: Unknown result type (might be due to invalid IL or missing references) //IL_05ce: Unknown result type (might be due to invalid IL or missing references) //IL_050a: Unknown result type (might be due to invalid IL or missing references) //IL_0501: Unknown result type (might be due to invalid IL or missing references) //IL_050f: Unknown result type (might be due to invalid IL or missing references) //IL_0514: Unknown result type (might be due to invalid IL or missing references) //IL_0519: Unknown result type (might be due to invalid IL or missing references) //IL_0520: Unknown result type (might be due to invalid IL or missing references) //IL_0522: Unknown result type (might be due to invalid IL or missing references) //IL_0630: Unknown result type (might be due to invalid IL or missing references) //IL_0635: Unknown result type (might be due to invalid IL or missing references) //IL_059a: Unknown result type (might be due to invalid IL or missing references) //IL_059f: Unknown result type (might be due to invalid IL or missing references) //IL_079a: Unknown result type (might be due to invalid IL or missing references) //IL_079f: Unknown result type (might be due to invalid IL or missing references) if (!Finite(ship.transform.position) || !Finite(ship.transform.rotation) || !Finite(target)) { return Reject("non-finite ship or target transform"); } if (ConflictsWithActiveScene(ship, target, contractId)) { return Reject("conflicts with an active OceanAdventures scene"); } if (!((type == EncounterType.GhostShip) ? ValheimReflection.TrySnapToNavigableWater(target, out target) : ValheimReflection.TrySnapToWater(target, out target))) { return Reject("target has no valid water surface or is shallower than the navigable-depth limit"); } if (!ValheimReflection.HasEncounterLandClearance(target, type)) { return Reject("target misses both the preferred 70 m shoreline reserve and the allowed hull-sized fallback"); } if (type != EncounterType.GhostShip && !ValheimReflection.HasWaterCorridor(ship.transform.position, target)) { return Reject("straight approach samples cross non-navigable water or land"); } Vector3 target2 = plannedGhostCourse ?? target; if (type == EncounterType.GhostShip && !plannedGhostCourse.HasValue) { if (!GhostSeaNavigation.TryFindSafeFacing(target, FlatForward(ship), out var facing)) { return Reject("no hull-sized safe ghostship heading fits at the target"); } if (!GhostSeaNavigation.TryChooseCourse(target, facing, null, out target2)) { target2 = target + facing; } } Quaternion val = ((type == EncounterType.Shipwreck || type == EncounterType.Smugglers) ? WreckSceneLayout.Rotation(FlatForward(ship)) : Quaternion.identity); Vector3[] positions = Array.Empty<Vector3>(); if (type == EncounterType.Smugglers) { bool flag = false; for (int i = 0; i < 4; i++) { Quaternion val2 = Quaternion.Euler(0f, SmugglerSceneFootprint.YawOffsetDegrees(i), 0f) * val; flag = !GhostSeaNavigation.HasSolidObstacle(target); if (!flag) { break; } for (int j = 0; j < 4; j++) { SmugglerSceneFootprint.HullCornerOffset(j, out var x, out var z); if (!ValheimReflection.TrySnapToWater(target + val2 * new Vector3(x, 0f, z), out var snapped) || GhostSeaNavigation.HasSolidObstacle(snapped)) { flag = false; break; } } if (flag) { val = val2; break; } } if (!flag) { return Reject("no smuggler longship orientation fits the water and obstacle footprint"); } } if (type == EncounterType.Shipwreck && !WreckSceneLayout.TryCreate(target, FlatForward(ship), out positions)) { return Reject("shipwreck layout cannot fit its full cargo/deck footprint in the sampled water"); } Vector3[] route = Array.Empty<Vector3>(); if (type == EncounterType.StormFront && (stormRunning || !StormRoute.TryCreate(target, FlatForward(ship), out route, diagnostics))) { return Reject(stormRunning ? "another Stormfront is already active" : "no safe seaward storm route passed validation"); } ActiveEncounter activeEncounter = new ActiveEncounter { Id = Guid.NewGuid().ToString("N"), Type = type, State = EncounterState.Active, OwnerId = ownerId, ContractId = contractId, Ship = ship, SourceShipPosition = ship.transform.position, SourceShipForward = FlatForward(ship), SceneRotation = val, Position = target, Target = target, StormRoute = route, Progress = ((type == EncounterType.StormFront) ? (-1) : 0), CargoPositions = positions, GhostRouteTarget = target2, StartedAt = Time.time, LastSeenParticipant = Time.time }; if (!HasFiniteSceneGeometry(activeEncounter)) { return Reject("generated event geometry contains invalid positions or rotations"); } if (string.IsNullOrEmpty(ownerId)) { float num = nextFreeEvent; EncounterType? encounterType = lastFreeType; nextFreeEvent = Mathf.Max(nextFreeEvent, EncounterPacing.NextFreeEligibility(Time.time, config.FreeEncounterIntervalSeconds)); lastFreeType = type; if (!PersistFreePacing()) { lastFreeType = encounterType; nextFreeEvent = Mathf.Max(num, Time.time + 120f); return Reject("free-event cooldown could not be saved before publishing the scene"); } } active[activeEncounter.Id] = activeEncounter; try { if (!string.IsNullOrEmpty(ownerId)) { ContractRecord activeContract = store.GetProfile(ownerId).ActiveContract; if (activeContract == null || activeContract.Id != contractId || activeContract.State != EncounterState.Dormant) { throw new InvalidOperationException("The requested personal contract is no longer dormant; no scene was spawned."); } activeContract.State = EncounterState.Active; if (!store.SaveNow()) { activeContract.State = EncounterState.Dormant; throw new InvalidOperationException("Contract activation could not be saved."); } activeEncounter.ContractActivated = true; } if (type == EncounterType.StormFront) { stormRunning = true; weather.Apply(activeEncounter.Id); if (!weather.IsApplied && !ValheimReflection.IsDedicatedServer()) { throw new InvalidOperationException("Native ThunderStorm weather was unavailable; the route was not started."); } network.SendWeather(0L, activeEncounter.Id); activeEncounter.StormWeatherStartedAt = Time.time; } GameObject val3 = null; GameObject val4 = null; if (type == EncounterType.GhostShip) { Vector3 val5 = activeEncounter.GhostRouteTarget - target; val5.y = 0f; Quaternion rotation = Quaternion.LookRotation((((Vector3)(ref val5)).sqrMagnitude > 0.01f) ? ((Vector3)(ref val5)).normalized : FlatForward(ship), Vector3.up); val3 = (activeEncounter.PendingGhostSpawn = ValheimReflection.SpawnNetworkedPrefab("VikingShip", target, rotation, ship.gameObject, "OceanAdventuresGhostShip")); if ((Object)(object)val3 == (Object)null) { throw new InvalidOperationException("Ghostship spawn did not return its exact network instance."); } activeEncounter.GhostZdo = ValheimReflection.GetNetworkZdo(val3); object? ghostZdo = activeEncounter.GhostZdo; ZDO val6 = (ZDO)((ghostZdo is ZDO) ? ghostZdo : null); if (val6 == null) { throw new InvalidOperationException("Ghostship spawn has no network identity."); } val6.Set("OceanAdventuresContractOwner", ownerId ?? string.Empty); activeEncounter.GhostNetworkId = val6.m_uid; activeEncounter.PendingGhostSpawn = null; OceanAdventuresPlugin.Log.LogInfo((object)"Ghostship network object adopted; server buoyancy and course control are active."); } if (type == EncounterType.Smugglers) { val4 = ValheimReflection.SpawnNetworkedPrefab("VikingShip", target, val, ship.gameObject, "OceanAdventuresSmugglerBoat"); if ((Object)(object)val4 == (Object)null) { throw new InvalidOperationException("Smuggler longship spawn did not return its network instance."); } activeEncounter.RewardChest = val4; ZNetView component = val4.GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { throw new InvalidOperationException("Smuggler longship has no network identity."); } activeEncounter.RewardChestId = component.GetZDO().m_uid; component.GetZDO().Set("OceanAdventuresRewardChest", true); component.GetZDO().Set("OceanAdventuresChestReady", false); component.GetZDO().Set("OceanAdventuresSceneActive", true); if ((Object)(object)val4.GetComponent<OceanAdventuresSmugglerBoat>() == (Object)null) { val4.AddComponent<OceanAdventuresSmugglerBoat>(); } ValheimReflection.SetNetworkMarker(val4, "OceanAdventuresSceneActor"); if (long.TryParse(ownerId, out var result)) { component.GetZDO().Set(ZDOVars.s_creator, result); } OceanAdventuresNetworkLifecyclePatches.RegisterSceneActor(component); ValheimReflection.SetNetworkPersistence(val4, persistent: true); Container componentInChildren = val4.GetComponentInChildren<Container>(true); if ((Object)(object)componentInChildren == (Object)null) { throw new InvalidOperationException("Smuggler longship has no native hold."); } ((Behaviour)componentInChildren).enabled = false; } if (type == EncounterType.Shipwreck) { SpawnSurvivor(activeEncounter); } if (type == EncounterType.Smugglers) { SpawnSmugglerCrew(activeEncounter); } activeEncounter.ServerVisual = CreateServerVisual(activeEncounter, val3, val4); if (type == EncounterType.GhostShip) { InitializeGhostCargo(activeEncounter); if (activeEncounter.GhostCargoUnavailable) { AbortUnavailableGhostCargo(activeEncounter); return false; } } if (!string.IsNullOrEmpty(ownerId)) { contracts.PublishProfile(ownerId); } Broadcast(activeEncounter, OceanAdventuresNetworkOp.EncounterStart); if (diagnostics) { ManualLogSource log = OceanAdventuresPlugin.Log; string[] obj = new string[5] { "Manual ", type.ToString(), " placement passed water, shoreline, footprint and route checks at ", null, null }; Vector3 position = activeEncounter.Position; obj[3] = ((object)(*(Vector3*)(&position))/*cast due to .constrained prefix*/).ToString(); obj[4] = "."; log.LogInfo((object)string.Concat(obj)); } return true; } catch (Exception ex) { OceanAdventuresPlugin.Log.LogError((object)("Encounter creation failed: " + ex)); EndEncounter(activeEncounter, EncounterState.Aborted); return false; } bool Reject(string reason) { if (diagnostics) { OceanAdventuresPlugin.Log.LogInfo((object)("Manual " + type.ToString() + " placement rejected: " + reason + ".")); } return false; } } private GameObject? CreateServerVisual(ActiveEncounter encounter, GameObject? networkedGhostShip, GameObject? networkedSmugglerBoat = null) { EncounterSnapshot snapshot = Snapshot(encounter); visuals.ApplyStart(snapshot, networkedGhostShip, networkedSmugglerBoat); return visuals.GetObject(encounter.Id); } private static void SpawnSurvivor(ActiveEncounter encounter) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) GameObject val = ValheimReflection.SpawnNetworkedPrefab(((Object)(object)ValheimReflection.GetPrefab("Dverger") != (Object)null) ? "Dverger" : "DvergerMage", encounter.Position + Vector3.up * 0.5f, Quaternion.identity, null, "OceanAdventuresSurvivor") ?? throw new InvalidOperationException("Native castaway Dvergr prefab is unavailable."); encounter.Combatants.Add(val); ValheimReflection.SetNetworkMarker(val, "OceanAdventuresSceneActor"); ValheimReflection.SetNetworkBool(val, "OceanAdventuresSceneActive", value: true); ValheimReflection.SetNetworkPersistence(val, persistent: true); encounter.SceneActorIds.Add(val.GetComponent<ZNetView>().GetZDO().m_uid); val.AddComponent<OceanAdventuresSurvivor>(); } public void InteractScene(Player player, string id, int cargoIndex) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: 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_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) if (!ValheimReflection.IsServer() || (Object)(object)player == (Object)null || ((Character)player).IsDead() || !active.TryGetValue(id, out ActiveEncounter value)) { return; } if (value.Type == EncounterType.Smugglers) { if (cargoIndex == 0 && value.State == EncounterState.ReadyToLoot) { SmugglerCrewState? smugglerCrew = value.SmugglerCrew; if (smugglerCrew == null) { _ = 1; } else _ = !smugglerCrew.Complete; } } else { if (value.Type != EncounterType.Shipwreck || (value.State != EncounterState.Active && value.State != EncounterState.ReadyToLoot) || value.Combatants.Count == 0 || (Object)(object)value.Combatants[0] == (Object)null) { return; } string playerId = ValheimReflection.GetPlayerId(player); bool flag; if (cargoIndex == -1) { Vector3 val = CurrentWaterPosition(value.Position) + value.SceneRotation * WreckSceneLayout.ReturnOffset; if (Vector3.Distance(((Component)player).transform.position, val) > 11f) { return; } if (value.State == EncounterState.ReadyToLoot && value.Cargo.Complete) { if (!value.Cargo.CanClaim(value.State, value.OwnerId, playerId)) { network.SendNotice(player, OceanAdventuresNoticeCode.OnlyContractHolder); } return; } if (value.State != EncounterState.Active) { return; } flag = value.Cargo.TryReturn(playerId); } else { if (value.State != EncounterState.Active || cargoIndex < 0 || cargoIndex >= value.CargoPositions.Length || Vector3.Distance(((Component)player).transform.position, CurrentWaterPosition(value.CargoPositions[cargoIndex])) > 4f) { return; } flag = value.Cargo.TryCollect(cargoIndex, playerId); } if (flag) { if (value.Cargo.Complete) { value.State = EncounterState.ReadyToLoot; } visuals.ApplyUpdate(Snapshot(value)); Broadcast(value, OceanAdventuresNetworkOp.EncounterUpdate); } } } private static Vector3 CurrentWaterPosition(Vector3 anchor) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) float liquidLevel = Floating.GetLiquidLevel(anchor, 1f, (LiquidType)0); if (liquidLevel > -1000f && !float.IsNaN(liquidLevel) && !float.IsInfinity(liquidLevel)) { anchor.y = liquidLevel; } return anchor; } private void UpdateRewardChest(ActiveEncounter encounter) { //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_03db: Unknown result type (might be due to invalid IL or missing references) if (Time.time < encounter.NextRewardChestAttempt) { return; } encounter.NextRewardChestAttempt = Time.time + 0.5f; Player val = null; if (!string.IsNullOrEmpty(encounter.OwnerId)) { ContractRecord activeContract = store.GetProfile(encounter.OwnerId).ActiveContract; if (activeContract == null || activeContract.Id != encounter.ContractId) { EndEncounter(encounter, EncounterState.Aborted); return; } val = ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player candidate) => (Object)(object)candidate != (Object)null && !((Character)candidate).IsDead() && ValheimReflection.GetPlayerId(candidate) == encounter.OwnerId)); } if (!encounter.ChestRewardsPrepared) { if ((Object)(object)val != (Object)null) { if (!contracts.TryPrepareChest(val, encounter.Type, encounter.ContractId, out List<RewardGrant> remaining)) { encounter.NextRewardChestAttempt = Time.time + 10f; return; } encounter.ChestRemaining = remaining; } else { if (!string.IsNullOrEmpty(encounter.OwnerId)) { return; } encounter.ChestRemaining = rewards.CreateFreeEncounterLoot(encounter.Type, encounter.SmugglerCrew?.Count ?? 0, HighestKnownTierNear(encounter.Position), encounter.Id); } encounter.ChestRewardsPrepared = true; } if ((Object)(object)encounter.RewardChest == (Object)null) { if (!((ZDOID)(ref encounter.RewardChestId)).Equals(default(ZDOID))) { ZNetScene instance = ZNetScene.instance; GameObject val2 = ((instance != null) ? instance.FindInstance(encounter.RewardChestId) : null); if ((Object)(object)val2 == (Object)null) { if (++encounter.RewardChestAttempts >= 20) { AbortUnavailableRewardChest(encounter, "The original reward vessel was lost; this scene cannot mint another copy."); } return; } encounter.RewardChest = val2; } else { if (encounter.Type == EncounterType.Smugglers) { AbortUnavailableRewardChest(encounter, "The smuggler longship was not available for its hold."); return; } Vector3 returnOffset = WreckSceneLayout.ReturnOffset; Vector3 position = CurrentWaterPosition(encounter.Position) + encounter.SceneRotation * returnOffset; GameObject val3 = ValheimReflection.SpawnNetworkedPrefab("piece_chest_wood", position, encounter.SceneRotation, null, "OceanAdventuresRewardChest"); if ((Object)(object)val3 == (Object)null) { AbortUnavailableRewardChest(encounter, "Native wooden chest prefab is unavailable."); return; } encounter.RewardChest = val3; ZNetView component = val3.GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { AbortUnavailableRewardChest(encounter, "Native reward chest has no network identity."); return; } encounter.RewardChestId = component.GetZDO().m_uid; OceanAdventuresNetworkLifecyclePatches.RegisterSceneActor(component); ValheimReflection.SetNetworkMarker(val3, "OceanAdventuresSceneActor"); ValheimReflection.SetNetworkBool(val3, "OceanAdventuresSceneActive", value: true); ValheimReflection.SetNetworkBool(val3, "OceanAdventuresChestReady", value: false); ValheimReflection.SetNetworkPersistence(val3, persistent: true); component.GetZDO().Set("OceanAdventuresChestHeightOffset", returnOffset.y); (val3.GetComponent<SceneWaterAnchor>() ?? val3.AddComponent<SceneWaterAnchor>()).Configure(returnOffset.y); Container componentInChildren = val3.GetComponentInChildren<Container>(true); if ((Object)(object)componentInChildren == (Object)null || !ConfigureRewardChestAccess(val3, componentInChildren, encounter.OwnerId)) { AbortUnavailableRewardChest(encounter, "Native reward chest cannot enforce its owner access."); return; } BoxCollider obj = ((Component)componentInChildren).gameObject.AddComponent<BoxCollider>(); obj.center = new Vector3(0f, 0.65f, 0f); obj.size = new Vector3(2.2f, 1.6f, 2.2f); ((Collider)obj).isTrigger = true; Broadcast(encounter, OceanAdventuresNetworkOp.EncounterUpdate); } } GameObject rewardChest = encounter.RewardChest; if ((Object)(object)rewardChest == (Object)null) { AbortUnavailableRewardChest(encounter, "Reward chest disappeared before it was looted."); return; } Container componentInChildren2 = rewardChest.GetComponentInChildren<Container>(true); if (!((Object)(object)componentInChildren2 == (Object)null) && ValheimReflection.TryEnsureContainerInventory(componentInChildren2, out object inventory)) { Inventory val4 = (Inventory)((inventory is Inventory) ? inventory : null); if (val4 != null) { if (!encounter.RewardChestReady) { val4.RemoveAll(); foreach (RewardGrant item in encounter.ChestRemaining) { if (ValheimReflection.Invoke(val4, "AddItem", item.Prefab, item.Amount, item.Quality, 0, 0L, "", false, false) == null) { if (++encounter.RewardChestAttempts >= 10) { AbortUnavailableRewardChest(encounter, "Native reward chest rejected an item."); } return; } } if (!ChestMatches(encounter.ChestRemaining, val4) || GhostHoldInteractionPatch.TryFindBadIcon(val4, out string _)) { if (++encounter.RewardChestAttempts >= 10) { AbortUnavailableRewardChest(encounter, "Native reward chest cargo failed validation."); } return; } ValheimReflection.Invoke(componentInChildren2, "OnContainerChanged"); encounter.RewardChestReady = true; ValheimReflection.SetNetworkBool(rewardChest, "OceanAdventuresChestReady", value: true); if (encounter.Type == EncounterType.Smugglers) { rewardChest.GetComponent<OceanAdventuresSmugglerBoat>()?.ApplyReady(ready: true); } OceanAdventuresPlugin.Log.LogInfo((object)(encounter.Type.ToString() + " native reward chest ready: " + string.Join(", ", encounter.ChestRemaining.Select((RewardGrant item) => item.Prefab + " x" + item.Amount)) + (((Object)(object)val == (Object)null) ? " (free)." : " (private contract chest)."))); visuals.ApplyUpdate(Snapshot(encounter)); Broadcast(encounter, OceanAdventuresNetworkOp.EncounterUpdate); return; } object obj2 = ValheimReflection.Invoke(componentInChildren2, "IsInUse"); if (obj2 is bool && (bool)obj2) { return; } List<RewardGrant> list = ObserveChestBalance(encounter.ChestRemaining, val4); if (!SameChestBalance(list, encounter.ChestRemaining)) { if (!string.IsNullOrEmpty(encounter.OwnerId) && !contracts.SaveChestRemaining(encounter.OwnerId, encounter.ContractId, list)) { return; } encounter.ChestRemaining = list; OceanAdventuresPlugin.Log.LogInfo((object)(encounter.Type.ToString() + " chest balance updated: " + ((list.Count == 0) ? "empty." : (string.Join(", ", list.Select((RewardGrant item) => item.Prefab + " x" + item.Amount)) + ".")))); } if (encounter.ChestRemaining.Count == 0 && val4.GetAllItems().Count == 0 && (string.IsNullOrEmpty(encounter.OwnerId) || (!((Object)(object)val == (Object)null) && contracts.CompleteChest(val, encounter.Type, encounter.ContractId)))) { MarkChestLooted(encounter, val); } return; } } if (++encounter.RewardChestAttempts >= 10) { AbortUnavailableRewardChest(encounter, "Native reward chest inventory did not initialize."); } } private static bool ConfigureRewardChestAccess(GameObject chest, Container container, string ownerId) { FieldInfo fieldInfo = AccessTools.Field(typeof(Container), "m_privacy"); if (fieldInfo == null) { return false; } fieldInfo.SetValue(container, Enum.ToObject(fieldInfo.FieldType, string.IsNullOrEmpty(ownerId) ? 2 : 0)); AccessTools.Field(typeof(Container), "m_autoDestroyEmpty")?.SetValue(container, false); AccessTools.Field(typeof(Container), "m_checkGuardStone")?.SetValue(container, false); if (string.IsNullOrEmpty(ownerId)) { return true; } if (!long.TryParse(ownerId, out var result) || result == 0L) { return false; } Piece componentInChildren = chest.GetComponentInChildren<Piece>(true); FieldInfo fieldInfo2 = AccessTools.Field(typeof(Piece), "m_creator"); ZNetView component = chest.GetComponent<ZNetView>(); if ((Object)(object)componentInChildren == (Object)null || fieldInfo2 == null || (Object)(object)component == (Object)null || !component.IsValid()) { return false; } fieldInfo2.SetValue(componentInChildren, result); component.GetZDO().Set(ZDOVars.s_creator, result); return componentInChildren.GetCreator() == result; } private static List<RewardGrant> ObserveChestBalance(List<RewardGrant> expected, Inventory inventory) { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem == null || (Object)(object)allItem.m_dropPrefab == (Object)null) { return expected.Select((RewardGrant grant) => new RewardGrant { Prefab = grant.Prefab, Amount = grant.Amount, Quality = grant.Quality }).ToList(); } GameObject dropPrefab = allItem.m_dropPrefab; string text = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); if (!string.IsNullOrEmpty(text)) { string key = text + "|" + allItem.m_quality; dictionary[key] = (dictionary.TryGetValue(key, out var value) ? (value + allItem.m_stack) : allItem.m_stack); } } List<RewardGrant> list = new List<RewardGrant>(); foreach (RewardGrant item in expected) { string key2 = item.Prefab + "|" + item.Quality; int value2; int num = (dictionary.TryGetValue(key2, out value2) ? value2 : 0); int num2 = Math.Min(item.Amount, num); dictionary[key2] = Math.Max(0, num - num2); if (num2 > 0) { list.Add(new RewardGrant { Prefab = item.Prefab, Amount = num2, Quality = item.Quality }); } } return list; } private static bool ChestMatches(List<RewardGrant> expected, Inventory inventory) { if (SameChestBalance(expected, ObserveChestBalance(expected, inventory))) { return inventory.GetAllItems().Count == expected.Count; } return false; } private static bool SameChestBalance(List<RewardGrant> left, List<RewardGrant> right) { if (left.Count == right.Count) { return left.Zip(right, (RewardGrant a, RewardGrant b) => a.Prefab == b.Prefab && a.Amount == b.Amount && a.Quality == b.Quality).All((bool match) => match); } return false; } private void AbortUnavailableRewardChest(ActiveEncounter encounter, string reason) { OceanAdventuresPlugin.Log.LogWarning((object)("Reward chest unavailable: " + reason)); EndEncounter(encounter, EncounterState.Aborted); } private void MarkChestLooted(ActiveEncounter encounter, Player? owner) { encounter.State = EncounterState.Claimed; encounter.SalvageClaimedAt = Time.time; if ((Object)(object)owner != (Object)null) { network.SendNotice(owner, (encounter.Type == EncounterType.Shipwreck) ? OceanAdventuresNoticeCode.WreckRewardQueued : OceanAdventuresNoticeCode.SmugglerRewardQueued); } visuals.ApplyUpdate(Snapshot(encounter)); Broadcast(encounter, OceanAdventuresNetworkOp.EncounterUpdate); } private void SpawnSmugglerCrew(ActiveEncounter encounter) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) SmugglerCrewState crew = new SmugglerCrewState(config.SmugglerCrewSize); encounter.SmugglerCrew = crew; encounter.SmugglerMissingSince = Enumerable.Repeat(-1f, crew.Count).ToArray(); for (int i = 0; i < crew.Count; i++) { Vector3 position = SmugglerCrewSpawnPosition(encounter, i); GameObject val = ValheimReflection.SpawnNetworkedPrefab("Dverger", position, encounter.SceneRotation, null, "OceanAdventuresSmuggler") ?? throw new InvalidOperationException("Could not spawn the full native smuggler crew."); encounter.Combatants.Add(val); ZNetView component = val.GetComponent<ZNetView>(); Humanoid component2 = val.GetComponent<Humanoid>(); if ((Object)(ob