using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("PostalExpansion")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1")] [assembly: AssemblyProduct("PostalExpansion")] [assembly: AssemblyTitle("PostalExpansion")] [assembly: AssemblyVersion("1.1.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PostalExpansion { internal sealed class AestraAbbeyGraveRobberTableMission : RegisteredLetterMissionDefinition { internal static readonly AestraAbbeyGraveRobberTableMission Instance = new AestraAbbeyGraveRobberTableMission(); private AestraAbbeyGraveRobberTableMission() : base("aestra_abbey_grave_robber_table", "Aestra Abbey", 26, new Vector3(232.158f, -0.924f, 194.327f), new Vector3(0f, 136.291f, 0f), new Vector3(2f, 2f, 2f), LetterDeliveryWindow.Day, new int[2] { 18, 6 }, "Table of grave robber") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { int originPortIndex = LetterMissionDefinition.GetOriginPortIndex(mission); if (originPortIndex == 6 || originPortIndex == 18) { return "(You put the letter under the table)"; } return UnexpectedOrigin(mission); } } internal sealed class AestraAbbeyMainAbbeyMission : RegisteredLetterMissionDefinition { internal static readonly AestraAbbeyMainAbbeyMission Instance = new AestraAbbeyMainAbbeyMission(); private AestraAbbeyMainAbbeyMission() : base("aestra_abbey_main_abbey", "Aestra Abbey", 26, new Vector3(-117.4f, 56.962f, -81.718f), new Vector3(0f, 220.653f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[3] { 28, 21, 10 }, "Abbey") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { return LetterMissionDefinition.GetOriginPortIndex(mission) switch { 28 => "Castigo corpus meum.", 21 => "A solis ortus cardine.", 10 => "Pacem in terries.", _ => UnexpectedOrigin(mission), }; } } internal sealed class AestraAbbeyOracleBenchMission : AnonymousLetterMissionDefinition { internal static readonly AestraAbbeyOracleBenchMission Instance = new AestraAbbeyOracleBenchMission(); private AestraAbbeyOracleBenchMission() : base("aestra_abbey_oracle_bench", "Aestra Abbey", 26, new Vector3(-129.328f, -1.928f, 1355.791f), new Vector3(0f, 257.793f, 0f), new Vector3(2.5f, 2.5f, 2.5f), 1362.3f, new LetterDeliveryWindow(19f, 7f, "19-07"), new int[3] { 24, 29, 27 }, "Oracle bench") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { return LetterMissionDefinition.GetOriginPortIndex(mission) switch { 24 => "(Letter open by itself once you approach one of the shrooms, showing totemic pattern before flying away. Closest shroom is gone)", 29 => "(The letter tries to let out the light but it got put out by shrooms quickly.)", 27 => "(You place the letter on bench. You heard a pleasent chime.)", _ => UnexpectedOrigin(mission), }; } } internal sealed class AlAnkhAcademyKeyKeeperMission : RegisteredLetterMissionDefinition { internal static readonly AlAnkhAcademyKeyKeeperMission Instance = new AlAnkhAcademyKeyKeeperMission(); private AlAnkhAcademyKeyKeeperMission() : base("al_ankh_academy_key_keeper", "Al'Ankh Academy", 5, new Vector3(-0.173f, 0.833f, 1.175f), new Vector3(0f, 170.709f, 0f), new Vector3(2f, 2f, 2f), LetterDeliveryWindow.Day, new int[11] { 0, 6, 32, 16, 9, 10, 25, 22, 23, 15, 21 }, "Key Keeper") { }//IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 0: case 6: case 9: case 15: case 22: case 23: return "Endless applications... I need a day off."; case 32: return "Oh! Ibn Doraid will be interested in this!"; case 16: return "These glasses cost a big fortune for sure."; case 10: case 25: return "These ideas are... interesting."; case 21: return "(Whistles) Your journey must have been eventful."; default: return UnexpectedOrigin(mission); } } } internal sealed class AlbacoreTownCreepyGuyMission : RegisteredLetterMissionDefinition { internal static readonly AlbacoreTownCreepyGuyMission Instance = new AlbacoreTownCreepyGuyMission(); private AlbacoreTownCreepyGuyMission() : base("albacore_town_creepy_guy", "Albacore Town", 3, new Vector3(40.721f, 3.318f, -50.81f), new Vector3(0f, 55.492f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Night, new int[3] { 15, 12, 25 }, "Creepy guy") { }//IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 12: case 15: return "Were you seen? You'd better not."; case 25: return "This makes me homesick... but I need to know where the fishing ground is."; default: return UnexpectedOrigin(mission); } } } internal sealed class AlchemistsIslandApprenticeMission : RegisteredLetterMissionDefinition { internal static readonly AlchemistsIslandApprenticeMission Instance = new AlchemistsIslandApprenticeMission(); private AlchemistsIslandApprenticeMission() : base("alchemists_island_apprentice", "Alchemist's Island", 4, new Vector3(-0.235f, 0.336f, 14.538f), new Vector3(0f, 35.018f, 0f), new Vector3(3f, 3f, 3f), LetterDeliveryWindow.Day, new int[12] { 0, 6, 32, 5, 20, 9, 10, 25, 22, 19, 28, 21 }, "Alchemist's apprentice") { }//IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 0: case 6: case 9: case 20: case 22: case 32: return "I will inform my mentor about this. Thank you."; case 5: return "This again... When will they stop trying?"; case 10: case 25: return "That's... a brilliant idea! I need to tell my mentor now!"; case 19: case 28: return "Your God cannot help the exhausted miners? Heh."; case 21: return "...Really?"; default: return UnexpectedOrigin(mission); } } } internal sealed class AlNilemLionsFangObeliskMission : AnonymousLetterMissionDefinition { internal static readonly AlNilemLionsFangObeliskMission Instance = new AlNilemLionsFangObeliskMission(); private AlNilemLionsFangObeliskMission() : base("al_nilem_lions_fang_obelisk", "Al'Nilem", 1, new Vector3(807.097f, 4.672f, 1451.161f), new Vector3(0f, 318.985f, 0f), new Vector3(5f, 5f, 5f), 1659.3f, new LetterDeliveryWindow(11f, 13f, "11-13"), new int[6] { 2, 31, 32, 29, 24, 28 }, "Isle of Clear Mind Obelisk") { }//IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 2: case 31: case 32: return "(Letter burns away after you place it on ground, you feel hotter)"; case 29: return "(Letter open by itself once you approach the obelisk, showing totemic pattern before flying away)"; case 24: case 28: return "(Letter got eject away before you can approach, falls into the seas)"; default: return UnexpectedOrigin(mission); } } } [HarmonyPatch(typeof(ShipItem), "UpdateLookText")] internal static class AnonymousLetterItemLookTextPatch { private static bool Prefix(ShipItem __instance) { if ((Object)(object)__instance == (Object)null || !PostalMail.IsRegisteredLetter(((Component)__instance).GetComponent())) { return true; } Good component = ((Component)__instance).GetComponent(); int num = (((Object)(object)component != (Object)null) ? component.GetMissionIndex() : (-1)); if (num < 0 || PlayerMissions.missions == null || num >= PlayerMissions.missions.Length) { return true; } Mission val = PlayerMissions.missions[num]; if (!AnonymousLetterMissions.TryGetDefinition(val, out var _)) { return true; } ((GoPointerButton)__instance).lookText = "due: " + val.GetDueText(); return false; } } internal abstract class AnonymousLetterMissionDefinition : LetterMissionDefinition { internal sealed override float DistanceToOffice { get; } internal sealed override bool UsePersistentWorldAnchor => true; internal sealed override bool UseDeliveryLocationForRoute => true; internal sealed override bool ShowDeliveryCoordinates => true; internal sealed override bool HideDestination => true; protected AnonymousLetterMissionDefinition(string id, string destinationPortName, int destinationPortIndex, Vector3 localPosition, Vector3 localEulerAngles, Vector3 triggerSize, float distanceToOffice, LetterDeliveryWindow deliveryWindow, int[] spawnPortIndices, string locationDescription) : base(id, destinationPortName, destinationPortIndex, localPosition, localEulerAngles, triggerSize, deliveryWindow, spawnPortIndices, locationDescription) { //IL_0004: 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_0008: Unknown result type (might be due to invalid IL or missing references) DistanceToOffice = distanceToOffice; } } internal static class AnonymousLetterMissionRegistry { private static readonly LetterMissionRegistry Registry = new LetterMissionRegistry("Anonymous Letter", OasisPirateHideoutMission.Instance, HappyBayUnnamedGraveMission.Instance, EastwindLighthouseMission.Instance, SaffronIslandTraderMission.Instance, AlNilemLionsFangObeliskMission.Instance, OldAnkhTownClearMindMission.Instance, AestraAbbeyOracleBenchMission.Instance, TurtleIslandPonderingPeakMission.Instance, DeadCoveNamelessRockMission.Instance); internal static IReadOnlyList All => Registry.All; } internal static class AnonymousLetterMissions { internal const int RequiredReputation = 8; internal const float DueDayBaseSpeed = 3f; internal const float ReputationMultiplier = 10f; internal const int FixedGoldReward = 5; internal const int OneDayLateGoldReward = 1; internal const int SeverelyLatePenalty = -8000; private static readonly LetterMissionSaveKeys SaveKeys = new LetterMissionSaveKeys("PostalExpansion.AnonymousLetter.Active.v1", "PostalExpansion.AnonymousLetter.Claimed.v1"); private static readonly LetterMissionSaveKeys LegacyPirateHideoutSaveKeys = new LetterMissionSaveKeys("PostalExpansion.RegisteredLetter.Active.v1", "PostalExpansion.RegisteredLetter.Claimed.v1"); private static readonly LetterMissionSet Missions = new LetterMissionSet("Anonymous Letter", AnonymousLetterMissionRegistry.All, SaveKeys, 8, 3f, 5, (Port destination) => "Anonymous letter"); internal static void AddMissions(Port origin, List missions) { Missions.AddMissions(origin, missions); } internal static int GetGoldReward(int daysLate) { if (daysLate <= 0) { return 5; } return (daysLate == 1) ? 1 : 0; } internal static List GetDebugStatusLines() { return Missions.GetDebugStatusLines(); } internal static bool TryGetDefinition(Mission mission, out AnonymousLetterMissionDefinition definition) { return Missions.TryGetDefinition(mission, out definition); } internal static void MissionAccepted(Mission mission) { Missions.MissionAccepted(mission); } internal static void MissionDelivered(Mission mission) { Missions.MissionDelivered(mission); } internal static void SavePersistentState() { Missions.SavePersistentState(); } internal static void LoadPersistentState() { Missions.LoadPersistentState(LegacyPirateHideoutSaveKeys); } internal static void ResetRuntimeState() { Missions.ResetRuntimeState(); } } internal sealed class ChronosChurchHouseMission : RegisteredLetterMissionDefinition { internal static readonly ChronosChurchHouseMission Instance = new ChronosChurchHouseMission(); private ChronosChurchHouseMission() : base("chronos_church_house", "Chronos", 21, new Vector3(-19.175f, 31.109f, -62.909f), new Vector3(0f, 17.783f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[5] { 26, 28, 5, 0, 9 }, "Church house") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 26: case 28: return "There is little common ground after the schism."; case 5: return "We appreciate the knowledge seeking."; case 0: case 9: return "The Church is under maintenance thus not open for visiting."; default: return UnexpectedOrigin(mission); } } } internal sealed class ConfigurationManagerAttributes { public Action CustomDrawer; public object DefaultValue; } internal sealed class CrabBeachHouseMission : RegisteredLetterMissionDefinition { internal static readonly CrabBeachHouseMission Instance = new CrabBeachHouseMission(); private CrabBeachHouseMission() : base("crab_beach_house", "Crab Beach", 11, new Vector3(-100.447f, 4.35f, 30.655f), new Vector3(0f, 188.793f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[5] { 9, 0, 15, 22, 21 }, "House at beach") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 0: case 9: case 15: case 22: return "I SAID I DON'T BUILD SHIPS ANYMORE! GO AWAY!"; case 21: return "...Where?"; default: return UnexpectedOrigin(mission); } } } internal sealed class DailySpecialMissionState { private sealed class Marker { } private readonly string activeMissionDataKey; private readonly string claimedOfferDataKey; private readonly Func serializeActiveMission; private readonly Func matchesSavedMission; private readonly Action onMissionRestored; private ConditionalWeakTable markedMissions = new ConditionalWeakTable(); private readonly HashSet claimedOffers = new HashSet(StringComparer.Ordinal); internal DailySpecialMissionState(string activeMissionDataKey, string claimedOfferDataKey, Func serializeActiveMission, Func matchesSavedMission, Action onMissionRestored) { this.activeMissionDataKey = activeMissionDataKey; this.claimedOfferDataKey = claimedOfferDataKey; this.serializeActiveMission = serializeActiveMission; this.matchesSavedMission = matchesSavedMission; this.onMissionRestored = onMissionRestored; } internal bool IsMarked(Mission mission) { Marker value; if (mission != null) { return markedMissions.TryGetValue(mission, out value); } return false; } internal void Mark(Mission mission) { if (mission != null) { markedMissions.GetValue(mission, (Mission _) => new Marker()); } } internal bool CanOfferAt(Port origin) { if ((Object)(object)origin == (Object)null) { return false; } PruneClaimsForCurrentDay(); return !claimedOffers.Contains(GetClaimKey(GameState.day, origin.portIndex)); } internal void MissionAccepted(Mission mission) { if (IsMarked(mission) && !((Object)(object)mission.originPort == (Object)null) && mission.missionIndex >= 0 && PlayerMissions.missions != null && mission.missionIndex < PlayerMissions.missions.Length && PlayerMissions.missions[mission.missionIndex] == mission) { PruneClaimsForCurrentDay(); claimedOffers.Add(GetClaimKey(GameState.day, mission.originPort.portIndex)); } } internal void SavePersistentState() { if (GameState.modData == null) { GameState.modData = new Dictionary(); } List list = new List(); if (PlayerMissions.missions != null) { for (int i = 0; i < PlayerMissions.missions.Length; i++) { Mission val = PlayerMissions.missions[i]; if (IsMarked(val) && !((Object)(object)val.originPort == (Object)null) && !((Object)(object)val.destinationPort == (Object)null)) { string text = serializeActiveMission(i, val); if (!string.IsNullOrEmpty(text)) { list.Add(text); } } } } GameState.modData[activeMissionDataKey] = string.Join(";", list); PruneClaimsForCurrentDay(); List list2 = new List(claimedOffers); list2.Sort(StringComparer.Ordinal); GameState.modData[claimedOfferDataKey] = string.Join(";", list2); } internal void LoadPersistentState() { ResetRuntimeState(); if (GameState.modData != null) { LoadClaims(); LoadActiveMissions(); } } internal void ResetRuntimeState() { markedMissions = new ConditionalWeakTable(); claimedOffers.Clear(); } private void LoadClaims() { if (!GameState.modData.TryGetValue(claimedOfferDataKey, out var value)) { return; } string value2 = GameState.day + ","; string[] array = value.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { if (text.StartsWith(value2, StringComparison.Ordinal)) { claimedOffers.Add(text); } } } private void LoadActiveMissions() { if (PlayerMissions.missions == null || !GameState.modData.TryGetValue(activeMissionDataKey, out var value)) { return; } string[] array = value.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ',' }); if (array2.Length != 0 && int.TryParse(array2[0], out var result) && result >= 0 && result < PlayerMissions.missions.Length) { Mission val = PlayerMissions.missions[result]; if (val != null && val.missionIndex == result && (Object)(object)val.originPort != (Object)null && (Object)(object)val.destinationPort != (Object)null && matchesSavedMission(val, array2)) { Mark(val); onMissionRestored?.Invoke(val); } } } } private static string GetClaimKey(int day, int originPortIndex) { return day + "," + originPortIndex; } private void PruneClaimsForCurrentDay() { string currentDayPrefix = GameState.day + ","; claimedOffers.RemoveWhere((string claim) => !claim.StartsWith(currentDayPrefix, StringComparison.Ordinal)); } } internal sealed class DeadCoveNamelessRockMission : AnonymousLetterMissionDefinition { internal static readonly DeadCoveNamelessRockMission Instance = new DeadCoveNamelessRockMission(); private DeadCoveNamelessRockMission() : base("dead_cove_nameless_rock", "Dead Cove", 30, new Vector3(-852.584f, 2.539f, -826.316f), new Vector3(0f, 26.327f, 0f), new Vector3(8f, 8f, 8f), 1195.2f, LetterDeliveryWindow.Night, new int[3] { 1, 19, 11 }, "Nameless rock southren tip") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { int originPortIndex = LetterMissionDefinition.GetOriginPortIndex(mission); if (originPortIndex == 1 || originPortIndex == 11 || originPortIndex == 19) { return "(There is no remarkable landmark so you just throw the letter on the rock randomly. You have no idea what you are doing.)"; } return UnexpectedOrigin(mission); } } internal sealed class DragonCliffsBrownTobaccoVendorMission : RegisteredLetterMissionDefinition { internal static readonly DragonCliffsBrownTobaccoVendorMission Instance = new DragonCliffsBrownTobaccoVendorMission(); private DragonCliffsBrownTobaccoVendorMission() : base("dragon_cliffs_brown_tobacco_vendor", "Dragon Cliffs", 9, new Vector3(47.025f, 1.993f, -11.703f), new Vector3(0f, 106.371f, 0f), new Vector3(3f, 3f, 3f), LetterDeliveryWindow.Day, new int[5] { 0, 15, 12, 20, 28 }, "Brown Tobacco Vendor") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 12: case 15: case 20: case 28: return "(Sigh) How much longer do I need to stay here...?"; case 0: return "Hmm..."; default: return UnexpectedOrigin(mission); } } } internal sealed class DragonCliffsChimesHutMission : RegisteredLetterMissionDefinition { internal static readonly DragonCliffsChimesHutMission Instance = new DragonCliffsChimesHutMission(); private DragonCliffsChimesHutMission() : base("dragon_cliffs_small_hut_beside_chimes", "Dragon Cliffs", 9, new Vector3(-45.037f, 0.07f, -119.983f), new Vector3(0f, 148.902f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[10] { 13, 29, 24, 16, 1, 25, 22, 28, 10, 21 }, "Small hut beside chimes") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 1: case 10: case 13: case 16: case 22: case 24: case 25: case 28: case 29: return "(Gasp) It finally arrived! Thanks!"; case 21: return "What took you so long?"; default: return UnexpectedOrigin(mission); } } } internal sealed class DragonCliffsTeaMerchantHouseMission : RegisteredLetterMissionDefinition { internal static readonly DragonCliffsTeaMerchantHouseMission Instance = new DragonCliffsTeaMerchantHouseMission(); private DragonCliffsTeaMerchantHouseMission() : base("dragon_cliffs_house_behind_tea_merchant", "Dragon Cliffs", 9, new Vector3(-6.196f, 2.711f, -52.022f), new Vector3(0f, 101.025f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Night, new int[5] { 6, 20, 22, 18, 0 }, "House behind tea merchant") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 6: if (!VanillaQuestState.IsHideoutQuestComplete()) { return "Thank you."; } return "New order, matey?"; case 0: case 18: case 20: case 22: if (!VanillaQuestState.IsHideoutQuestComplete()) { return "...You can leave now."; } return "Which scurvy dog brings ye 'ere?"; default: return UnexpectedOrigin(mission); } } } internal sealed class EastwindLighthouseMission : AnonymousLetterMissionDefinition { internal static readonly EastwindLighthouseMission Instance = new EastwindLighthouseMission(); private EastwindLighthouseMission() : base("eastwind_lighthouse", "Eastwind", 19, new Vector3(3070.795f, 8.574f, -4694.155f), new Vector3(0f, 266.217f, 0f), new Vector3(1.5f, 1.5f, 1.5f), 5610.9f, new LetterDeliveryWindow(22f, 2f, "22-02"), new int[6] { 2, 10, 26, 21, 23, 6 }, "Light house") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 6: return "Aye matey, Me belong 'ere now."; case 2: case 10: case 21: case 23: case 26: return "YER GODS 'AVE NO POWER 'ERE LANDLUBBER!"; default: return UnexpectedOrigin(mission); } } } internal sealed class FeyValleyWaterfallManorVendorMission : RegisteredLetterMissionDefinition { internal static readonly FeyValleyWaterfallManorVendorMission Instance = new FeyValleyWaterfallManorVendorMission(); private FeyValleyWaterfallManorVendorMission() : base("fey_valley_waterfall_manor_vendor", "Fey Valley", 27, new Vector3(-126.239f, 51.409f, 42.779f), new Vector3(0f, 300.007f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[6] { 15, 0, 9, 20, 12, 26 }, "Waterfall manor vendor") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 26: return "Gods be praised! This year should be a good harvest year!"; case 12: case 20: return "We're supposed to bring their fruit back, not the other way around!"; case 0: case 9: case 15: return "I will let the Abbot know about the orders."; default: return UnexpectedOrigin(mission); } } } internal sealed class FireFishTownPainterMission : RegisteredLetterMissionDefinition { internal static readonly FireFishTownPainterMission Instance = new FireFishTownPainterMission(); private FireFishTownPainterMission() : base("fire_fish_town_painter", "Fire Fish Town", 23, new Vector3(7.186f, 3.075f, 216.135f), new Vector3(0f, 190.815f, 0f), new Vector3(3f, 3f, 3f), LetterDeliveryWindow.Day, new int[5] { 15, 0, 9, 21, 20 }, "Painter") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 0: case 15: return "Always happy to hear from fellow painters!"; case 9: return "Ugh! I escape there for a reason!"; case 20: return "Daddy..."; case 21: return "I would like to visit there one day!"; default: return UnexpectedOrigin(mission); } } } internal sealed class FireFishTownTempleHouseMission : RegisteredLetterMissionDefinition { internal static readonly FireFishTownTempleHouseMission Instance = new FireFishTownTempleHouseMission(); private FireFishTownTempleHouseMission() : base("fire_fish_town_temple_house", "Fire Fish Town", 23, new Vector3(-5.519f, 7.688f, 82.87f), new Vector3(0f, 35.728f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[1] { 10 }, "Temple house") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { if (LetterMissionDefinition.GetOriginPortIndex(mission) != 10) { return UnexpectedOrigin(mission); } return "All is well."; } } internal sealed class FireflyGrottoChurchMission : RegisteredLetterMissionDefinition { internal static readonly FireflyGrottoChurchMission Instance = new FireflyGrottoChurchMission(); private FireflyGrottoChurchMission() : base("firefly_grotto_church", "Firefly Grotto", 28, new Vector3(128.273f, 5.458f, 37.452f), new Vector3(0f, 107.82f, 0f), new Vector3(2.5f, 2.5f, 2.5f), LetterDeliveryWindow.Day, new int[4] { 26, 12, 20, 9 }, "Chruch") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 26: return "Ora et Labora."; case 9: case 12: case 20: return "We must continued our missionary work."; default: return UnexpectedOrigin(mission); } } } internal sealed class FortAestrinUpperTownGateMission : RegisteredLetterMissionDefinition { internal static readonly FortAestrinUpperTownGateMission Instance = new FortAestrinUpperTownGateMission(); private FortAestrinUpperTownGateMission() : base("fort_aestrin_upper_town_gate", "Fort Aestrin", 15, new Vector3(112.355f, 35.876f, -61.713f), new Vector3(359.526f, 116.915f, 359.692f), new Vector3(3f, 3f, 3f), LetterDeliveryWindow.Day, new int[10] { 0, 6, 22, 9, 12, 20, 26, 28, 19, 21 }, "Upper Town Gate") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 0: case 6: case 9: case 19: case 22: return "Your job here is done, courier. Now move along."; case 12: case 20: return "I wonder when we'll get the next shipment of that colorful tobacco."; case 26: case 28: return "God be praised!"; case 21: return "You've come a long way, my friend!"; default: return UnexpectedOrigin(mission); } } } internal sealed class FortAestrinWesternmostPierHouseMission : RegisteredLetterMissionDefinition { internal static readonly FortAestrinWesternmostPierHouseMission Instance = new FortAestrinWesternmostPierHouseMission(); private FortAestrinWesternmostPierHouseMission() : base("fort_aestrin_westernmost_house_at_pier", "Fort Aestrin", 15, new Vector3(-144.465f, -1.242f, -99.124f), new Vector3(359.794f, 152.539f, 359.474f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Night, new int[4] { 0, 2, 32, 31 }, "Westernmost house on the pier") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { int originPortIndex = LetterMissionDefinition.GetOriginPortIndex(mission); if (originPortIndex == 0 || originPortIndex == 2 || (uint)(originPortIndex - 31) <= 1u) { return "Sabbih Ash-shams!"; } return UnexpectedOrigin(mission); } } internal static class GoldenDeliveryMissions { private readonly struct DestinationCandidate { internal Port Destination { get; } internal float Distance { get; } internal DestinationCandidate(Port destination, float distance) { Destination = destination; Distance = distance; } } internal const string DisplayName = "Golden Delivery"; internal const int RequiredReputation = 9; internal const float MoneyMultiplier = 10f; internal const float ReputationMultiplier = 15f; internal const int SeverelyLatePenalty = -50000; private const float SpawnChance = 0.5f; private const float MinimumDistance = 288f; private const float DueDayBaseSpeed = 4f; private const float ComparableRegularMailPackageMultiplier = 2f; private const string ActiveMissionDataKey = "PostalExpansion.GoldenDelivery.Active.v1"; private const string ClaimedOfferDataKey = "PostalExpansion.GoldenDelivery.Claimed.v1"; private static readonly DailySpecialMissionState State = new DailySpecialMissionState("PostalExpansion.GoldenDelivery.Active.v1", "PostalExpansion.GoldenDelivery.Claimed.v1", SerializeActiveMission, MatchesSavedMission, ApplyMissionName); internal static bool IsGolden(Mission mission) { return State.IsMarked(mission); } internal static void AddMission(Port origin, List missions) { if (!CanOfferAt(origin) || missions == null || !PostalExpressMail.EnsureRegistered(PrefabsDirectory.instance)) { return; } Random random = new Random(GetDailySeed(origin)); if (!(random.NextDouble() >= 0.5)) { List candidates = GetCandidates(origin); if (candidates.Count != 0) { DestinationCandidate candidate = candidates[random.Next(candidates.Count)]; Mission val = GenerateMission(origin, candidate); MarkRuntime(val); missions.Add(val); } } } internal static void MissionAccepted(Mission mission) { State.MissionAccepted(mission); } internal static void SavePersistentState() { State.SavePersistentState(); } internal static void LoadPersistentState() { State.LoadPersistentState(); } internal static void ResetRuntimeState() { State.ResetRuntimeState(); } private static bool CanOfferAt(Port origin) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)origin == (Object)null || origin.portIndex == 33 || origin.portIndex == 7 || PlayerReputation.GetRepLevel(origin.region) < 9) { return false; } return State.CanOfferAt(origin); } private static List GetCandidates(Port origin) { List list = new List(); if (Port.ports == null) { return list; } Port[] ports = Port.ports; foreach (Port val in ports) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)origin) && val.portIndex != 7 && !PlayerAlreadyHasGoldenMission(origin, val)) { float distance = Mission.GetDistance(origin, val); if (distance > 288f) { list.Add(new DestinationCandidate(val, distance)); } } } return list; } private static Mission GenerateMission(Port origin, DestinationCandidate candidate) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown Good component = PostalExpressMail.Prefab.GetComponent(); int num = Mathf.RoundToInt(candidate.Distance * DebugMarketTracker.instance.missionDistanceFee * 2f * Plugin.RegularMailRewardMultiplier.Value * 10f); num = CurrencyMarket.instance.GetSellPriceInCurrency((Currency)candidate.Destination.region, (float)num, false); int num2 = MailDueDateCalculator.Calculate(origin, candidate.Destination, component, 4f); Mission val = new Mission(origin, candidate.Destination, PostalExpressMail.Prefab, 1, num, 1f, 0, num2); val.pricePerKm = (float)val.totalPrice / val.distance; return val; } private static void MarkRuntime(Mission mission) { State.Mark(mission); ApplyMissionName(mission); } private static void ApplyMissionName(Mission mission) { mission.missionName = "Golden Delivery to " + mission.destinationPort.GetPortName(); } private static bool IsExpressMission(Mission mission) { if (mission == null || (Object)(object)mission.goodPrefab == (Object)null) { return false; } return PostalMail.IsExpressMail(mission.goodPrefab.GetComponent()); } private static bool PlayerAlreadyHasGoldenMission(Port origin, Port destination) { if (PlayerMissions.missions == null) { return false; } Mission[] missions = PlayerMissions.missions; foreach (Mission val in missions) { if (IsGolden(val) && (Object)(object)val.originPort == (Object)(object)origin && (Object)(object)val.destinationPort == (Object)(object)destination) { return true; } } return false; } private static int GetDailySeed(Port origin) { return ((1907 + GameState.day) * 31 + origin.portIndex) * 31 + 9; } private static string SerializeActiveMission(int slot, Mission mission) { return string.Join(",", slot, mission.originPort.portIndex, mission.destinationPort.portIndex, mission.dueDay, mission.totalPrice); } private static bool MatchesSavedMission(Mission mission, string[] fields) { if (fields.Length == 5 && int.TryParse(fields[1], out var result) && int.TryParse(fields[2], out var result2) && int.TryParse(fields[3], out var result3) && int.TryParse(fields[4], out var result4) && IsExpressMission(mission) && mission.goodCount == 1 && mission.originPort.portIndex == result && mission.destinationPort.portIndex == result2 && mission.dueDay == result3) { return mission.totalPrice == result4; } return false; } } internal sealed class GoldRockCityPalaceFishVendorMission : RegisteredLetterMissionDefinition { internal static readonly GoldRockCityPalaceFishVendorMission Instance = new GoldRockCityPalaceFishVendorMission(); private GoldRockCityPalaceFishVendorMission() : base("gold_rock_city_palace_fish_vendor", "Gold Rock City", 0, new Vector3(-130.644f, 2.269f, 70.875f), new Vector3(0f, 226.858f, 0f), new Vector3(2f, 2f, 2f), LetterDeliveryWindow.Day, new int[7] { 15, 3, 23, 22, 9, 20, 6 }, "Fish vendor at palace") { }//IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 3: case 15: case 22: case 23: return "People love exotic fish!"; case 9: case 20: return "Eels... not sure about that."; case 6: if (!VanillaQuestState.IsHideoutQuestComplete()) { return "I will need a fast boat for this delivery."; } return "Our mutual friend has come up with some new stuff, eh?"; default: return UnexpectedOrigin(mission); } } } internal sealed class GoldRockCityPalaceGateMission : RegisteredLetterMissionDefinition { internal static readonly GoldRockCityPalaceGateMission Instance = new GoldRockCityPalaceGateMission(); private GoldRockCityPalaceGateMission() : base("gold_rock_city_palace_gate", "Gold Rock City", 0, new Vector3(-94.099f, 7.512f, -36.68f), new Vector3(0f, 261.509f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[8] { 15, 5, 6, 32, 9, 22, 20, 21 }, "Palace Gate") { }//IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { return "Letter Received. Leave."; } } internal sealed class HappyBayLocalPrinceMission : RegisteredLetterMissionDefinition { internal static readonly HappyBayLocalPrinceMission Instance = new HappyBayLocalPrinceMission(); private HappyBayLocalPrinceMission() : base("happy_bay_local_prince", "Happy Bay", 20, new Vector3(102.092f, 17.839f, -23.281f), new Vector3(0f, 81.201f, 0f), new Vector3(3f, 3f, 3f), LetterDeliveryWindow.Day, new int[10] { 0, 12, 15, 9, 22, 26, 28, 21, 2, 6 }, "Local Prince") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 12: case 15: return "More demands..."; case 2: case 26: case 28: return "Take your god or whatever somewhere else."; case 9: case 22: return "These merchants only care about gold."; case 0: return "A surprise, but a welcome one."; case 21: return "At least they stay away from this."; case 6: if (!VanillaQuestState.IsHideoutQuestComplete()) { return "You're going to have a hard time on the return trip."; } return "Nice doing business with you."; default: return UnexpectedOrigin(mission); } } } internal sealed class HappyBayUnnamedGraveMission : AnonymousLetterMissionDefinition { internal static readonly HappyBayUnnamedGraveMission Instance = new HappyBayUnnamedGraveMission(); private HappyBayUnnamedGraveMission() : base("happy_bay_unnamed_grave", "Happy Bay", 20, new Vector3(-6791.194f, 55.928f, -5663.258f), new Vector3(0f, 355.344f, 0f), new Vector3(2.5f, 2.5f, 2.5f), 8844.2f, new LetterDeliveryWindow(5f, 7f, "05-07"), new int[1] { 2 }, "Unnamed grave") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { if (LetterMissionDefinition.GetOriginPortIndex(mission) != 2) { return UnexpectedOrigin(mission); } return "(You place the letter on the grave. It fades into light when sun raise up)"; } } internal sealed class KiciaBayWesternmostHouseMission : RegisteredLetterMissionDefinition { internal static readonly KiciaBayWesternmostHouseMission Instance = new KiciaBayWesternmostHouseMission(); private KiciaBayWesternmostHouseMission() : base("kicia_bay_westernmost_house", "Kicia Bay", 22, new Vector3(204.082f, 3.362f, -107.532f), new Vector3(0f, 74.864f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Night, new int[5] { 6, 9, 0, 18, 20 }, "Western most house") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { int originPortIndex = LetterMissionDefinition.GetOriginPortIndex(mission); if (!VanillaQuestState.IsHideoutQuestComplete()) { switch (originPortIndex) { case 0: case 6: return "I wish for a dryer air..."; case 9: case 18: case 20: return "Oranges...Always oranges..."; default: return UnexpectedOrigin(mission); } } switch (originPortIndex) { case 6: return "Aye...more work! Ye should be shipwreck in the fog!"; case 0: case 9: case 18: case 20: return "They just want me blue tobacco, feck off!"; default: return UnexpectedOrigin(mission); } } } internal static class Letter { internal const int LetterGoodIndex = 68; private const int QuestLetterItemIndex = 330; private const string LetterName = "Registered Letter"; private const string InheritedLabelObjectName = "label"; private const string LetterSealTextureFile = "registered_letter_seal.png"; private const string LetterSealObjectName = "postal_expansion_registered_letter_seal"; private const float LetterSealSize = 0.11f; private const float LetterSealSurfaceOffset = 0.002f; private static GameObject letterPrefab; private static readonly PostalSealVisual LetterSeal = new PostalSealVisual("postal_expansion_registered_letter_seal", "registered_letter_seal.png", "registered_letter_seal", 0.11f); internal static GameObject Prefab => letterPrefab; internal static bool EnsureRegistered(PrefabsDirectory directory) { if ((Object)(object)directory == (Object)null || directory.directory == null) { return false; } if (PostalPrefabRegistration.IsRegistered(directory, 238, letterPrefab)) { return true; } if (!HasPrefab(directory, 221)) { Debug.LogWarning((object)"Postal Expansion: regular mail prefab 221 was not found."); return false; } if (!HasPrefab(directory, 330)) { Debug.LogWarning((object)"Postal Expansion: vanilla quest letter prefab 330 was not found."); return false; } if (PostalPrefabRegistration.IsSlotOccupiedByAnotherPrefab(directory, 238, letterPrefab)) { Debug.LogError((object)"Postal Expansion: prefab index 238 is already occupied."); return false; } GameObject val = directory.directory[221]; GameObject questLetterPrefab = directory.directory[330]; if (!HasRequiredComponents(val, questLetterPrefab) || !PostalPrefabRegistration.EnsureCapacity(directory, 238)) { Debug.LogError((object)"Postal Expansion: Registered Letter prefab components could not be prepared."); return false; } if ((Object)(object)letterPrefab == (Object)null) { letterPrefab = Object.Instantiate(val); ((Object)letterPrefab).name = string.Format("{0} ({1}) {2}", 238, 68, "Registered Letter"); Object.DontDestroyOnLoad((Object)(object)letterPrefab); } ConfigurePrefab(letterPrefab, questLetterPrefab); if (!PostalPrefabRegistration.RegisterShipItem(directory, 238, letterPrefab)) { Debug.LogError((object)"Postal Expansion: Registered Letter prefab could not be registered as a ship item."); return false; } directory.directory[238] = letterPrefab; return true; } private static bool HasPrefab(PrefabsDirectory directory, int prefabIndex) { if (directory.directory.Length > prefabIndex) { return (Object)(object)directory.directory[prefabIndex] != (Object)null; } return false; } private static bool HasRequiredComponents(GameObject regularMailPrefab, GameObject questLetterPrefab) { if ((Object)(object)regularMailPrefab.GetComponent() != (Object)null && (Object)(object)regularMailPrefab.GetComponent() != (Object)null && (Object)(object)regularMailPrefab.GetComponent() != (Object)null && (Object)(object)regularMailPrefab.GetComponent() != (Object)null && (Object)(object)regularMailPrefab.GetComponent() != (Object)null && (Object)(object)regularMailPrefab.GetComponent() != (Object)null && (Object)(object)questLetterPrefab.GetComponent() != (Object)null && (Object)(object)questLetterPrefab.GetComponent() != (Object)null && (Object)(object)questLetterPrefab.GetComponent() != (Object)null) { return (Object)(object)questLetterPrefab.GetComponent() != (Object)null; } return false; } private static void ConfigurePrefab(GameObject prefab, GameObject questLetterPrefab) { //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010e: 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_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) prefab.SetActive(true); ((Object)prefab).name = string.Format("{0} ({1}) {2}", 238, 68, "Registered Letter"); prefab.GetComponent().prefabIndex = 238; ShipItem component = prefab.GetComponent(); ShipItem component2 = questLetterPrefab.GetComponent(); component.name = "Registered Letter"; component.mass = 1f; ((PickupableItem)component).big = ((PickupableItem)component2).big; ((PickupableItem)component).holdDistance = ((PickupableItem)component2).holdDistance; ((PickupableItem)component).holdHeight = ((PickupableItem)component2).holdHeight; ((PickupableItem)component).heldRotationOffset = ((PickupableItem)component2).heldRotationOffset; component.inventoryScale = component2.inventoryScale; component.inventoryRotation = component2.inventoryRotation; component.inventoryRotationX = component2.inventoryRotationX; prefab.GetComponent().requiredRepLevel = 7; MeshFilter component3 = questLetterPrefab.GetComponent(); MeshRenderer component4 = questLetterPrefab.GetComponent(); BoxCollider component5 = questLetterPrefab.GetComponent(); prefab.GetComponent().sharedMesh = component3.sharedMesh; ((Renderer)prefab.GetComponent()).sharedMaterials = ((Renderer)component4).sharedMaterials; BoxCollider component6 = prefab.GetComponent(); component6.center = component5.center; component6.size = component5.size; Transform val = prefab.transform.Find("label"); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)val).gameObject); } Vector3 center = component6.center; center.z += component6.size.z / 2f + 0.002f; LetterSeal.Ensure(prefab, center, Quaternion.identity); } } internal sealed class LetterDeliveryArea : MonoBehaviour { private Port port; private LetterMissionDefinition definition; private LetterDialoguePresenceArea dialogueArea; private Good waitingLetter; private bool closedNoticeShown; internal void Initialize(Port destinationPort, LetterMissionDefinition missionDefinition, LetterDialoguePresenceArea presenceArea) { port = destinationPort; definition = missionDefinition; dialogueArea = presenceArea; } private void OnTriggerEnter(Collider other) { Good componentInParent = ((Component)other).GetComponentInParent(); if (IsLetter(componentInParent)) { if (!TryGetMatchingMission(componentInParent, out var _)) { ShowNotification("Wrong Letter!"); return; } waitingLetter = componentInParent; closedNoticeShown = false; TryDeliver(); } } private void OnTriggerExit(Collider other) { if ((Object)(object)((Component)other).GetComponentInParent() == (Object)(object)waitingLetter) { waitingLetter = null; closedNoticeShown = false; } } private void Update() { if ((Object)(object)waitingLetter != (Object)null && LetterDeliveryHours.IsOpen(definition.DeliveryWindow)) { TryDeliver(); } } private void TryDeliver() { if ((Object)(object)waitingLetter == (Object)null || !TryGetMatchingMission(waitingLetter, out var mission)) { waitingLetter = null; } else if (!LetterDeliveryHours.IsOpen(definition.DeliveryWindow)) { if (!closedNoticeShown) { closedNoticeShown = true; ShowNotification("Wrong Time!"); } } else { Good obj = waitingLetter; waitingLetter = null; closedNoticeShown = false; string deliveryDialogue = definition.GetDeliveryDialogue(mission); LetterMissions.MissionDelivered(mission); obj.Deliver(); dialogueArea?.ShowForDelivery(deliveryDialogue); } } private bool TryGetMatchingMission(Good letter, out Mission mission) { mission = null; if ((Object)(object)letter == (Object)null || PlayerMissions.missions == null) { return false; } int missionIndex = letter.GetMissionIndex(); if (missionIndex < 0 || missionIndex >= PlayerMissions.missions.Length) { return false; } mission = PlayerMissions.missions[missionIndex]; if (mission != null && mission.missionIndex == missionIndex && (Object)(object)mission.destinationPort == (Object)(object)port && LetterMissions.TryGetDefinition(mission, out var letterMissionDefinition)) { return string.Equals(letterMissionDefinition.Id, definition.Id, StringComparison.Ordinal); } return false; } private static bool IsLetter(Good good) { return PostalMail.IsRegisteredLetter(((Object)(object)good != (Object)null) ? ((Component)good).GetComponent() : null); } private static void ShowNotification(string message) { if ((Object)(object)NotificationUi.instance != (Object)null) { NotificationUi.instance.ShowNotification(message); } } } [HarmonyPatch(typeof(PortDude), "Awake")] internal static class LetterDeliveryAreaCreationPatch { private const string AreaRootPrefix = "postal_expansion_letter_"; private static void Postfix(PortDude __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.GetPort() == (Object)null) { return; } foreach (LetterMissionDefinition item in LetterMissionDefinitions.All) { if (item.DestinationPortIndex == __instance.GetPort().portIndex) { EnsureArea(__instance, item); } } } private static void EnsureArea(PortDude portDude, LetterMissionDefinition definition) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_00a6: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) string text = "postal_expansion_letter_" + definition.Id; Transform areaParent = GetAreaParent(portDude, definition); if (!((Object)(object)areaParent == (Object)null) && !((Object)(object)areaParent.Find(text) != (Object)null)) { GameObject val = new GameObject(text); if (definition.UsePersistentWorldAnchor) { val.transform.position = ((Component)portDude).transform.TransformPoint(definition.LocalPosition); val.transform.rotation = ((Component)portDude).transform.rotation * Quaternion.Euler(definition.LocalEulerAngles); val.transform.SetParent(areaParent, true); } else { val.transform.SetParent(areaParent, false); val.transform.localPosition = definition.LocalPosition; val.transform.localRotation = Quaternion.Euler(definition.LocalEulerAngles); } LetterDialoguePresenceArea dialogueArea = CreateDialogueArea(val, definition); CreateDeliveryArea(val, portDude.GetPort(), definition, dialogueArea); if (definition.UsePersistentWorldAnchor) { val.AddComponent().Initialize(((Component)portDude).transform, definition.LocalPosition, definition.LocalEulerAngles); } } } private static Transform GetAreaParent(PortDude portDude, LetterMissionDefinition definition) { if (!definition.UsePersistentWorldAnchor) { return ((Component)portDude).transform; } if ((Object)(object)Refs.shiftingWorld != (Object)null) { return Refs.shiftingWorld; } FloatingOriginManager val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null) { return ((Component)val).transform; } Debug.LogWarning((object)("Postal Expansion: Cannot create persistent delivery area " + definition.Id + "; shifting world is unavailable.")); return null; } internal static bool TryGetAreaTransform(LetterMissionDefinition definition, out Transform areaTransform) { areaTransform = null; if (definition == null) { return false; } Transform val = null; if (definition.UsePersistentWorldAnchor) { val = Refs.shiftingWorld; } else if (Port.ports != null && definition.DestinationPortIndex >= 0 && definition.DestinationPortIndex < Port.ports.Length) { Port val2 = Port.ports[definition.DestinationPortIndex]; PortDude val3 = (((Object)(object)val2 != (Object)null) ? val2.GetDude() : null); val = (((Object)(object)val3 != (Object)null) ? ((Component)val3).transform : null); } areaTransform = (((Object)(object)val != (Object)null) ? val.Find("postal_expansion_letter_" + definition.Id) : null); return (Object)(object)areaTransform != (Object)null; } private static LetterDialoguePresenceArea CreateDialogueArea(GameObject root, LetterMissionDefinition definition) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown GameObject val = new GameObject("postal_expansion_letter_dialogue_" + definition.Id) { layer = 2 }; val.transform.SetParent(root.transform, false); BoxCollider obj = val.AddComponent(); ((Collider)obj).isTrigger = true; obj.size = definition.TriggerSize * 5f; AddTriggerRigidbody(val); LetterDialoguePresenceArea letterDialoguePresenceArea = val.AddComponent(); letterDialoguePresenceArea.Initialize(definition); return letterDialoguePresenceArea; } private static void CreateDeliveryArea(GameObject root, Port port, LetterMissionDefinition definition, LetterDialoguePresenceArea dialogueArea) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown GameObject val = new GameObject("postal_expansion_letter_delivery_" + definition.Id) { layer = 2 }; val.transform.SetParent(root.transform, false); BoxCollider obj = val.AddComponent(); ((Collider)obj).isTrigger = true; obj.size = definition.TriggerSize; AddTriggerRigidbody(val); val.AddComponent().Initialize(port, definition, dialogueArea); } private static void AddTriggerRigidbody(GameObject areaObject) { Rigidbody obj = areaObject.AddComponent(); obj.isKinematic = true; obj.useGravity = false; } } internal sealed class LetterDeliveryDialogue { private const string BubbleGraphicsName = "gfx"; private const string BubbleReferenceText = "Share a drink with a fellow sailor?"; private const int DialogueWrapCharacterLimit = 40; private const float ObserverOffset = 0.75f; private const float HeadHeightOffset = 0.5f; private const float MinimumBubbleWidthMultiplier = 0.35f; private const float MaximumBubbleWidthMultiplier = 1.8f; private static bool missingBubbleGraphicsWarningLogged; private readonly Transform anchor; private readonly int portIndex; private bool missingTemplateWarningLogged; private GameObject panel; private TextMesh text; private Transform bubbleGraphics; private Vector3 baseBubbleScale; private float baseTextWidth; internal LetterDeliveryDialogue(Transform dialogueAnchor, int destinationPortIndex) { anchor = dialogueAnchor; portIndex = destinationPortIndex; } internal bool Show(string dialogueText) { if (!EnsureCreated()) { return false; } string dialogueText2 = Wrap(dialogueText, 40); text.text = dialogueText2; ResizeBubble(dialogueText2); PositionTowardObserver(); panel.SetActive(true); FaceObserver(); return true; } internal void Hide() { if ((Object)(object)panel != (Object)null) { panel.SetActive(false); } } internal void FaceObserver() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)panel == (Object)null) && panel.activeInHierarchy && !((Object)(object)Refs.observerMirror == (Object)null)) { PositionTowardObserver(); panel.transform.rotation = ((Component)Refs.observerMirror).transform.rotation; } } private void PositionTowardObserver() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)panel == (Object)null) && !((Object)(object)Refs.observerMirror == (Object)null)) { Vector3 val = ((Component)Refs.observerMirror).transform.position - anchor.position; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = -anchor.forward; } else { ((Vector3)(ref val)).Normalize(); } Vector3 position = anchor.position + val * 0.75f; position.y = ((Component)Refs.observerMirror).transform.position.y + 0.5f; panel.transform.position = position; } } private bool EnsureCreated() { if ((Object)(object)panel != (Object)null && (Object)(object)text != (Object)null) { return true; } panel = LetterDialogueTemplate.Create(anchor, out text); if ((Object)(object)panel == (Object)null || (Object)(object)text == (Object)null) { if (!missingTemplateWarningLogged) { missingTemplateWarningLogged = true; int num = portIndex; Debug.LogWarning((object)("Postal Expansion: the letter dialogue template is unavailable at port " + num + ".")); } return false; } InitializeBubbleLayout(); panel.SetActive(false); return true; } private void InitializeBubbleLayout() { //IL_008d: 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) Transform val = (((Object)(object)text != (Object)null) ? ((Component)text).transform.parent : null); bubbleGraphics = (((Object)(object)val != (Object)null) ? val.Find("gfx") : null); if ((Object)(object)bubbleGraphics == (Object)null || (Object)(object)text == (Object)null || (Object)(object)text.font == (Object)null) { if (!missingBubbleGraphicsWarningLogged) { missingBubbleGraphicsWarningLogged = true; Debug.LogWarning((object)"Postal Expansion: letter dialogue bubble graphics could not be found; dynamic sizing is disabled."); } } else { baseBubbleScale = bubbleGraphics.localScale; baseTextWidth = MeasureMaximumLineWidth("Share a drink with a fellow sailor?"); } } private void ResizeBubble(string dialogueText) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)bubbleGraphics == (Object)null) && !((Object)(object)text == (Object)null) && !((Object)(object)text.font == (Object)null) && !(baseTextWidth <= 0f)) { bubbleGraphics.localScale = baseBubbleScale; float num = MeasureMaximumLineWidth(dialogueText); if (!(num <= 0f)) { float num2 = Mathf.Clamp(num / baseTextWidth, 0.35f, 1.8f); Vector3 localScale = baseBubbleScale; localScale.x *= num2; bubbleGraphics.localScale = localScale; } } } private float MeasureMaximumLineWidth(string value) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) text.font.RequestCharactersInTexture(value, text.fontSize, text.fontStyle); float num = 0f; float num2 = 0f; CharacterInfo val = default(CharacterInfo); foreach (char c in value) { if (c == '\n') { num2 = Mathf.Max(num2, num); num = 0f; } else if (text.font.GetCharacterInfo(c, ref val, text.fontSize, text.fontStyle)) { num += (float)((CharacterInfo)(ref val)).advance; } } return Mathf.Max(num2, num); } private static string Wrap(string value, int size) { value = value.TrimStart(Array.Empty()); if (value.Length <= size) { return value; } int num = value.LastIndexOf(' ', size); if (num == -1) { num = Mathf.Min(value.Length, size); } return value.Substring(0, num) + ((num >= value.Length) ? string.Empty : ("\n" + Wrap(value.Substring(num), size))); } } internal readonly struct LetterDeliveryWindow { internal static readonly LetterDeliveryWindow Day = new LetterDeliveryWindow(7f, 18f, "07-18"); internal static readonly LetterDeliveryWindow Night = new LetterDeliveryWindow(19f, 5f, "19 - 05"); internal float StartHour { get; } internal float EndHour { get; } internal string DisplayHours { get; } internal LetterDeliveryWindow(float startHour, float endHour, string displayHours) { StartHour = startHour; EndHour = endHour; DisplayHours = displayHours; } } internal static class LetterDeliveryHours { internal static bool IsOpen(LetterDeliveryWindow window) { if ((Object)(object)Sun.sun == (Object)null) { return false; } float localTime = Sun.sun.localTime; if (!(window.StartHour <= window.EndHour)) { if (!(localTime >= window.StartHour)) { return localTime <= window.EndHour; } return true; } if (localTime >= window.StartHour) { return localTime <= window.EndHour; } return false; } internal static string GetDisplayHours(LetterDeliveryWindow window) { return window.DisplayHours; } } internal sealed class LetterDialoguePresenceArea : MonoBehaviour { private const float DialogueRetryInterval = 0.5f; private readonly HashSet playerColliders = new HashSet(); private LetterDeliveryDialogue dialogue; private string pendingText; private bool shown; private bool consumed; private float nextDialogueRetryTime; internal void Initialize(LetterMissionDefinition missionDefinition) { dialogue = new LetterDeliveryDialogue(((Component)this).transform, missionDefinition.DestinationPortIndex); } internal void ShowForDelivery(string text) { if (!string.IsNullOrEmpty(text) && !shown && pendingText == null) { consumed = false; pendingText = text; nextDialogueRetryTime = 0f; TryShow(); } } private void OnTriggerEnter(Collider other) { if (((Component)other).CompareTag("Player")) { playerColliders.Add(other); TryShow(); } } private void OnTriggerExit(Collider other) { if (!((Component)other).CompareTag("Player")) { return; } playerColliders.Remove(other); if (playerColliders.Count == 0) { if (shown) { dialogue.Hide(); } shown = false; consumed = true; pendingText = null; nextDialogueRetryTime = 0f; } } private void Update() { if (shown) { dialogue.FaceObserver(); } else if (pendingText != null && playerColliders.Count > 0 && Time.time >= nextDialogueRetryTime) { nextDialogueRetryTime = Time.time + 0.5f; TryShow(); } } private void TryShow() { if (!shown && !consumed && pendingText != null && playerColliders.Count != 0) { shown = dialogue.Show(pendingText); } } } internal static class LetterDialogueTemplate { private const string TemplateObjectName = "postal_expansion_letter_dialogue_template"; private static GameObject template; private static string textRelativePath; internal static bool TryCapture(TavernRumorsDude source) { if ((Object)(object)template != (Object)null) { return true; } if ((Object)(object)source == (Object)null || (Object)(object)source.speechUI == (Object)null || (Object)(object)source.text == (Object)null) { return false; } string relativePath = GetRelativePath(source.speechUI.transform, ((Component)source.text).transform); if (relativePath == null) { return false; } GameObject val = Object.Instantiate(source.speechUI); ((Object)val).name = "postal_expansion_letter_dialogue_template"; val.transform.SetParent((Transform)null, true); if ((Object)(object)source.drinkButton != (Object)null) { string relativePath2 = GetRelativePath(source.speechUI.transform, source.drinkButton.transform); Transform val2 = FindRelativeTransform(val.transform, relativePath2); if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)val2).gameObject); } } val.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val); template = val; textRelativePath = relativePath; return true; } internal static GameObject Create(Transform parent, out TextMesh dialogueText) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) dialogueText = null; if ((Object)(object)parent == (Object)null || (!EnsureCaptured() && (Object)(object)template == (Object)null)) { return null; } GameObject val = Object.Instantiate(template); ((Object)val).name = "postal_expansion_letter_dialogue"; val.transform.SetParent(parent, false); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; Transform val2 = FindRelativeTransform(val.transform, textRelativePath); dialogueText = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent() : null); val.SetActive(false); if ((Object)(object)dialogueText != (Object)null) { return val; } Object.Destroy((Object)(object)val); return null; } internal static void Dispose() { if ((Object)(object)template != (Object)null) { Object.Destroy((Object)(object)template); } template = null; textRelativePath = null; } private static bool EnsureCaptured() { if ((Object)(object)template != (Object)null) { return true; } TavernRumorsDude[] array = Object.FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { if (TryCapture(array[i])) { return true; } } array = Resources.FindObjectsOfTypeAll(); for (int i = 0; i < array.Length; i++) { if (TryCapture(array[i])) { return true; } } return false; } private static Transform FindRelativeTransform(Transform root, string relativePath) { if ((Object)(object)root == (Object)null || relativePath == null) { return null; } if (relativePath.Length != 0) { return root.Find(relativePath); } return root; } private static string GetRelativePath(Transform root, Transform child) { if ((Object)(object)root == (Object)null || (Object)(object)child == (Object)null) { return null; } List list = new List(); Transform val = child; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { list.Add(((Object)val).name); val = val.parent; } if ((Object)(object)val != (Object)(object)root) { return null; } list.Reverse(); return string.Join("/", list.ToArray()); } } [HarmonyPatch(typeof(TavernRumorsDude), "Awake")] internal static class LetterDialogueTemplateCapturePatch { private static void Postfix(TavernRumorsDude __instance) { LetterDialogueTemplate.TryCapture(__instance); } } internal static class LetterMissionDebug { private const int EntriesPerPage = 5; private static int page; internal static void ShowSpawnLocations() { List list = new List(); list.AddRange(RegisteredLetterMissions.GetDebugStatusLines()); list.AddRange(AnonymousLetterMissions.GetDebugStatusLines()); int num = Mathf.Max(1, Mathf.CeilToInt((float)list.Count / 5f)); page = Mathf.Clamp(page, 0, num - 1); int num2 = page * 5; int num3 = Mathf.Min(5, list.Count - num2); List list2 = new List(); list2.Add("Letter mission spawns (" + (page + 1) + "/" + num + "):"); List list3 = list2; for (int i = 0; i < num3; i++) { list3.Add(list[num2 + i]); } Debug.Log((object)("Postal Expansion: letter mission spawns: | " + string.Join(" | ", list))); if ((Object)(object)NotificationUi.instance != (Object)null) { NotificationUi.instance.ShowNotification(string.Join("\n", list3)); } page = (page + 1) % num; } internal static void Reset() { page = 0; } } internal abstract class LetterMissionDefinition { internal string Id { get; } internal string DestinationPortName { get; } internal int DestinationPortIndex { get; } internal Vector3 LocalPosition { get; } internal Vector3 LocalEulerAngles { get; } internal Vector3 TriggerSize { get; } internal LetterDeliveryWindow DeliveryWindow { get; } internal int[] SpawnPortIndices { get; } internal string LocationDescription { get; } internal virtual float DistanceToOffice { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Vector2 val = new Vector2(LocalPosition.x, LocalPosition.z); return ((Vector2)(ref val)).magnitude; } } internal virtual bool UsePersistentWorldAnchor => false; internal virtual bool UseDeliveryLocationForRoute => false; internal virtual bool ShowDeliveryCoordinates => false; internal virtual bool HideDestination => false; protected LetterMissionDefinition(string id, string destinationPortName, int destinationPortIndex, Vector3 localPosition, Vector3 localEulerAngles, Vector3 triggerSize, LetterDeliveryWindow deliveryWindow, int[] spawnPortIndices, string locationDescription) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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) Id = id; DestinationPortName = destinationPortName; DestinationPortIndex = destinationPortIndex; LocalPosition = localPosition; LocalEulerAngles = localEulerAngles; TriggerSize = triggerSize; DeliveryWindow = deliveryWindow; SpawnPortIndices = spawnPortIndices; LocationDescription = locationDescription; } internal abstract string GetDeliveryDialogue(Mission mission); protected static int GetOriginPortIndex(Mission mission) { return mission?.originPort?.portIndex ?? (-1); } protected string UnexpectedOrigin(Mission mission) { Port val = mission?.originPort; string text = (((Object)(object)val != (Object)null) ? (val.GetPortName() + " (" + val.portIndex + ")") : "missing origin"); Debug.LogWarning((object)("Postal Expansion: letter mission " + Id + " received unexpected origin " + text + ".")); return string.Empty; } } [HarmonyPatch(typeof(MissionDetailsUI), "InsertMissionData")] internal static class LetterMissionDetailsPatch { private const float LocationLabelPosition = 0.45f; private const float LocationValuePosition = 0.8f; private const float DeliveryHoursValueOffset = 0.5f; private const string LocationLabelObjectName = "postal_expansion_letter_location_label"; private const string LocationValueObjectName = "postal_expansion_letter_location_value"; private const string DeliveryHoursLabelObjectName = "postal_expansion_letter_delivery_hours_label"; private const string DeliveryHoursValueObjectName = "postal_expansion_letter_delivery_hours_value"; private static readonly FieldInfo LocationTextField = AccessTools.Field(typeof(MissionDetailsUI), "locationText"); private static readonly FieldInfo DestinationTextField = AccessTools.Field(typeof(MissionDetailsUI), "destination"); private static readonly FieldInfo DueTextField = AccessTools.Field(typeof(MissionDetailsUI), "due"); private static readonly FieldInfo DistanceTextField = AccessTools.Field(typeof(MissionDetailsUI), "distance"); private static readonly FieldInfo TotalGoldTextField = AccessTools.Field(typeof(MissionDetailsUI), "totalGold"); private static readonly FieldInfo MapRendererField = AccessTools.Field(typeof(MissionDetailsUI), "mapRenderer"); private static void Postfix(MissionDetailsUI __instance, Mission mission) { //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || LocationTextField == null || DestinationTextField == null || DueTextField == null || DistanceTextField == null || TotalGoldTextField == null) { return; } object? value = DestinationTextField.GetValue(__instance); TextMesh val = (TextMesh)((value is TextMesh) ? value : null); object? value2 = DueTextField.GetValue(__instance); TextMesh val2 = (TextMesh)((value2 is TextMesh) ? value2 : null); object? value3 = DistanceTextField.GetValue(__instance); TextMesh val3 = (TextMesh)((value3 is TextMesh) ? value3 : null); object? value4 = TotalGoldTextField.GetValue(__instance); TextMesh val4 = (TextMesh)((value4 is TextMesh) ? value4 : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null) { return; } Transform parent = ((Component)val).transform.parent; Transform parent2 = ((Component)val3).transform.parent; TextMesh val5 = FindText(parent, "postal_expansion_letter_location_label"); TextMesh val6 = FindText(parent, "postal_expansion_letter_location_value"); TextMesh val7 = FindText(parent2, "postal_expansion_letter_delivery_hours_label"); TextMesh val8 = FindText(parent2, "postal_expansion_letter_delivery_hours_value"); TextMesh val9 = FindTextByContent(parent, "Destination:"); TextMesh val10 = FindTextStartingWith(parent, "Cargo:"); if (!LetterMissions.TryGetDefinition(mission, out var definition)) { SetActive(val9, active: true); SetActive(val, active: true); SetActive(val5, active: false); SetActive(val6, active: false); SetActive(val7, active: false); SetActive(val8, active: false); return; } val4.text = LetterMissions.GetFixedGoldReward(mission) + " " + PlayerGold.GetCurrencyName(3); if ((Object)(object)val9 == (Object)null || (Object)(object)val10 == (Object)null) { Debug.LogWarning((object)"Postal Expansion: mission detail labels were not found."); return; } val5 = val5 ?? CloneText(val9, "postal_expansion_letter_location_label"); val6 = val6 ?? CloneText(val, "postal_expansion_letter_location_value"); if ((Object)(object)val5 == (Object)null || (Object)(object)val6 == (Object)null) { return; } if (definition.HideDestination) { ((Component)val5).transform.localPosition = ((Component)val9).transform.localPosition; ((Component)val6).transform.localPosition = ((Component)val).transform.localPosition; } else { float y = ((Component)val).transform.localPosition.y; float y2 = ((Component)val10).transform.localPosition.y; SetLocalY(((Component)val5).transform, Mathf.Lerp(y, y2, 0.45f)); SetLocalY(((Component)val6).transform, Mathf.Lerp(y, y2, 0.8f)); } val5.text = "Location:"; val6.text = definition.LocationDescription; SetActive(val9, !definition.HideDestination); SetActive(val, !definition.HideDestination); SetActive(val5, active: true); SetActive(val6, active: true); val7 = val7 ?? CloneText(val3, "postal_expansion_letter_delivery_hours_label"); val8 = val8 ?? CloneText(val2, "postal_expansion_letter_delivery_hours_value"); if ((Object)(object)val7 != (Object)null && (Object)(object)val8 != (Object)null) { float y3 = Mathf.Lerp(((Component)val2).transform.localPosition.y, ((Component)val3).transform.localPosition.y, 0.5f); SetLocalY(((Component)val7).transform, y3); SetLocalY(((Component)val8).transform, y3); val7.text = "Delivery hours:"; val8.text = LetterDeliveryHours.GetDisplayHours(definition.DeliveryWindow); SetActive(val7, active: true); SetActive(val8, active: true); PositionDeliveryHoursValue(val7, val8); } object? value5 = LocationTextField.GetValue(__instance); TextMesh val11 = (TextMesh)((value5 is TextMesh) ? value5 : null); object? obj = MapRendererField?.GetValue(__instance); Renderer val12 = (Renderer)((obj is Renderer) ? obj : null); if (definition.ShowDeliveryCoordinates) { if ((Object)(object)val12 != (Object)null) { ((Component)val12).gameObject.SetActive(false); } if ((Object)(object)val11 != (Object)null) { val11.text = (((Object)(object)FloatingOriginManager.instance != (Object)null && LetterDeliveryAreaCreationPatch.TryGetAreaTransform(definition, out var areaTransform)) ? GetVanillaLocationText(areaTransform) : "approximate location:\nunavailable"); } } else if ((Object)(object)val11 != (Object)null) { val11.text = string.Empty; } } private static string GetVanillaLocationText(Transform deliveryArea) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) Vector3 globeCoords = FloatingOriginManager.instance.GetGlobeCoords(deliveryArea); int num = Mathf.RoundToInt(globeCoords.x); int num2 = Mathf.RoundToInt(globeCoords.z); string text = ((num < 0) ? "W" : "E"); string text2 = ((num2 < 0) ? "S" : "N"); return "approximate location:\n" + num2 + " " + text2 + ", " + num + " " + text; } private static TextMesh CloneText(TextMesh source, string objectName) { if ((Object)(object)source == (Object)null || (Object)(object)((Component)source).transform.parent == (Object)null) { return null; } GameObject obj = Object.Instantiate(((Component)source).gameObject, ((Component)source).transform.parent); ((Object)obj).name = objectName; return obj.GetComponent(); } private static TextMesh FindText(Transform root, string objectName) { Transform val = (((Object)(object)root != (Object)null) ? root.Find(objectName) : null); if (!((Object)(object)val != (Object)null)) { return null; } return ((Component)val).GetComponent(); } private static TextMesh FindTextByContent(Transform root, string content) { return FindText(root, (string text) => string.Equals(text.Trim(), content, StringComparison.Ordinal)); } private static TextMesh FindTextStartingWith(Transform root, string content) { return FindText(root, (string text) => text.TrimStart(Array.Empty()).StartsWith(content, StringComparison.Ordinal)); } private static TextMesh FindText(Transform root, Func predicate) { if ((Object)(object)root == (Object)null) { return null; } TextMesh[] componentsInChildren = ((Component)root).GetComponentsInChildren(true); foreach (TextMesh val in componentsInChildren) { if ((Object)(object)val != (Object)null && predicate(val.text ?? string.Empty)) { return val; } } return null; } private static void SetLocalY(Transform target, float y) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Vector3 localPosition = target.localPosition; localPosition.y = y; target.localPosition = localPosition; } private static void PositionDeliveryHoursValue(TextMesh label, TextMesh value) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)label).transform.localRotation * Vector3.right; Vector3 localPosition = ((Component)label).transform.localPosition + ((Vector3)(ref val)).normalized * 0.5f; localPosition.y = ((Component)value).transform.localPosition.y; localPosition.z = ((Component)value).transform.localPosition.z; ((Component)value).transform.localPosition = localPosition; } private static void SetActive(TextMesh text, bool active) { if ((Object)(object)text != (Object)null) { ((Component)text).gameObject.SetActive(active); } } } internal sealed class LetterMissionRegistry where TDefinition : LetterMissionDefinition { private readonly List definitions = new List(); internal IReadOnlyList All => definitions; internal LetterMissionRegistry(string categoryName, params TDefinition[] candidates) { HashSet hashSet = new HashSet(StringComparer.Ordinal); foreach (TDefinition val in candidates) { if (val == null || string.IsNullOrEmpty(val.Id)) { Debug.LogError((object)("Postal Expansion: ignored a " + categoryName + " mission without an ID.")); } else if (!hashSet.Add(val.Id)) { Debug.LogError((object)("Postal Expansion: ignored duplicate " + categoryName + " mission ID " + val.Id + ".")); } else { definitions.Add(val); } } } } internal static class LetterMissionDefinitions { private static readonly IReadOnlyList Definitions = BuildDefinitions(); internal static IReadOnlyList All => Definitions; private static IReadOnlyList BuildDefinitions() { List list = new List(); foreach (RegisteredLetterMissionDefinition item in RegisteredLetterMissionRegistry.All) { list.Add(item); } foreach (AnonymousLetterMissionDefinition item2 in AnonymousLetterMissionRegistry.All) { list.Add(item2); } return list.AsReadOnly(); } } internal static class LetterMissionRoute { internal static float GetMissionDistance(Port origin, Port destination, LetterMissionDefinition definition) { float distance = Mission.GetDistance(origin, destination); if (!definition.UseDeliveryLocationForRoute || (Object)(object)destination == (Object)null || destination.portIndex != definition.DestinationPortIndex) { return distance; } return distance + definition.DistanceToOffice / 100f; } } internal static class LetterMissions { internal static bool TryGetDefinition(Mission mission, out LetterMissionDefinition definition) { if (AnonymousLetterMissions.TryGetDefinition(mission, out var definition2)) { definition = definition2; return true; } if (RegisteredLetterMissions.TryGetDefinition(mission, out var definition3)) { definition = definition3; return true; } definition = null; return false; } internal static void MissionDelivered(Mission mission) { AnonymousLetterMissions.MissionDelivered(mission); RegisteredLetterMissions.MissionDelivered(mission); } internal static int GetFixedGoldReward(Mission mission) { if (!AnonymousLetterMissions.TryGetDefinition(mission, out var _)) { return 1; } return 5; } internal static float GetReputationMultiplier(Mission mission) { if (!AnonymousLetterMissions.TryGetDefinition(mission, out var _)) { return 8f; } return 10f; } internal static int GetSeverelyLatePenalty(Mission mission) { if (!AnonymousLetterMissions.TryGetDefinition(mission, out var _)) { return -5000; } return -8000; } } internal readonly struct LetterMissionSaveKeys { internal string ActiveMissions { get; } internal string ClaimedOffers { get; } internal LetterMissionSaveKeys(string activeMissions, string claimedOffers) { ActiveMissions = activeMissions; ClaimedOffers = claimedOffers; } } internal sealed class LetterMissionSet where TDefinition : LetterMissionDefinition { private sealed class MissionData { internal TDefinition Definition { get; } internal MissionData(TDefinition definition) { Definition = definition; } } private readonly string categoryName; private readonly IReadOnlyList definitions; private readonly Dictionary definitionsById; private readonly LetterMissionSaveKeys saveKeys; private readonly int requiredReputation; private readonly float dueDayBaseSpeed; private readonly int fixedGoldReward; private readonly Func getMissionName; private ConditionalWeakTable missionData = new ConditionalWeakTable(); private readonly Dictionary dailyOffers = new Dictionary(StringComparer.Ordinal); private readonly HashSet claimedOffers = new HashSet(StringComparer.Ordinal); private int dailyOfferDay = int.MinValue; internal LetterMissionSet(string categoryName, IReadOnlyList definitions, LetterMissionSaveKeys saveKeys, int requiredReputation, float dueDayBaseSpeed, int fixedGoldReward, Func getMissionName) { this.categoryName = categoryName; this.definitions = definitions; this.saveKeys = saveKeys; this.requiredReputation = requiredReputation; this.dueDayBaseSpeed = dueDayBaseSpeed; this.fixedGoldReward = fixedGoldReward; this.getMissionName = getMissionName; definitionsById = new Dictionary(StringComparer.Ordinal); foreach (TDefinition definition in definitions) { definitionsById.Add(definition.Id, definition); } } internal void AddMissions(Port origin, List missions) { if ((Object)(object)origin == (Object)null || missions == null) { return; } EnsureDailyOffers(); foreach (TDefinition definition in definitions) { if (dailyOffers.TryGetValue(definition.Id, out var value) && value != null && !((Object)(object)value.originPort != (Object)(object)origin) && CanOfferFrom(origin, value.destinationPort) && value.missionIndex == -1 && !IsClaimed(definition.Id) && !HasActiveMission(definition.Id)) { missions.Add(value); } } } internal List GetDebugStatusLines() { EnsureDailyOffers(); List list = new List(); foreach (TDefinition definition in definitions) { Mission activeMission; Mission value; string text = ((!TryGetActiveMission(definition.Id, out activeMission)) ? ((!IsClaimed(definition.Id)) ? ((!dailyOffers.TryGetValue(definition.Id, out value) || value == null || value.missionIndex != -1 || !CanOfferFrom(value.originPort, value.destinationPort)) ? "not available today" : GetPortName(value.originPort)) : "already claimed today") : ("active from " + GetPortName(activeMission.originPort))); list.Add(categoryName + " | " + definition.LocationDescription + ": " + text); } return list; } internal bool TryGetDefinition(Mission mission, out TDefinition definition) { if (mission != null && missionData.TryGetValue(mission, out var value)) { definition = value.Definition; return true; } definition = null; return false; } internal void MissionAccepted(Mission mission) { if (TryGetDefinition(mission, out var definition) && mission.missionIndex >= 0 && PlayerMissions.missions != null && mission.missionIndex < PlayerMissions.missions.Length && PlayerMissions.missions[mission.missionIndex] == mission) { claimedOffers.Add(GetClaimKey(GameState.day, definition.Id)); } } internal void MissionDelivered(Mission mission) { if (TryGetDefinition(mission, out var definition)) { claimedOffers.Add(GetClaimKey(GameState.day, definition.Id)); } } internal void SavePersistentState() { if (GameState.modData == null) { GameState.modData = new Dictionary(); } List list = new List(); if (PlayerMissions.missions != null) { for (int i = 0; i < PlayerMissions.missions.Length; i++) { Mission val = PlayerMissions.missions[i]; if (TryGetDefinition(val, out var definition) && !((Object)(object)val.originPort == (Object)null) && !((Object)(object)val.destinationPort == (Object)null)) { list.Add(string.Join(",", i, val.originPort.portIndex, val.destinationPort.portIndex, definition.Id)); } } } GameState.modData[saveKeys.ActiveMissions] = string.Join(";", list); PruneClaimsForCurrentDay(); List list2 = new List(claimedOffers); list2.Sort(StringComparer.Ordinal); GameState.modData[saveKeys.ClaimedOffers] = string.Join(";", list2); } internal void LoadPersistentState(params LetterMissionSaveKeys[] legacySaveKeys) { ResetRuntimeState(); if (GameState.modData != null) { LoadClaims(saveKeys); LoadActiveMissions(saveKeys); foreach (LetterMissionSaveKeys sourceKeys in legacySaveKeys) { LoadClaims(sourceKeys); LoadActiveMissions(sourceKeys); } } } internal void ResetRuntimeState() { missionData = new ConditionalWeakTable(); dailyOffers.Clear(); claimedOffers.Clear(); dailyOfferDay = int.MinValue; } private void LoadClaims(LetterMissionSaveKeys sourceKeys) { if (!GameState.modData.TryGetValue(sourceKeys.ClaimedOffers, out var value)) { return; } string text = GameState.day + ","; string[] array = value.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { if (text2.StartsWith(text, StringComparison.Ordinal)) { string key = text2.Substring(text.Length); if (definitionsById.ContainsKey(key)) { claimedOffers.Add(text2); } } } } private void LoadActiveMissions(LetterMissionSaveKeys sourceKeys) { if (PlayerMissions.missions == null || !GameState.modData.TryGetValue(sourceKeys.ActiveMissions, out var value)) { return; } string[] array = value.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ',' }); if (array2.Length == 4 && int.TryParse(array2[0], out var result) && int.TryParse(array2[1], out var result2) && int.TryParse(array2[2], out var result3) && result >= 0 && result < PlayerMissions.missions.Length && definitionsById.TryGetValue(array2[3], out var value2)) { Mission val = PlayerMissions.missions[result]; if (IsLetterMission(val) && val.missionIndex == result && (Object)(object)val.originPort != (Object)null && (Object)(object)val.destinationPort != (Object)null && val.originPort.portIndex == result2 && val.destinationPort.portIndex == result3 && value2.DestinationPortIndex == result3) { Attach(val, value2); claimedOffers.Add(GetClaimKey(GameState.day, value2.Id)); } } } } private void EnsureDailyOffers() { if (dailyOfferDay == GameState.day || Port.ports == null || (Object)(object)Letter.Prefab == (Object)null || (Object)(object)Sun.sun == (Object)null) { return; } dailyOffers.Clear(); dailyOfferDay = GameState.day; PruneClaimsForCurrentDay(); foreach (TDefinition definition in definitions) { if (IsClaimed(definition.Id) || HasActiveMission(definition.Id)) { continue; } Port port = GetPort(definition.DestinationPortIndex); if ((Object)(object)port == (Object)null) { continue; } List list = new List(); int[] spawnPortIndices = definition.SpawnPortIndices; for (int i = 0; i < spawnPortIndices.Length; i++) { Port port2 = GetPort(spawnPortIndices[i]); if (CanOfferFrom(port2, port)) { list.Add(port2); } } if (list.Count != 0) { ValidatePortName(definition, port); Random random = new Random(GetSelectionSeed(definition.Id)); Port origin = list[random.Next(list.Count)]; dailyOffers[definition.Id] = CreateMission(origin, port, definition); } } } private bool CanOfferFrom(Port origin, Port destination) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)origin == (Object)null || (Object)(object)destination == (Object)null || PlayerReputation.GetRepLevel(origin.region) < requiredReputation) { return false; } float distance = Mission.GetDistance(origin, destination); return PostalMail.IsWithinVanillaReputationRange(origin, distance); } private Mission CreateMission(Port origin, Port destination, TDefinition definition) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown Good component = Letter.Prefab.GetComponent(); float missionDistance = LetterMissionRoute.GetMissionDistance(origin, destination, definition); int num = (definition.UseDeliveryLocationForRoute ? MailDueDateCalculator.CalculateFromMissionDistance(missionDistance, component, dueDayBaseSpeed) : MailDueDateCalculator.Calculate(origin, destination, component, dueDayBaseSpeed)); Mission val = new Mission(origin, destination, Letter.Prefab, 1, fixedGoldReward, 1f, 0, num); val.missionName = getMissionName(destination); if (definition.UseDeliveryLocationForRoute) { val.distance = missionDistance; } val.pricePerKm = 0f; Attach(val, definition); return val; } private void Attach(Mission mission, TDefinition definition) { if (mission != null && definition != null && !TryGetDefinition(mission, out var _)) { missionData.Add(mission, new MissionData(definition)); ApplyDeliveryRoute(mission, definition); mission.totalPrice = fixedGoldReward; mission.pricePerKm = 0f; mission.missionName = getMissionName(mission.destinationPort); } } private void ApplyDeliveryRoute(Mission mission, TDefinition definition) { if (definition.UseDeliveryLocationForRoute) { float distance = Mission.GetDistance(mission.originPort, mission.destinationPort); float missionDistance = LetterMissionRoute.GetMissionDistance(mission.originPort, mission.destinationPort, definition); float num = Mathf.Abs(mission.distance - distance); float num2 = Mathf.Abs(mission.distance - missionDistance); if (num < num2) { Good component = mission.goodPrefab.GetComponent(); int num3 = MailDueDateCalculator.CalculateFromMissionDistance(distance, component, dueDayBaseSpeed); int num4 = MailDueDateCalculator.CalculateFromMissionDistance(missionDistance, component, dueDayBaseSpeed); mission.dueDay += num4 - num3; } mission.distance = missionDistance; } } private bool HasActiveMission(string missionId) { Mission activeMission; return TryGetActiveMission(missionId, out activeMission); } private bool TryGetActiveMission(string missionId, out Mission activeMission) { activeMission = null; if (PlayerMissions.missions == null) { return false; } Mission[] missions = PlayerMissions.missions; foreach (Mission val in missions) { if (TryGetDefinition(val, out var definition) && string.Equals(definition.Id, missionId, StringComparison.Ordinal)) { activeMission = val; return true; } } return false; } private static string GetPortName(Port port) { if (!((Object)(object)port != (Object)null)) { return "unknown port"; } return port.GetPortName(); } private static bool IsLetterMission(Mission mission) { if (mission == null || (Object)(object)mission.goodPrefab == (Object)null) { return false; } return PostalMail.IsRegisteredLetter(mission.goodPrefab.GetComponent()); } private static Port GetPort(int portIndex) { if (Port.ports == null || portIndex < 0 || portIndex >= Port.ports.Length) { return null; } return Port.ports[portIndex]; } private void ValidatePortName(TDefinition definition, Port destination) { if (!string.Equals(definition.DestinationPortName, destination.GetPortName(), StringComparison.Ordinal)) { Debug.LogWarning((object)("Postal Expansion: " + categoryName + " mission " + definition.Id + " expects port " + definition.DestinationPortName + " but index " + definition.DestinationPortIndex + " is " + destination.GetPortName() + ".")); } } private static int GetSelectionSeed(string missionId) { int num = 17; foreach (char c in missionId) { num = num * 31 + c; } return (GameState.day + 761) * 31 + num; } private bool IsClaimed(string missionId) { return claimedOffers.Contains(GetClaimKey(GameState.day, missionId)); } private static string GetClaimKey(int day, string missionId) { return day + "," + missionId; } private void PruneClaimsForCurrentDay() { string currentDayPrefix = GameState.day + ","; claimedOffers.RemoveWhere((string claim) => !claim.StartsWith(currentDayPrefix, StringComparison.Ordinal)); } } [HarmonyPatch(typeof(Mission), "DeliverGood")] internal static class LetterRewardCurrencyPatch { private static readonly FieldInfo DestinationPortField = AccessTools.Field(typeof(Mission), "destinationPort"); private static readonly FieldInfo PortRegionField = AccessTools.Field(typeof(Port), "region"); private static readonly MethodInfo MissionLogAddMethod = AccessTools.Method(typeof(MissionLog), "AddToLog", (Type[])null, (Type[])null); private static readonly MethodInfo ResolveCurrencyMethod = AccessTools.Method(typeof(LetterRewardCurrencyPatch), "ResolveCurrency", (Type[])null, (Type[])null); private static IEnumerable Transpiler(IEnumerable instructions) { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown List list = new List(instructions); List list2 = new List(); for (int i = 0; i < list.Count; i++) { if (LoadsDestinationRegion(list, i)) { int num = i + 2; bool num2 = num + 1 < list.Count && list[num + 1].opcode == OpCodes.Stloc_0; bool flag = num + 1 < list.Count && CodeInstructionExtensions.Calls(list[num + 1], MissionLogAddMethod); if (num2 || flag) { list2.Add(num + 1); } } } if (list2.Count != 2) { throw new InvalidOperationException("Could not locate Mission.DeliverGood's reward currency paths."); } for (int num3 = list2.Count - 1; num3 >= 0; num3--) { list.InsertRange(list2[num3], (IEnumerable)(object)new CodeInstruction[2] { new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Call, (object)ResolveCurrencyMethod) }); } return list; } private static bool LoadsDestinationRegion(IReadOnlyList code, int index) { if (index + 2 < code.Count && code[index].opcode == OpCodes.Ldarg_0 && CodeInstructionExtensions.LoadsField(code[index + 1], DestinationPortField, false)) { return CodeInstructionExtensions.LoadsField(code[index + 2], PortRegionField, false); } return false; } private static int ResolveCurrency(PortRegion destinationRegion, Mission mission) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected I4, but got Unknown if (!LetterMissions.TryGetDefinition(mission, out var _)) { return (int)destinationRegion; } return 3; } } [HarmonyPatch(typeof(Mission), "GetDeliveryPrice")] internal static class MailDeliveryPricePatch { private static bool Prefix(Mission __instance, ref int __result) { if (GoldenDeliveryMissions.IsGolden(__instance)) { int num = Mathf.Max(0, GameState.day - __instance.dueDay); __result = ((num == 0) ? (__instance.totalPrice / Mathf.Max(1, __instance.goodCount)) : 0); return false; } if (AnonymousLetterMissions.TryGetDefinition(__instance, out var _)) { int daysLate = Mathf.Max(0, GameState.day - __instance.dueDay); __result = AnonymousLetterMissions.GetGoldReward(daysLate); return false; } if (RegisteredLetterMissions.TryGetDefinition(__instance, out var _)) { __result = 1; return false; } if (!UrgentExpressMail.IsUrgent(__instance)) { return true; } float num2 = Mathf.Max(0, GameState.day - __instance.dueDay) switch { 0 => 1f, 1 => 0.5f, _ => 0.05f, }; int num3 = __instance.totalPrice / __instance.goodCount; __result = Mathf.RoundToInt((float)num3 * num2); return false; } } [HarmonyPatch(typeof(Mission), "GetDeliveryRep")] internal static class MailDeliveryRepPatch { private const int SeverelyLateExpressMissionPenalty = -100; private const int SeverelyLateUrgentMissionPenalty = -200; private static bool Prefix(Mission __instance, ref int __result) { if (__instance == null || (Object)(object)__instance.goodPrefab == (Object)null) { return true; } SaveablePrefab component = __instance.goodPrefab.GetComponent(); if ((Object)(object)component == (Object)null) { return true; } if (GoldenDeliveryMissions.IsGolden(__instance)) { int num = Mathf.Max(0, GameState.day - __instance.dueDay); if (num >= 2) { __result = -50000; } else if (num == 1) { __result = 0; } else { __result = Mathf.RoundToInt(__instance.distance / (float)Mathf.Max(1, __instance.goodCount) * Plugin.RegularMailReputationMultiplier.Value * 15f); } return false; } if (PostalMail.IsRegisteredLetter(component)) { int num2 = Mathf.Max(0, GameState.day - __instance.dueDay); if (num2 >= 2) { __result = LetterMissions.GetSeverelyLatePenalty(__instance); } else if (num2 == 1) { __result = 0; } else { __result = Mathf.RoundToInt(__instance.distance / (float)__instance.goodCount * Plugin.RegularMailReputationMultiplier.Value * LetterMissions.GetReputationMultiplier(__instance)); } return false; } bool flag = PostalMail.IsExpressMail(component); bool flag2 = flag && UrgentExpressMail.IsUrgent(__instance); float num3 = Plugin.RegularMailReputationMultiplier.Value; if (flag) { num3 *= PostalExpressMail.GetExpressRewardMultiplier(__instance.destinationPort); if (flag2) { num3 *= 2f; } } else if (component.prefabIndex != 221) { return true; } int num4 = Mathf.Max(0, GameState.day - __instance.dueDay); float num5 = __instance.distance / (float)__instance.goodCount * num3; if (!flag) { float num6 = Mathf.Min((float)num4 * 0.2f, 1f); __result = Mathf.RoundToInt(num5 - num5 * num6); return false; } if (flag2) { if (num4 >= 2) { __result = -200; } else if (num4 == 1) { __result = 0; } else { __result = Mathf.RoundToInt(num5); } return false; } if (num4 >= 3) { __result = -100; return false; } float num7 = Mathf.Min((float)num4 * 0.5f, 1f); __result = Mathf.RoundToInt(num5 - num5 * num7); return false; } } internal static class MailDueDateCalculator { internal static int Calculate(Port origin, Port destination, Good mailGood, float baseSpeed) { return CalculateFromMissionDistance(Mission.GetDistance(origin, destination), mailGood, baseSpeed); } internal static int CalculateFromMissionDistance(float missionDistance, Good mailGood, float baseSpeed) { float num = missionDistance / 10f; float num2 = baseSpeed * (Sun.sun.GetRealtimeDayLength() / 60f / 60f); if (mailGood.requiredRepLevel > 1) { num2 *= 3.5f; } num2 *= ((mailGood.requiredRepLevel == 1) ? 2.5f : 1.5f); int num3 = Mathf.Max(1, Mathf.RoundToInt(num / num2)); return GameState.day + num3; } } internal sealed class MirageMountainMonasteryMission : RegisteredLetterMissionDefinition { internal static readonly MirageMountainMonasteryMission Instance = new MirageMountainMonasteryMission(); private MirageMountainMonasteryMission() : base("mirage_mountain_monastery", "Mirage Mountain", 32, new Vector3(36.812f, 3.241f, -3.945f), new Vector3(0f, 175.062f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[4] { 2, 0, 5, 24 }, "monastary") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 2: case 5: return "Sabbih Ash-shams!"; case 0: return "We appreciate Sultan's relief effort."; case 24: return "We place faith in our Academy scholars, instead of this .... primitive practice."; default: return UnexpectedOrigin(mission); } } } [HarmonyPatch(typeof(MissionDetailsUI), "ClickButton")] internal static class MissionDetailsUiClickButtonPatch { private static bool Prefix(MissionDetailsUI __instance) { return !PostalMissionUi.TryHandlePortMissionAcceptance(__instance); } } [HarmonyPatch(typeof(MissionListUI), "ChangePage")] internal static class MissionListUiChangePagePatch { private static bool Prefix(MissionListUI __instance, int pageChange) { return !PostalMissionUi.TryChangePage(__instance, pageChange); } } [HarmonyPatch(typeof(MissionListUI), "DisplayMissions")] internal static class MissionListUiDisplayMissionsPatch { private static void Postfix(MissionListUI __instance) { PostalMissionUi.UpdateMissionHighlights(__instance); } } [HarmonyPatch(typeof(MissionListUI), "EnablePortMissionUI")] internal static class MissionListUiEnablePortPatch { private static void Prefix(MissionListUI __instance, ref Mission[] missions, PortDude dude) { PostalMissionUi.FilterInitialPortMissions(__instance, dude, ref missions); } private static void Postfix(MissionListUI __instance) { PostalMissionUi.EnsureMailButton(__instance); PostalMissionUi.RefreshPageCount(__instance); } } [HarmonyPatch(typeof(MissionListUI), "ResetPage")] internal static class MissionListUiResetPagePatch { private static bool Prefix(MissionListUI __instance) { return !PostalMissionUi.TryResetPage(__instance); } } [HarmonyPatch(typeof(MissionListUI), "SetWorldMissions")] internal static class MissionListUiSetWorldPatch { private static void Postfix(MissionListUI __instance) { if (GameState.inPortMissionList) { PostalMissionUi.SetMailMissions(mail: false); } } } internal sealed class MountMaleficWheatManorMission : RegisteredLetterMissionDefinition { internal static readonly MountMaleficWheatManorMission Instance = new MountMaleficWheatManorMission(); private MountMaleficWheatManorMission() : base("mount_malefic_wheat_manor", "Mount Malefic", 17, new Vector3(-105.357f, 0.931f, 172.698f), new Vector3(0f, 201.481f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[7] { 15, 19, 28, 20, 9, 12, 22 }, "Wheat manor") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 15: case 19: case 28: return "I'll arrange next shipment."; case 12: case 20: return "I fully support the colonization effort!"; case 9: case 22: return "You ever taste those rice noodles? It's like eating a whip!"; default: return UnexpectedOrigin(mission); } } } internal static class NandTweaksCompatibility { internal const string PluginGuid = "com.nandbrew.nandtweaks"; private const string DecalPatchTypeName = "NANDTweaks.MissionGoodsPatches+TextureReplacer"; private const string DecalPostfixMethodName = "Postfix"; internal static void Apply(Harmony harmony) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown Type type = AccessTools.TypeByName("NANDTweaks.MissionGoodsPatches+TextureReplacer"); if (!(type == null)) { MethodInfo methodInfo = AccessTools.Method(type, "Postfix", new Type[1] { typeof(Good) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(NandTweaksCompatibility), "AllowMissionGoodsDecal", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Debug.LogWarning((object)"Postal Expansion: NANDTweaks mission decal compatibility could not be applied."); } else { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } } private static bool AllowMissionGoodsDecal(Good __0) { return !PostalMail.IsRegisteredLetter(((Object)(object)__0 == (Object)null) ? null : ((Component)__0).GetComponent()); } } internal sealed class NeverdinTempleMission : RegisteredLetterMissionDefinition { internal static readonly NeverdinTempleMission Instance = new NeverdinTempleMission(); private NeverdinTempleMission() : base("neverdin_temple", "Neverdin", 2, new Vector3(-11.756f, 3.429f, 148.984f), new Vector3(0f, 4.207f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[6] { 0, 10, 9, 6, 32, 22 }, "Temple") { }//IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 9: case 22: return "They need the guidance of the sun."; case 0: case 6: return "Sabbih Ash-shams!"; case 32: return "The sun gives, and the sun takes away."; case 10: return "This interfaith dialogue is inspiring."; default: return UnexpectedOrigin(mission); } } } internal sealed class OasisLighthouseMission : RegisteredLetterMissionDefinition { internal static readonly OasisLighthouseMission Instance = new OasisLighthouseMission(); private OasisLighthouseMission() : base("oasis_lighthouse", "Oasis", 6, new Vector3(284.163f, 5.081f, -96.62f), new Vector3(0f, 149.388f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Night, new int[5] { 0, 32, 5, 22, 20 }, "Lighthouse") { }//IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 0: case 32: return "Always vigilant."; case 5: case 22: return "We'll help with astrology research."; case 20: return "...I will keep an eye out."; default: return UnexpectedOrigin(mission); } } } internal sealed class OasisPirateHideoutMission : AnonymousLetterMissionDefinition { internal static readonly OasisPirateHideoutMission Instance = new OasisPirateHideoutMission(); private OasisPirateHideoutMission() : base("oasis_pirate_hideout", "Oasis", 6, new Vector3(-10609.6f, 146.177f, -6544.561f), new Vector3(0f, 251.32f, 0f), new Vector3(2f, 2f, 2f), 12463.9f, new LetterDeliveryWindow(12f, 17f, "12-17"), new int[6] { 18, 9, 22, 21, 0, 20 }, "Shitface on bench") { }//IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { int originPortIndex = LetterMissionDefinition.GetOriginPortIndex(mission); if (!VanillaQuestState.IsHideoutQuestComplete()) { switch (originPortIndex) { case 21: return "Quite an old salt ain't ye? Ye should talk to Cap'n."; case 0: case 9: case 18: case 20: case 22: return "If ye ain't afraid o' gettin' yer feet wet, talk to Cap'n."; default: return UnexpectedOrigin(mission); } } return originPortIndex switch { 18 => "Cap'n should get a better 'ouse in Fort Aestrin...", 9 => "Them scallywag better not be squiffy on leaves this time...", 22 => "Pegleg got shitty writing, an' now the letter be wet...", 0 => "Fishy landlubber better 'ave jolly target this time...", 20 => "Me freedom fighters now, Heh Heh.", 21 => "WE NEED A BIGGER SHIP CAP'N!", _ => UnexpectedOrigin(mission), }; } } internal sealed class OldAnkhTownClearMindMission : AnonymousLetterMissionDefinition { internal static readonly OldAnkhTownClearMindMission Instance = new OldAnkhTownClearMindMission(); private OldAnkhTownClearMindMission() : base("old_ankh_town_clear_mind_palms", "Old Ankh Town", 31, new Vector3(-1377.22f, 8.869f, -195.353f), new Vector3(0f, 139.835f, 0f), new Vector3(2.5f, 2.5f, 2.5f), 1391.9f, LetterDeliveryWindow.Night, new int[6] { 15, 30, 25, 13, 11, 19 }, "Lion's Fang middle of 3 palm trees") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 11: case 13: case 15: case 19: case 25: case 30: return "(You put the letter in bottle and bury it as request.)"; default: return UnexpectedOrigin(mission); } } } internal sealed class OldAnkhTownCoffeeFarmerMission : RegisteredLetterMissionDefinition { internal static readonly OldAnkhTownCoffeeFarmerMission Instance = new OldAnkhTownCoffeeFarmerMission(); private OldAnkhTownCoffeeFarmerMission() : base("old_ankh_town_coffee_farmer", "Old Ankh Town", 31, new Vector3(-223.606f, 2.544f, 56.382f), new Vector3(0f, 303.025f, 0f), new Vector3(2.5f, 2.5f, 2.5f), LetterDeliveryWindow.Day, new int[7] { 0, 15, 9, 12, 20, 22, 19 }, "Coffee farmer") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 0: return "Glory to Sultan!"; case 9: case 22: return "I'd love to get some tea in return."; case 12: case 15: case 19: case 20: return "Arabica beans are much better then Emerald's Robusta."; default: return UnexpectedOrigin(mission); } } } internal sealed class OnnaHoneyShamanMission : RegisteredLetterMissionDefinition { internal static readonly OnnaHoneyShamanMission Instance = new OnnaHoneyShamanMission(); private OnnaHoneyShamanMission() : base("onna_honey_shaman", "On'na", 24, new Vector3(-7.32f, 1.007f, 0.395f), new Vector3(0f, 99.218f, 0f), new Vector3(2.5f, 2.5f, 2.5f), LetterDeliveryWindow.Day, new int[8] { 29, 21, 6, 27, 17, 4, 13, 31 }, "Honey Shaman") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 29: return "Ha! You think I'm stupid?"; case 21: return "My honey knows no boundary."; case 4: case 6: case 13: case 17: case 27: case 31: return "I should grow more flowers... maybe a little rain..."; default: return UnexpectedOrigin(mission); } } } internal sealed class PersistentLetterDeliveryAnchor : MonoBehaviour { private Transform source; private Vector3 sourceLocalPosition; private Quaternion sourceLocalRotation; private Rigidbody[] triggerBodies; internal void Initialize(Transform sourceTransform, Vector3 localPosition, Vector3 localEulerAngles) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) source = sourceTransform; sourceLocalPosition = localPosition; sourceLocalRotation = Quaternion.Euler(localEulerAngles); triggerBodies = ((Component)this).GetComponentsInChildren(); ApplyPose(); } private void FixedUpdate() { ApplyPose(); } private void ApplyPose() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null) { return; } Vector3 position = source.TransformPoint(sourceLocalPosition); Quaternion rotation = source.rotation * sourceLocalRotation; ((Component)this).transform.position = position; ((Component)this).transform.rotation = rotation; if (triggerBodies == null) { return; } Rigidbody[] array = triggerBodies; foreach (Rigidbody val in array) { if ((Object)(object)val != (Object)null) { val.position = position; val.rotation = rotation; } } } } [BepInPlugin("com.DogEggz.postalexpansion", "Postal Expansion", "1.1.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { internal const string PluginGuid = "com.DogEggz.postalexpansion"; internal const string PluginName = "Postal Expansion"; internal const string PluginVersion = "1.1.1"; private const float DefaultRegularMailRewardMultiplier = 2f; private const float DefaultRegularMailReputationMultiplier = 4.5f; private static readonly float[] RegularMailRewardOptions = new float[5] { 2f, 3f, 4f, 6f, 8f }; private static readonly float[] RegularMailReputationOptions = new float[4] { 3f, 3.75f, 4.5f, 6f }; private Harmony harmony; internal static ConfigEntry RegularMailRewardMultiplier { get; private set; } internal static ConfigEntry RegularMailReputationMultiplier { get; private set; } private void Awake() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown BindConfiguration(); harmony = new Harmony("com.DogEggz.postalexpansion"); harmony.PatchAll(Assembly.GetExecutingAssembly()); NandTweaksCompatibility.Apply(harmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Postal Expansion loaded."); } private void BindConfiguration() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { CustomDrawer = DrawRegularMailRewardButtons, DefaultValue = 2f }; RegularMailRewardMultiplier = ((BaseUnityPlugin)this).Config.Bind("Regular Mail", "Gold Reward Multiplier", 2f, new ConfigDescription("Gold reward multiplier for newly generated regular mail missions. 4f is the vanilla value.", (AcceptableValueBase)(object)new AcceptableValueList(RegularMailRewardOptions), new object[1] { configurationManagerAttributes })); ConfigurationManagerAttributes configurationManagerAttributes2 = new ConfigurationManagerAttributes { CustomDrawer = DrawRegularMailReputationButtons, DefaultValue = 4.5f }; RegularMailReputationMultiplier = ((BaseUnityPlugin)this).Config.Bind("Regular Mail", "Reputation Reward Multiplier", 4.5f, new ConfigDescription("Reputation reward multiplier for regular mail deliveries only. 3f is the vanilla value.", (AcceptableValueBase)(object)new AcceptableValueList(RegularMailReputationOptions), new object[1] { configurationManagerAttributes2 })); } private void Update() { if (Input.GetKeyDown((KeyCode)285)) { LetterMissionDebug.ShowSpawnLocations(); } } private static void DrawRegularMailRewardButtons(ConfigEntryBase setting) { DrawFloatButtons(setting, RegularMailRewardOptions); } private static void DrawRegularMailReputationButtons(ConfigEntryBase setting) { DrawFloatButtons(setting, RegularMailReputationOptions); } private static void DrawFloatButtons(ConfigEntryBase setting, float[] options) { float num = (float)setting.BoxedValue; GUILayout.BeginHorizontal(Array.Empty()); foreach (float num2 in options) { bool flag = Mathf.Approximately(num, num2); if (GUILayout.Toggle(flag, $"{num2:0.##}f", GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }) && !flag) { setting.BoxedValue = num2; } } GUILayout.EndHorizontal(); } private void OnDestroy() { LetterDialogueTemplate.Dispose(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } } internal static class PostalExpressMail { private readonly struct ExpressMissionDestinationCandidate { internal Port Destination { get; } internal float Distance { get; } internal bool HardPort { get; } internal ExpressMissionDestinationCandidate(Port destination, float distance, bool hardPort) { Destination = destination; Distance = distance; HardPort = hardPort; } } private const int ExpressMailGoodIndex = 69; private const float ExpressWorldDistance = 140f; private const float ExpressMailDueDayBaseSpeed = 2.6f; private const float ExpressRewardMultiplier = 1.5f; private const float HardPortRewardMultiplier = 2f; private const float ComparableRegularMailPackageMultiplier = 2f; private const string ExpressMailName = "Express Mail"; private const string ExpressMailSealTextureFile = "express_mail_seal.png"; private const string ExpressMailSealObjectName = "postal_expansion_express_mail_seal"; private const float ExpressMailSealSize = 0.32f; private static readonly Vector3 ExpressMailSealLocalPosition = new Vector3(0.303f, 0.215f, 0.171f); private static readonly HashSet ExpressMailAvailablePortIndices = new HashSet { 15, 18, 19, 16, 20, 12, 9, 11, 13, 23, 22, 0, 2, 1, 31, 6, 25, 28, 10, 5, 21 }; private static readonly HashSet HardPortIndices = new HashSet { 26, 32, 31, 3, 13, 30, 24, 25, 21 }; private static GameObject expressMailPrefab; private static readonly PostalSealVisual ExpressMailSeal = new PostalSealVisual("postal_expansion_express_mail_seal", "express_mail_seal.png", "express_mail_seal", 0.32f); internal static GameObject Prefab => expressMailPrefab; internal static bool EnsureRegistered(PrefabsDirectory directory) { if ((Object)(object)directory == (Object)null || directory.directory == null) { return false; } if (PostalPrefabRegistration.IsRegistered(directory, 239, expressMailPrefab)) { return true; } if (PostalPrefabRegistration.IsSlotOccupiedByAnotherPrefab(directory, 239, expressMailPrefab)) { Debug.LogError((object)"Postal Expansion: prefab index 239 is already occupied."); return false; } if (directory.directory.Length <= 221 || (Object)(object)directory.directory[221] == (Object)null) { Debug.LogWarning((object)"Postal Expansion: regular mail prefab was not found."); return false; } GameObject val = directory.directory[221]; if (!HasRequiredComponents(val) || !PostalPrefabRegistration.EnsureCapacity(directory, 239)) { Debug.LogError((object)"Postal Expansion: Express Mail prefab components could not be prepared."); return false; } if ((Object)(object)expressMailPrefab == (Object)null) { expressMailPrefab = Object.Instantiate(val); ((Object)expressMailPrefab).name = string.Format("{0} ({1}) {2}", 239, 69, "Express Mail"); Object.DontDestroyOnLoad((Object)(object)expressMailPrefab); } ConfigureExpressMailPrefab(expressMailPrefab); EnsureExpressMailSeal(expressMailPrefab); if (!PostalPrefabRegistration.RegisterShipItem(directory, 239, expressMailPrefab)) { Debug.LogError((object)"Postal Expansion: Express Mail prefab could not be registered as a ship item."); return false; } directory.directory[239] = expressMailPrefab; return true; } internal static void AddExpressMissions(Port origin, List missions) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) PrefabsDirectory instance = PrefabsDirectory.instance; if ((Object)(object)origin == (Object)null || missions == null || (!PostalPrefabRegistration.IsRegistered(instance, 239, expressMailPrefab) && !EnsureRegistered(instance)) || Port.ports == null || !ExpressMailAvailablePortIndices.Contains(origin.portIndex)) { return; } Good component = expressMailPrefab.GetComponent(); if ((Object)(object)component == (Object)null) { return; } int repLevel = PlayerReputation.GetRepLevel(origin.region); if (repLevel < component.requiredRepLevel) { return; } int expressMissionCount = GetExpressMissionCount(repLevel); List expressMissionDestinationCandidates = GetExpressMissionDestinationCandidates(origin, repLevel, world: false); List expressMissionDestinationCandidates2 = GetExpressMissionDestinationCandidates(origin, repLevel, world: true); HashSet selectedPorts = new HashSet(); List list = new List(expressMissionCount * 2); int num = 0; int num2 = 0; if (repLevel >= 6) { num = AddRandomExpressMissions(origin, component, GetHardPortCandidates(expressMissionDestinationCandidates), 1, selectedPorts, list, GetExpressSelectionSeed(origin, repLevel, 11)); num2 = AddRandomExpressMissions(origin, component, GetHardPortCandidates(expressMissionDestinationCandidates2), 1, selectedPorts, list, GetExpressSelectionSeed(origin, repLevel, 12)); } AddRandomExpressMissions(origin, component, expressMissionDestinationCandidates, expressMissionCount - num, selectedPorts, list, GetExpressSelectionSeed(origin, repLevel, 21)); AddRandomExpressMissions(origin, component, expressMissionDestinationCandidates2, expressMissionCount - num2, selectedPorts, list, GetExpressSelectionSeed(origin, repLevel, 22)); if (repLevel >= 6 && UrgentExpressMail.CanOfferAt(origin)) { List list2 = list.FindAll((Mission mission) => mission != null && mission.distance >= 140f); if (list2.Count > 0) { Random random = new Random(GetExpressSelectionSeed(origin, repLevel, 31)); UrgentExpressMail.MarkGenerated(list2[random.Next(list2.Count)]); } } missions.AddRange(list); } internal static float GetExpressRewardMultiplier(Port destination) { if (!IsHardPort(destination)) { return 1.5f; } return 3f; } private static bool HasRequiredComponents(GameObject prefab) { if ((Object)(object)prefab.GetComponent() != (Object)null && (Object)(object)prefab.GetComponent() != (Object)null) { return (Object)(object)prefab.GetComponent() != (Object)null; } return false; } private static void ConfigureExpressMailPrefab(GameObject prefab) { prefab.SetActive(true); prefab.GetComponent().prefabIndex = 239; prefab.GetComponent().name = "Express Mail"; prefab.GetComponent().requiredRepLevel = 3; } private static bool IsHardPort(Port port) { if ((Object)(object)port != (Object)null) { return HardPortIndices.Contains(port.portIndex); } return false; } private static int GetExpressMissionCount(int reputationLevel) { if (reputationLevel < 3) { return 0; } return reputationLevel switch { 3 => 1, 4 => 2, _ => 3, }; } private static List GetExpressMissionDestinationCandidates(Port origin, int reputationLevel, bool world) { List list = new List(); Port[] ports = Port.ports; foreach (Port val in ports) { if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)origin || val.portIndex == 33 || val.portIndex == 7) { continue; } float distance = Mission.GetDistance(origin, val); bool flag = (world ? (distance >= 140f) : (distance < 140f)); if (PostalMail.IsWithinVanillaReputationRange(origin, distance) && flag) { bool flag2 = IsHardPort(val); if ((!flag2 || reputationLevel >= 4) && !PlayerAlreadyHasMission(origin, val, expressMailPrefab)) { list.Add(new ExpressMissionDestinationCandidate(val, distance, flag2)); } } } return list; } private static List GetHardPortCandidates(List candidates) { List list = new List(); foreach (ExpressMissionDestinationCandidate candidate in candidates) { if (candidate.HardPort) { list.Add(candidate); } } return list; } private static int AddRandomExpressMissions(Port origin, Good expressGood, List candidates, int count, HashSet selectedPorts, List missions, int seed) { ShuffleExpressCandidates(candidates, new Random(seed)); int num = 0; foreach (ExpressMissionDestinationCandidate candidate in candidates) { if (num >= count) { break; } if (selectedPorts.Add(candidate.Destination.portIndex)) { missions.Add(GenerateExpressMailMission(origin, candidate.Destination, expressGood, candidate.Distance)); num++; } } return num; } private static void ShuffleExpressCandidates(List candidates, Random random) { for (int num = candidates.Count - 1; num > 0; num--) { int index = random.Next(num + 1); ExpressMissionDestinationCandidate value = candidates[num]; candidates[num] = candidates[index]; candidates[index] = value; } } private static int GetExpressSelectionSeed(Port origin, int reputationLevel, int selectionGroupId) { return (((527 + GameState.day) * 31 + origin.portIndex) * 31 + reputationLevel) * 31 + selectionGroupId; } private static Mission GenerateExpressMailMission(Port origin, Port destination, Good expressGood, float distance) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) float num = distance * DebugMarketTracker.instance.missionDistanceFee; float expressRewardMultiplier = GetExpressRewardMultiplier(destination); int num2 = Mathf.RoundToInt(num * 2f * Plugin.RegularMailRewardMultiplier.Value * expressRewardMultiplier); int expressMailDueDay = GetExpressMailDueDay(origin, destination, expressGood); Mission val = new Mission(origin, destination, ((Component)expressGood).gameObject, 1, num2, 1f, 0, expressMailDueDay); val.totalPrice = CurrencyMarket.instance.GetSellPriceInCurrency((Currency)destination.region, (float)val.totalPrice, false); val.pricePerKm = (float)val.totalPrice / val.distance; return val; } private static int GetExpressMailDueDay(Port origin, Port destination, Good expressGood) { return MailDueDateCalculator.Calculate(origin, destination, expressGood, 2.6f); } private static bool PlayerAlreadyHasMission(Port origin, Port destination, GameObject goodPrefab) { Mission[] missions = PlayerMissions.missions; if (missions == null) { return false; } Mission[] array = missions; foreach (Mission val in array) { if (val != null && (Object)(object)val.originPort == (Object)(object)origin && (Object)(object)val.destinationPort == (Object)(object)destination && (Object)(object)val.goodPrefab == (Object)(object)goodPrefab) { return true; } } return false; } private static void EnsureExpressMailSeal(GameObject prefab) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) ExpressMailSeal.Ensure(prefab, ExpressMailSealLocalPosition, Quaternion.identity); } } internal static class PostalMail { internal const int RegularMailItemIndex = 221; internal const int RegisteredLetterItemIndex = 238; internal const int ExpressMailItemIndex = 239; internal static bool IsMailPrefab(SaveablePrefab saveable) { if ((Object)(object)saveable != (Object)null) { if (saveable.prefabIndex != 221 && !IsRegisteredLetter(saveable)) { return IsExpressMail(saveable); } return true; } return false; } internal static bool IsRegisteredLetter(SaveablePrefab saveable) { return IsOwnedPrefab(saveable, 238, Letter.Prefab); } internal static bool IsExpressMail(SaveablePrefab saveable) { return IsOwnedPrefab(saveable, 239, PostalExpressMail.Prefab); } internal static bool IsWithinVanillaReputationRange(Port origin, float distance) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)origin != (Object)null) { return distance <= PlayerReputation.GetMaxDistance(origin.region); } return false; } private static bool IsOwnedPrefab(SaveablePrefab saveable, int prefabIndex, GameObject expectedPrefab) { if ((Object)(object)saveable != (Object)null && saveable.prefabIndex == prefabIndex) { return PostalPrefabRegistration.IsRegistered(PrefabsDirectory.instance, prefabIndex, expectedPrefab); } return false; } } internal sealed class PostalMailTabButton : GPButtonMissionListWorld { public override void Start() { Component component = ((Component)this).GetComponent("Outline"); if ((Object)(object)component != (Object)null) { Behaviour val = (Behaviour)(object)((component is Behaviour) ? component : null); if (val != null) { val.enabled = false; } Object.Destroy((Object)(object)component); } ((GoPointerButton)this).Start(); } public override void OnActivate() { PostalMissionUi.SetMailMissions(mail: true); MissionListUI.instance.ResetPage(); } } [HarmonyPatch(typeof(PlayerMissions), "AcceptMission")] internal static class PostalMissionAcceptancePatch { private static void Postfix(Mission mission) { UrgentExpressMail.MissionAccepted(mission); GoldenDeliveryMissions.MissionAccepted(mission); RegisteredLetterMissions.MissionAccepted(mission); AnonymousLetterMissions.MissionAccepted(mission); } } [HarmonyPatch(typeof(SaveLoadManager), "SaveModData")] internal static class PostalMissionSavePatch { private static void Prefix() { UrgentExpressMail.SavePersistentState(); GoldenDeliveryMissions.SavePersistentState(); RegisteredLetterMissions.SavePersistentState(); AnonymousLetterMissions.SavePersistentState(); SpecialMailHistory.SavePersistentState(); } } [HarmonyPatch(typeof(SaveLoadManager), "LoadModData")] internal static class PostalMissionLoadPatch { private static void Postfix() { UrgentExpressMail.LoadPersistentState(); GoldenDeliveryMissions.LoadPersistentState(); RegisteredLetterMissions.LoadPersistentState(); AnonymousLetterMissions.LoadPersistentState(); SpecialMailHistory.LoadPersistentState(); LetterMissionDebug.Reset(); } } [HarmonyPatch(typeof(StartMenu), "StartNewGame")] internal static class PostalMissionNewGamePatch { private static void Prefix() { UrgentExpressMail.ResetRuntimeState(); GoldenDeliveryMissions.ResetRuntimeState(); RegisteredLetterMissions.ResetRuntimeState(); AnonymousLetterMissions.ResetRuntimeState(); SpecialMailHistory.ResetRuntimeState(); LetterMissionDebug.Reset(); } } internal enum PostalMissionTab { Local, World, Mail } internal static class PostalMissionUi { private const int VanillaMissionPageSize = 5; private const string MailTabObjectName = "postal_expansion_mail_tab"; private const string MailTabLabel = "mail"; private const float ThreeTabScale = 2f / 3f; private static readonly Color UrgentMissionColor = new Color(1f, 0.45f, 0.08f, 1f); private static readonly Color GoldenMissionColor = new Color(1f, 0.72f, 0.12f, 1f); private static readonly MaterialPropertyBlock MissionHighlightProperties = new MaterialPropertyBlock(); private static readonly FieldInfo LocalButtonField = AccessTools.Field(typeof(MissionListUI), "localButton"); private static readonly FieldInfo WorldButtonField = AccessTools.Field(typeof(MissionListUI), "worldButton"); private static readonly FieldInfo MissionButtonsField = AccessTools.Field(typeof(MissionListUI), "missionButtons"); private static readonly FieldInfo CurrentPortDudeField = AccessTools.Field(typeof(MissionListUI), "currentPortDude"); private static readonly FieldInfo CurrentPageField = AccessTools.Field(typeof(MissionListUI), "currentPage"); private static readonly FieldInfo CurrentPageCountField = AccessTools.Field(typeof(MissionListUI), "currentPageCount"); private static readonly FieldInfo PageCountTextField = AccessTools.Field(typeof(MissionListUI), "pageCountText"); private static readonly FieldInfo CurrentMissionField = AccessTools.Field(typeof(MissionDetailsUI), "currentMission"); private static readonly FieldInfo DetailsUiField = AccessTools.Field(typeof(MissionDetailsUI), "UI"); private static readonly FieldInfo ClickableField = AccessTools.Field(typeof(MissionDetailsUI), "clickable"); private static readonly FieldInfo MapZoomedInField = AccessTools.Field(typeof(MissionDetailsUI), "mapZoomedIn"); private static readonly MethodInfo UpdateTextsMethod = AccessTools.Method(typeof(MissionDetailsUI), "UpdateTexts", (Type[])null, (Type[])null); private static MissionListUI patchedUi; private static PostalMailTabButton mailButton; private static bool mailMissions; internal static void EnsureMailButton(MissionListUI ui) { if ((Object)(object)ui == (Object)null || ((Object)(object)patchedUi == (Object)(object)ui && (Object)(object)mailButton != (Object)null)) { return; } if (LocalButtonField == null || WorldButtonField == null) { Debug.LogWarning((object)"Postal Expansion: mission tab fields were not found."); return; } object? value = LocalButtonField.GetValue(ui); GPButtonMissionListWorld val = (GPButtonMissionListWorld)((value is GPButtonMissionListWorld) ? value : null); object? value2 = WorldButtonField.GetValue(ui); GPButtonMissionListWorld val2 = (GPButtonMissionListWorld)((value2 is GPButtonMissionListWorld) ? value2 : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { Debug.LogWarning((object)"Postal Expansion: mission tab buttons were not found."); return; } Transform tabRoot = GetTabRoot((Component)(object)val); Transform tabRoot2 = GetTabRoot((Component)(object)val2); if ((Object)(object)tabRoot == (Object)null || (Object)(object)tabRoot2 == (Object)null || (Object)(object)tabRoot.parent == (Object)null || (Object)(object)tabRoot.parent != (Object)(object)tabRoot2.parent) { Debug.LogWarning((object)"Postal Expansion: mission tab roots were not found."); return; } Transform val3 = tabRoot2.parent.Find("postal_expansion_mail_tab"); if ((Object)(object)val3 != (Object)null) { mailButton = ((Component)val3).GetComponentInChildren(true); if ((Object)(object)mailButton != (Object)null) { patchedUi = ui; UpdateTabMaterials(ui); return; } } GameObject val4 = Object.Instantiate(((Component)tabRoot2).gameObject, tabRoot2.parent); ((Object)val4).name = "postal_expansion_mail_tab"; GPButtonMissionListWorld componentInChildren = val4.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { Object.Destroy((Object)(object)val4); Debug.LogWarning((object)"Postal Expansion: cloned world tab button was not found."); return; } GameObject gameObject = ((Component)componentInChildren).gameObject; ((Behaviour)componentInChildren).enabled = false; Object.Destroy((Object)(object)componentInChildren); TextMesh componentInChildren2 = val4.GetComponentInChildren(true); if ((Object)(object)componentInChildren2 != (Object)null) { componentInChildren2.text = "mail"; } mailButton = gameObject.AddComponent(); ((GPButtonMissionListWorld)mailButton).activeMat = val2.activeMat; ((GPButtonMissionListWorld)mailButton).inactiveMat = val2.inactiveMat; FitThreeTabs(tabRoot, tabRoot2, val4.transform); patchedUi = ui; UpdateTabMaterials(ui); } internal static void SetMailMissions(bool mail) { mailMissions = mail; UpdateTabMaterials(MissionListUI.instance); } internal static void FilterInitialPortMissions(MissionListUI ui, PortDude dude, ref Mission[] missions) { if ((Object)(object)ui != (Object)null && (Object)(object)dude != (Object)null) { missions = GetMissions(ui, dude.GetPort(), PostalMissionTab.Local, 0); } } internal static void RefreshPageCount(MissionListUI ui) { if (HasMissionListPagingReflection() && !((Object)(object)ui == (Object)null) && !((Object)(object)GetCurrentPortDude(ui) == (Object)null)) { UpdatePageCount(ui); UpdatePageCountText(ui); } } internal static bool TryResetPage(MissionListUI ui) { if (!GameState.inPortMissionList || !HasMissionListPagingReflection() || (Object)(object)ui == (Object)null || (Object)(object)GetCurrentPortDude(ui) == (Object)null) { return false; } SetCurrentPage(ui, 0); UISoundPlayer.instance.PlayParchmentSound(); ui.DisplayMissions(GetMissions(ui, GetCurrentPage(ui))); UpdatePageCount(ui); UpdatePageCountText(ui); return true; } internal static bool TryChangePage(MissionListUI ui, int pageChange) { if (!GameState.inPortMissionList || !HasMissionListPagingReflection() || (Object)(object)ui == (Object)null) { return false; } if ((Object)(object)GetCurrentPortDude(ui) == (Object)null) { Debug.LogError((object)"MissionListUI.ChangePage: currentPortDude is null."); return true; } SetCurrentPage(ui, GetCurrentPage(ui) + pageChange); if (GetCurrentPage(ui) < 0) { SetCurrentPage(ui, 0); } else if (GetCurrentPage(ui) > GetCurrentPageCount(ui) - 1) { SetCurrentPage(ui, GetCurrentPageCount(ui) - 1); } else { UISoundPlayer.instance.PlayParchmentSound(); ui.DisplayMissions(GetMissions(ui, GetCurrentPage(ui))); } UpdatePageCountText(ui); return true; } internal static bool TryHandlePortMissionAcceptance(MissionDetailsUI detailsUi) { MissionListUI instance = MissionListUI.instance; if (!GameState.inPortMissionList || !HasMissionDetailsReflection() || !HasMissionListPagingReflection() || (Object)(object)detailsUi == (Object)null || (Object)(object)instance == (Object)null || (Object)(object)GetCurrentPortDude(instance) == (Object)null || !(bool)ClickableField.GetValue(detailsUi) || (bool)MapZoomedInField.GetValue(detailsUi)) { return false; } object? value = CurrentMissionField.GetValue(detailsUi); Mission val = (Mission)((value is Mission) ? value : null); if (val == null || val.missionIndex != -1) { return false; } PlayerMissions.AcceptMission(val); instance.DisplayMissions(GetMissions(instance, 0)); object? value2 = DetailsUiField.GetValue(detailsUi); GameObject val2 = (GameObject)((value2 is GameObject) ? value2 : null); if ((Object)(object)val2 != (Object)null) { val2.SetActive(false); } UpdateTextsMethod.Invoke(detailsUi, null); return true; } internal static void UpdateMissionHighlights(MissionListUI ui) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ui == (Object)null || MissionButtonsField == null || !(MissionButtonsField.GetValue(ui) is GPButtonListedMission[] array)) { return; } GPButtonListedMission[] array2 = array; foreach (GPButtonListedMission val in array2) { if (!((Object)(object)val == (Object)null)) { Renderer component = ((Component)val).GetComponent(); Material val2 = (((Object)(object)component != (Object)null) ? component.sharedMaterial : null); if (!((Object)(object)val2 == (Object)null)) { bool activeSelf = ((Component)val).gameObject.activeSelf; bool flag = activeSelf && GoldenDeliveryMissions.IsGolden(val.mission); bool flag2 = activeSelf && UrgentExpressMail.IsUrgent(val.mission); bool highlighted = flag || flag2; Color highlightColor = (flag ? GoldenMissionColor : UrgentMissionColor); MissionHighlightProperties.Clear(); component.GetPropertyBlock(MissionHighlightProperties); SetMissionMaterialColor(val2, "_Color", highlighted, highlightColor); SetMissionMaterialColor(val2, "_BaseColor", highlighted, highlightColor); component.SetPropertyBlock(MissionHighlightProperties); } } } } private static Mission[] GetMissions(MissionListUI ui, int page) { return GetMissions(ui, GetCurrentPortDude(ui).GetPort(), GetCurrentTab(ui), page); } private static Mission[] GetMissions(MissionListUI ui, Port port, PostalMissionTab tab, int page) { int missionPageSize = GetMissionPageSize(ui); Mission[] array = (Mission[])(object)new Mission[missionPageSize]; int num = page * missionPageSize; List availableMissions = GetAvailableMissions(port, tab); for (int i = 0; i < array.Length; i++) { int num2 = num + i; if (num2 < availableMissions.Count) { array[i] = availableMissions[num2]; } } return array; } private static int GetMissionPageSize(MissionListUI ui) { if (MissionButtonsField?.GetValue(ui) is GPButtonListedMission[] array && array.Length != 0) { return array.Length; } return 5; } private static List GetAvailableMissions(Port port, PostalMissionTab tab) { List list = new List(); if ((Object)(object)port == (Object)null) { return list; } if (tab == PostalMissionTab.Mail) { AddFilteredVanillaMissions(port, world: false, tab, list); AddFilteredVanillaMissions(port, world: true, tab, list); PostalExpressMail.AddExpressMissions(port, list); GoldenDeliveryMissions.AddMission(port, list); RegisteredLetterMissions.AddMissions(port, list); AnonymousLetterMissions.AddMissions(port, list); } else { AddFilteredVanillaMissions(port, tab == PostalMissionTab.World, tab, list); } list.Sort((Mission left, Mission right) => right.pricePerKm.CompareTo(left.pricePerKm)); return list; } private static void AddFilteredVanillaMissions(Port port, bool world, PostalMissionTab tab, List missions) { AddMissions(port.GetMissions(0, world), tab, missions); int num = Mathf.Max(1, Mathf.CeilToInt((float)port.GetMissionCount() / 5f)); for (int i = 1; i < num; i++) { AddMissions(port.GetMissions(i, world), tab, missions); } } private static void AddMissions(Mission[] candidates, PostalMissionTab tab, List missions) { if (candidates == null) { return; } foreach (Mission val in candidates) { if (val != null && ShouldShowMission(val, tab)) { missions.Add(val); } } } private static bool ShouldShowMission(Mission candidate, PostalMissionTab tab) { bool flag = IsMailMission(candidate); if (tab != PostalMissionTab.Mail) { return !flag; } return flag; } private static bool IsMailMission(Mission candidate) { if (candidate == null || (Object)(object)candidate.goodPrefab == (Object)null) { return false; } return PostalMail.IsMailPrefab(candidate.goodPrefab.GetComponent()); } private static void UpdatePageCount(MissionListUI ui) { int missionPageSize = GetMissionPageSize(ui); int count = GetAvailableMissions(GetCurrentPortDude(ui).GetPort(), GetCurrentTab(ui)).Count; int num = Mathf.Max(1, Mathf.CeilToInt((float)count / (float)missionPageSize)); CurrentPageCountField.SetValue(ui, num); } private static void UpdatePageCountText(MissionListUI ui) { object? value = PageCountTextField.GetValue(ui); TextMesh val = (TextMesh)((value is TextMesh) ? value : null); if ((Object)(object)val != (Object)null) { val.text = $"{GetCurrentPage(ui) + 1} / {GetCurrentPageCount(ui)}"; } } private static void UpdateTabMaterials(MissionListUI ui) { if (!((Object)(object)ui == (Object)null) && !(LocalButtonField == null) && !(WorldButtonField == null)) { object? value = LocalButtonField.GetValue(ui); GPButtonMissionListWorld val = (GPButtonMissionListWorld)((value is GPButtonMissionListWorld) ? value : null); object? value2 = WorldButtonField.GetValue(ui); GPButtonMissionListWorld val2 = (GPButtonMissionListWorld)((value2 is GPButtonMissionListWorld) ? value2 : null); PostalMissionTab currentTab = GetCurrentTab(ui); if ((Object)(object)val != (Object)null) { val.SetMaterial(currentTab == PostalMissionTab.Local); } if ((Object)(object)val2 != (Object)null) { val2.SetMaterial(currentTab == PostalMissionTab.World); } if ((Object)(object)mailButton != (Object)null) { ((GPButtonMissionListWorld)mailButton).SetMaterial(currentTab == PostalMissionTab.Mail); } } } private static PostalMissionTab GetCurrentTab(MissionListUI ui) { if (mailMissions) { return PostalMissionTab.Mail; } if (!ui.worldMissions) { return PostalMissionTab.Local; } return PostalMissionTab.World; } private static PortDude GetCurrentPortDude(MissionListUI ui) { object? obj = CurrentPortDudeField?.GetValue(ui); return (PortDude)((obj is PortDude) ? obj : null); } private static int GetCurrentPage(MissionListUI ui) { return (int)CurrentPageField.GetValue(ui); } private static void SetCurrentPage(MissionListUI ui, int page) { CurrentPageField.SetValue(ui, page); } private static int GetCurrentPageCount(MissionListUI ui) { return (int)CurrentPageCountField.GetValue(ui); } private static Transform GetTabRoot(Component tabComponent) { if (!((Object)(object)tabComponent.transform.parent != (Object)null)) { return tabComponent.transform; } return tabComponent.transform.parent; } private static void FitThreeTabs(Transform localRoot, Transform worldRoot, Transform mailRoot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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_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) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) Vector3 localPosition = localRoot.localPosition; Vector3 localPosition2 = worldRoot.localPosition; Vector3 val = localPosition2 - localPosition; localRoot.localPosition = localPosition - val / 6f; worldRoot.localPosition = localPosition + val * 0.5f; mailRoot.localPosition = localPosition2 + val / 6f; ScaleTabRoot(localRoot); ScaleTabRoot(worldRoot); ScaleTabRoot(mailRoot); } private static void ScaleTabRoot(Transform tabRoot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Vector3 localScale = tabRoot.localScale; localScale.x *= 2f / 3f; tabRoot.localScale = localScale; } private static void SetMissionMaterialColor(Material material, string propertyName, bool highlighted, Color highlightColor) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (material.HasProperty(propertyName)) { Color color = material.GetColor(propertyName); if (highlighted) { color.r = highlightColor.r; color.g = highlightColor.g; color.b = highlightColor.b; } MissionHighlightProperties.SetColor(propertyName, color); } } private static bool HasMissionListPagingReflection() { if (MissionButtonsField != null && CurrentPortDudeField != null && CurrentPageField != null && CurrentPageCountField != null) { return PageCountTextField != null; } return false; } private static bool HasMissionDetailsReflection() { if (CurrentMissionField != null && DetailsUiField != null && ClickableField != null && MapZoomedInField != null) { return UpdateTextsMethod != null; } return false; } } internal static class PostalPrefabRegistration { internal static bool IsRegistered(PrefabsDirectory directory, int prefabIndex, GameObject prefab) { if ((Object)(object)directory != (Object)null && directory.directory != null && directory.shipItems != null && (Object)(object)prefab != (Object)null && directory.directory.Length > prefabIndex && directory.shipItems.Length > prefabIndex && (Object)(object)directory.directory[prefabIndex] == (Object)(object)prefab) { return (Object)(object)directory.shipItems[prefabIndex] == (Object)(object)prefab.GetComponent(); } return false; } internal static bool IsSlotOccupiedByAnotherPrefab(PrefabsDirectory directory, int prefabIndex, GameObject expectedPrefab) { if ((Object)(object)directory == (Object)null) { return false; } if (directory.directory != null && directory.directory.Length > prefabIndex && (Object)(object)directory.directory[prefabIndex] != (Object)null && (Object)(object)directory.directory[prefabIndex] != (Object)(object)expectedPrefab) { return true; } ShipItem val = (((Object)(object)expectedPrefab != (Object)null) ? expectedPrefab.GetComponent() : null); if (directory.shipItems != null && directory.shipItems.Length > prefabIndex && (Object)(object)directory.shipItems[prefabIndex] != (Object)null) { return (Object)(object)directory.shipItems[prefabIndex] != (Object)(object)val; } return false; } internal static bool EnsureCapacity(PrefabsDirectory directory, int prefabIndex) { if ((Object)(object)directory == (Object)null || directory.directory == null) { return false; } int num = prefabIndex + 1; if (directory.directory.Length < num) { Array.Resize(ref directory.directory, num); } if (directory.shipItems == null) { directory.shipItems = (ShipItem[])(object)new ShipItem[directory.directory.Length]; } else if (directory.shipItems.Length < num) { Array.Resize(ref directory.shipItems, num); } return true; } internal static bool RegisterShipItem(PrefabsDirectory directory, int prefabIndex, GameObject prefab) { if ((Object)(object)directory == (Object)null || directory.shipItems == null || directory.shipItems.Length <= prefabIndex || (Object)(object)prefab == (Object)null) { return false; } ShipItem component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } directory.shipItems[prefabIndex] = component; return true; } } internal sealed class PostalSealVisual { private readonly string objectName; private readonly string textureFileName; private readonly string resourceName; private readonly float size; private Texture2D texture; private Material material; private Mesh mesh; internal PostalSealVisual(string objectName, string textureFileName, string resourceName, float size) { this.objectName = objectName; this.textureFileName = textureFileName; this.resourceName = resourceName; this.size = size; } internal void Ensure(GameObject prefab, Vector3 localPosition, Quaternion localRotation) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null) { return; } MeshRenderer component = prefab.GetComponent(); Material val = GetMaterial(component); if (!((Object)(object)val == (Object)null)) { Transform val2 = prefab.transform.Find(objectName); if ((Object)(object)val2 == (Object)null) { val2 = new GameObject(objectName).transform; val2.SetParent(prefab.transform, false); } val2.localPosition = localPosition; val2.localRotation = localRotation; val2.localScale = Vector3.one; GameObject gameObject = ((Component)val2).gameObject; gameObject.SetActive(true); gameObject.layer = prefab.layer; (gameObject.GetComponent() ?? gameObject.AddComponent()).sharedMesh = GetMesh(); MeshRenderer val3 = gameObject.GetComponent() ?? gameObject.AddComponent(); ((Renderer)val3).sharedMaterial = val; ((Renderer)val3).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val3).receiveShadows = true; if ((Object)(object)component != (Object)null) { ((Renderer)val3).lightProbeUsage = ((Renderer)component).lightProbeUsage; ((Renderer)val3).reflectionProbeUsage = ((Renderer)component).reflectionProbeUsage; ((Renderer)val3).renderingLayerMask = ((Renderer)component).renderingLayerMask; } } } private Material GetMaterial(MeshRenderer parentRenderer) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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) //IL_00a6: Expected O, but got Unknown Texture2D val = LoadTexture(); if ((Object)(object)val == (Object)null) { return null; } if ((Object)(object)material != (Object)null) { return material; } Material val2 = ((parentRenderer != null) ? ((Renderer)parentRenderer).sharedMaterial : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val2.shader == (Object)null) { Debug.LogWarning((object)("Postal Expansion: no source material was found for the " + resourceName + ".")); return null; } material = new Material(val2) { name = resourceName + "_material", mainTexture = (Texture)(object)val, color = Color.white, renderQueue = 2450 }; ConfigureLitCutoutMaterial(material); return material; } private static void ConfigureLitCutoutMaterial(Material targetMaterial) { //IL_0135: Unknown result type (might be due to invalid IL or missing references) targetMaterial.SetOverrideTag("RenderType", "TransparentCutout"); if (targetMaterial.HasProperty("_Mode")) { targetMaterial.SetFloat("_Mode", 1f); } if (targetMaterial.HasProperty("_Cutoff")) { targetMaterial.SetFloat("_Cutoff", 0.1f); } if (targetMaterial.HasProperty("_SrcBlend")) { targetMaterial.SetInt("_SrcBlend", 1); } if (targetMaterial.HasProperty("_DstBlend")) { targetMaterial.SetInt("_DstBlend", 0); } if (targetMaterial.HasProperty("_ZWrite")) { targetMaterial.SetInt("_ZWrite", 1); } if (targetMaterial.HasProperty("_Cull")) { targetMaterial.SetInt("_Cull", 2); } if (targetMaterial.HasProperty("_Glossiness")) { targetMaterial.SetFloat("_Glossiness", 0f); } if (targetMaterial.HasProperty("_Metallic")) { targetMaterial.SetFloat("_Metallic", 0f); } if (targetMaterial.HasProperty("_SpecularHighlights")) { targetMaterial.SetFloat("_SpecularHighlights", 0f); } if (targetMaterial.HasProperty("_GlossyReflections")) { targetMaterial.SetFloat("_GlossyReflections", 0f); } if (targetMaterial.HasProperty("_EmissionColor")) { targetMaterial.SetColor("_EmissionColor", Color.black); } targetMaterial.EnableKeyword("_ALPHATEST_ON"); targetMaterial.DisableKeyword("_ALPHABLEND_ON"); targetMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON"); targetMaterial.DisableKeyword("_EMISSION"); } private Texture2D LoadTexture() { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown if ((Object)(object)texture != (Object)null) { return texture; } string postalAssetPath = GetPostalAssetPath(textureFileName); if (postalAssetPath == null) { Debug.LogWarning((object)("Postal Expansion: " + resourceName + " texture was not found.")); return null; } byte[] array; try { array = File.ReadAllBytes(postalAssetPath); } catch (IOException ex) { Debug.LogWarning((object)("Postal Expansion: " + resourceName + " texture could not be read: " + ex.Message)); return null; } catch (UnauthorizedAccessException ex2) { Debug.LogWarning((object)("Postal Expansion: " + resourceName + " texture could not be read: " + ex2.Message)); return null; } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, array)) { Debug.LogWarning((object)("Postal Expansion: " + resourceName + " texture could not be loaded.")); Object.Destroy((Object)(object)val); return null; } ((Object)val).name = resourceName + "_texture"; ((Texture)val).filterMode = (FilterMode)1; ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).anisoLevel = 8; texture = val; return texture; } private Mesh GetMesh() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_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_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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_0119: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0140: 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_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mesh != (Object)null) { return mesh; } float num = size / 2f; Mesh val = new Mesh(); ((Object)val).name = resourceName + "_mesh"; val.vertices = (Vector3[])(object)new Vector3[8] { new Vector3(0f - num, 0f - num, 0f), new Vector3(0f - num, num, 0f), new Vector3(num, num, 0f), new Vector3(num, 0f - num, 0f), new Vector3(0f - num, 0f - num, 0f), new Vector3(0f - num, num, 0f), new Vector3(num, num, 0f), new Vector3(num, 0f - num, 0f) }; val.uv = (Vector2[])(object)new Vector2[8] { new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(1f, 0f), new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(1f, 0f) }; val.normals = (Vector3[])(object)new Vector3[8] { Vector3.back, Vector3.back, Vector3.back, Vector3.back, Vector3.forward, Vector3.forward, Vector3.forward, Vector3.forward }; val.triangles = new int[12] { 0, 1, 2, 0, 2, 3, 6, 5, 4, 7, 6, 4 }; mesh = val; mesh.RecalculateBounds(); return mesh; } private static string GetPostalAssetPath(string fileName) { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string[] array = new string[2] { Path.Combine(directoryName ?? string.Empty, "assets", fileName), Path.Combine(Paths.PluginPath, "PostalExpansion", "assets", fileName) }; foreach (string text in array) { if (File.Exists(text)) { return text; } } return null; } } [HarmonyPatch(typeof(ShipItem), "AddLODGroup")] internal static class PostalShipItemLodPatch { private static bool Prefix(ShipItem __instance) { SaveablePrefab val = ((__instance != null) ? ((Component)__instance).GetComponent() : null); if ((Object)(object)val == (Object)null || !PostalMail.IsMailPrefab(val) || val.prefabIndex == 221) { return true; } LODGroup obj = RefsDirectory.instance?.LODtemplateItems; LOD[] array = ((obj != null) ? obj.GetLODs() : null); if (array == null || array.Length == 0) { return true; } LODGroup obj2 = ((Component)__instance).GetComponent() ?? ((Component)__instance).gameObject.AddComponent(); LOD[] array2 = (LOD[])array.Clone(); array2[0].renderers = ((Component)__instance).GetComponentsInChildren(true); obj2.SetLODs(array2); return false; } } [HarmonyPatch(typeof(PrefabsDirectory), "Start")] internal static class PrefabsDirectoryStartPatch { private static void Postfix(PrefabsDirectory __instance) { Letter.EnsureRegistered(__instance); PostalExpressMail.EnsureRegistered(__instance); } } internal abstract class RegisteredLetterMissionDefinition : LetterMissionDefinition { protected RegisteredLetterMissionDefinition(string id, string destinationPortName, int destinationPortIndex, Vector3 localPosition, Vector3 localEulerAngles, Vector3 triggerSize, LetterDeliveryWindow deliveryWindow, int[] spawnPortIndices, string locationDescription) : base(id, destinationPortName, destinationPortIndex, localPosition, localEulerAngles, triggerSize, deliveryWindow, spawnPortIndices, locationDescription) { }//IL_0004: 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_0008: Unknown result type (might be due to invalid IL or missing references) } internal static class RegisteredLetterMissionRegistry { private static readonly LetterMissionRegistry Registry = new LetterMissionRegistry("Registered Letter", GoldRockCityPalaceGateMission.Instance, GoldRockCityPalaceFishVendorMission.Instance, NeverdinTempleMission.Instance, AlbacoreTownCreepyGuyMission.Instance, AlchemistsIslandApprenticeMission.Instance, AlAnkhAcademyKeyKeeperMission.Instance, DragonCliffsBrownTobaccoVendorMission.Instance, DragonCliffsTeaMerchantHouseMission.Instance, DragonCliffsChimesHutMission.Instance, SanctuaryMainBuildingMission.Instance, CrabBeachHouseMission.Instance, SageHillsWestTobaccoFarmerMission.Instance, FortAestrinUpperTownGateMission.Instance, FortAestrinWesternmostPierHouseMission.Instance, SunspireSpyglassMakerMission.Instance, HappyBayLocalPrinceMission.Instance, OasisLighthouseMission.Instance, SirenSongEmptyHouseMission.Instance, SerpentIsleWesternmostHouseMission.Instance, MountMaleficWheatManorMission.Instance, ChronosChurchHouseMission.Instance, FireFishTownTempleHouseMission.Instance, SennaAlchemistMission.Instance, SennaBlueTobaccoVendorMission.Instance, OnnaHoneyShamanMission.Instance, FireflyGrottoChurchMission.Instance, AestraAbbeyMainAbbeyMission.Instance, AestraAbbeyGraveRobberTableMission.Instance, FeyValleyWaterfallManorVendorMission.Instance, TurtleIslandSunShamanMission.Instance, OldAnkhTownCoffeeFarmerMission.Instance, MirageMountainMonasteryMission.Instance, FireFishTownPainterMission.Instance, KiciaBayWesternmostHouseMission.Instance); internal static IReadOnlyList All => Registry.All; } internal static class RegisteredLetterMissions { internal const int RequiredReputation = 7; internal const float DueDayBaseSpeed = 3f; internal const float ReputationMultiplier = 8f; internal const int FixedGoldReward = 1; internal const int SeverelyLatePenalty = -5000; private static readonly LetterMissionSaveKeys SaveKeys = new LetterMissionSaveKeys("PostalExpansion.RegisteredLetter.Active.v1", "PostalExpansion.RegisteredLetter.Claimed.v1"); private static readonly LetterMissionSet Missions = new LetterMissionSet("Registered Letter", RegisteredLetterMissionRegistry.All, SaveKeys, 7, 3f, 1, (Port destination) => "Registered Letter to " + destination.GetPortName()); internal static void AddMissions(Port origin, List missions) { Missions.AddMissions(origin, missions); } internal static List GetDebugStatusLines() { return Missions.GetDebugStatusLines(); } internal static bool TryGetDefinition(Mission mission, out RegisteredLetterMissionDefinition definition) { return Missions.TryGetDefinition(mission, out definition); } internal static void MissionAccepted(Mission mission) { Missions.MissionAccepted(mission); } internal static void MissionDelivered(Mission mission) { Missions.MissionDelivered(mission); } internal static void SavePersistentState() { Missions.SavePersistentState(); } internal static void LoadPersistentState() { Missions.LoadPersistentState(); } internal static void ResetRuntimeState() { Missions.ResetRuntimeState(); } } [HarmonyPatch(typeof(PortDude), "OnTriggerEnter")] internal static class RegisteredLetterOfficeDeliveryBlockPatch { private static bool Prefix(Collider other) { Good val = (((Object)(object)other != (Object)null) ? ((Component)other).GetComponentInParent() : null); return !PostalMail.IsRegisteredLetter(((Object)(object)val != (Object)null) ? ((Component)val).GetComponent() : null); } } [HarmonyPatch(typeof(IslandMissionOffice), "GenerateMailMission", new Type[] { typeof(Good), typeof(Port), typeof(float), typeof(int) })] internal static class RegularMissionPackageCountandGoldPatch { private static void Postfix(IslandMissionOffice __instance, float distance, Mission __result) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (__result != null) { int num = Mathf.Clamp(PlayerReputation.GetRepLevel(__instance.GetPort().region), 1, 3); float num2 = distance * DebugMarketTracker.instance.missionDistanceFee; float num3 = (float)num * 0.5f + 0.5f; __result.goodCount = num; __result.totalPrice = Mathf.RoundToInt(num2 * num3 * Plugin.RegularMailRewardMultiplier.Value); __result.pricePerKm = (float)__result.totalPrice / __result.distance; } } } internal sealed class SaffronIslandTraderMission : AnonymousLetterMissionDefinition { internal static readonly SaffronIslandTraderMission Instance = new SaffronIslandTraderMission(); private SaffronIslandTraderMission() : base("saffron_island_trader", "Saffron Island", 33, new Vector3(0.015f, 0.75f, 0.586f), new Vector3(0f, 177.864f, 0f), new Vector3(2.5f, 2.5f, 2.5f), 3.5f, LetterDeliveryWindow.Day, new int[3] { 30, 11, 1 }, "Trader") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { int originPortIndex = LetterMissionDefinition.GetOriginPortIndex(mission); if (originPortIndex == 1 || originPortIndex == 11 || originPortIndex == 30) { return "Thank you, we haven't heard from our family for a while."; } return UnexpectedOrigin(mission); } } internal sealed class SageHillsWestTobaccoFarmerMission : RegisteredLetterMissionDefinition { internal static readonly SageHillsWestTobaccoFarmerMission Instance = new SageHillsWestTobaccoFarmerMission(); private SageHillsWestTobaccoFarmerMission() : base("sage_hills_west_tobacco_farmer", "Sage Hills", 13, new Vector3(-165.17f, 41.067f, -82.02f), new Vector3(0f, 212.161f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[9] { 0, 22, 20, 15, 21, 14, 6, 9, 12 }, "Green tobacco farmer (West Hill)") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 0: case 6: case 12: case 14: case 15: case 20: case 21: return "Best tobacco in the world!"; case 22: return "Heh! Tired of the blue one?"; case 9: return "Bi dien ha? This cut is too much!"; default: return UnexpectedOrigin(mission); } } } internal static class SailwindPortIndex { internal const int GoldRockCity = 0; internal const int AlNilem = 1; internal const int Neverdin = 2; internal const int AlbacoreTown = 3; internal const int AlchemistsIsland = 4; internal const int AlAnkhAcademy = 5; internal const int Oasis = 6; internal const int TestPort = 7; internal const int DragonCliffs = 9; internal const int Sanctuary = 10; internal const int CrabBeach = 11; internal const int NewPort = 12; internal const int SageHills = 13; internal const int SerpentIsle = 14; internal const int FortAestrin = 15; internal const int Sunspire = 16; internal const int MountMalefic = 17; internal const int SirenSong = 18; internal const int Eastwind = 19; internal const int HappyBay = 20; internal const int Chronos = 21; internal const int KiciaBay = 22; internal const int FireFishTown = 23; internal const int Onna = 24; internal const int Senna = 25; internal const int AestraAbbey = 26; internal const int FeyValley = 27; internal const int FireflyGrotto = 28; internal const int TurtleIsland = 29; internal const int DeadCove = 30; internal const int OldAnkhTown = 31; internal const int MirageMountain = 32; internal const int SaffronIsland = 33; } internal sealed class SanctuaryMainBuildingMission : RegisteredLetterMissionDefinition { internal static readonly SanctuaryMainBuildingMission Instance = new SanctuaryMainBuildingMission(); private SanctuaryMainBuildingMission() : base("sanctuary_main_building", "Sanctuary", 10, new Vector3(28.065f, 3.492f, -28.618f), new Vector3(0f, 88.481f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[10] { 0, 4, 5, 15, 26, 20, 9, 29, 13, 21 }, "Main Building") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 0: case 4: case 5: return "(You hear one bell ring.)"; case 15: case 20: case 26: return "(You hear two bells ring.)"; case 9: case 13: case 29: return "(You hear three bells ring.)"; case 21: return "...Namo Amituofo."; default: return UnexpectedOrigin(mission); } } } [HarmonyPatch(typeof(SaveLoadManager), "LoadGame")] internal static class SaveLoadManagerLoadGamePatch { private static void Prefix() { PrefabsDirectory instance = PrefabsDirectory.instance; Letter.EnsureRegistered(instance); PostalExpressMail.EnsureRegistered(instance); } } internal sealed class SennaAlchemistMission : RegisteredLetterMissionDefinition { internal static readonly SennaAlchemistMission Instance = new SennaAlchemistMission(); private SennaAlchemistMission() : base("senna_alchemist", "Sen'na", 25, new Vector3(105.438f, 0.418f, -173.214f), new Vector3(0f, 354.712f, 0f), new Vector3(2.5f, 2.5f, 2.5f), LetterDeliveryWindow.Day, new int[2] { 15, 4 }, "Alchemist") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { return LetterMissionDefinition.GetOriginPortIndex(mission) switch { 15 => "Church force me out and they want me back? Not gonna happen!", 4 => "I wonder what they comes up with this time.", _ => UnexpectedOrigin(mission), }; } } internal sealed class SennaBlueTobaccoVendorMission : RegisteredLetterMissionDefinition { internal static readonly SennaBlueTobaccoVendorMission Instance = new SennaBlueTobaccoVendorMission(); private SennaBlueTobaccoVendorMission() : base("senna_blue_tobacco_vendor", "Sen'na", 25, new Vector3(85.64f, -0.105f, -173.198f), new Vector3(0f, 215.361f, 0f), new Vector3(3f, 3f, 3f), LetterDeliveryWindow.Day, new int[8] { 9, 15, 0, 21, 20, 12, 6, 14 }, "Blue Tobacco vendor") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 9: return "Those tobacco from sage hills is obviously inferior."; case 21: return "Hmm...this might need special preservation."; case 0: case 6: case 12: case 14: case 15: case 20: return "Blue Dream worth every dragons you spend on it."; default: return UnexpectedOrigin(mission); } } } internal sealed class SerpentIsleWesternmostHouseMission : RegisteredLetterMissionDefinition { internal static readonly SerpentIsleWesternmostHouseMission Instance = new SerpentIsleWesternmostHouseMission(); private SerpentIsleWesternmostHouseMission() : base("serpent_isle_westernmost_house", "Serpent Isle", 14, new Vector3(183.915f, 2.622f, -58.679f), new Vector3(0f, 171.934f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[14] { 15, 12, 9, 0, 22, 23, 20, 18, 16, 27, 2, 31, 6, 21 }, "Westernmost house") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 21: return "They must be rich if they can take a vacation here."; case 0: case 2: case 6: case 9: case 12: case 15: case 16: case 18: case 20: case 22: case 23: case 27: case 31: return "We'll check on cabin availability."; default: return UnexpectedOrigin(mission); } } } internal sealed class SirenSongEmptyHouseMission : RegisteredLetterMissionDefinition { internal static readonly SirenSongEmptyHouseMission Instance = new SirenSongEmptyHouseMission(); private SirenSongEmptyHouseMission() : base("siren_song_empty_house", "Siren Song", 18, new Vector3(16.545f, 1.4f, -21.654f), new Vector3(0f, 250.853f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Night, new int[5] { 20, 6, 9, 0, 22 }, "Empty house") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 0: case 6: case 9: case 20: case 22: return "(You slip the letter under the door)"; default: return UnexpectedOrigin(mission); } } } internal enum SpecialMailKind { None, AnonymousLetter, UrgentExpressMail, GoldenDelivery } internal static class SpecialMailDisplay { internal static SpecialMailKind CurrentDeliveryKind { get; private set; } internal static SpecialMailKind GetKind(Mission mission) { if (GoldenDeliveryMissions.IsGolden(mission)) { return SpecialMailKind.GoldenDelivery; } if (UrgentExpressMail.IsUrgent(mission)) { return SpecialMailKind.UrgentExpressMail; } if (AnonymousLetterMissions.TryGetDefinition(mission, out var _)) { return SpecialMailKind.AnonymousLetter; } return SpecialMailKind.None; } internal static string GetDisplayName(SpecialMailKind kind) { return kind switch { SpecialMailKind.AnonymousLetter => "Anonymous Letter", SpecialMailKind.UrgentExpressMail => "Urgent Express Mail", SpecialMailKind.GoldenDelivery => "Golden Delivery", _ => string.Empty, }; } internal static void BeginDelivery(Mission mission) { CurrentDeliveryKind = GetKind(mission); } internal static void EndDelivery() { CurrentDeliveryKind = SpecialMailKind.None; } } [HarmonyPatch(typeof(Mission), "DeliverGood")] internal static class SpecialMailDeliveryContextPatch { private static void Prefix(Mission __instance) { SpecialMailDisplay.BeginDelivery(__instance); } private static Exception Finalizer(Exception __exception) { SpecialMailDisplay.EndDelivery(); return __exception; } } [HarmonyPatch(typeof(NotificationUi), "ShowNotification", new Type[] { typeof(string) })] internal static class SpecialMailDeliveryNotificationPatch { private static void Prefix(ref string notification) { SpecialMailKind currentDeliveryKind = SpecialMailDisplay.CurrentDeliveryKind; if (currentDeliveryKind != SpecialMailKind.None && !string.IsNullOrEmpty(notification) && notification.StartsWith("Delivered ", StringComparison.Ordinal)) { int num = notification.IndexOf('\n'); string text = ((num >= 0) ? notification.Substring(num) : string.Empty); notification = "Delivered " + SpecialMailDisplay.GetDisplayName(currentDeliveryKind) + text; } } } [HarmonyPatch(typeof(MissionDetailsUI), "InsertMissionData")] internal static class SpecialMailMissionDetailsPatch { private static readonly FieldInfo CargoNameField = AccessTools.Field(typeof(MissionDetailsUI), "cargoName"); private static void Postfix(MissionDetailsUI __instance, Mission mission) { SpecialMailKind kind = SpecialMailDisplay.GetKind(mission); if (kind != SpecialMailKind.None) { object? obj = CargoNameField?.GetValue(__instance); TextMesh val = (TextMesh)((obj is TextMesh) ? obj : null); if ((Object)(object)val != (Object)null) { val.text = SpecialMailDisplay.GetDisplayName(kind); } } } } [HarmonyPatch(typeof(ShipItem), "UpdateLookText")] internal static class SpecialMailItemLookTextPatch { private static void Postfix(ShipItem __instance) { Good val = ((__instance != null) ? ((Component)__instance).GetComponent() : null); int num = (((Object)(object)val != (Object)null) ? val.GetMissionIndex() : (-1)); if (num >= 0 && PlayerMissions.missions != null && num < PlayerMissions.missions.Length) { Mission val2 = PlayerMissions.missions[num]; SpecialMailKind kind = SpecialMailDisplay.GetKind(val2); if (kind == SpecialMailKind.GoldenDelivery || kind == SpecialMailKind.UrgentExpressMail) { ((GoPointerButton)__instance).lookText = SpecialMailDisplay.GetDisplayName(kind) + "\nto " + val2.destinationPort.GetPortName() + "\ndue: " + val2.GetDueText(); } } } } internal static class SpecialMailHistory { private sealed class Marker { internal SpecialMailKind Kind { get; } internal Marker(SpecialMailKind kind) { Kind = kind; } } private const string SaveKey = "PostalExpansion.SpecialMail.History.v1"; private static ConditionalWeakTable historyKinds = new ConditionalWeakTable(); internal static bool TryGetKind(LoggedMission mission, out SpecialMailKind kind) { if (mission != null && historyKinds.TryGetValue(mission, out var value)) { kind = value.Kind; return true; } kind = SpecialMailKind.None; return false; } internal static void SavePersistentState() { if (GameState.modData == null) { GameState.modData = new Dictionary(); } List list = new List(); LoggedMission[] array = MissionLog.instance?.loggedMissions; if (array != null) { for (int i = 0; i < array.Length; i++) { LoggedMission val = array[i]; if (TryGetKind(val, out var kind)) { list.Add(string.Join(",", i, (int)kind, val.day, val.goodIndex)); } } } GameState.modData["PostalExpansion.SpecialMail.History.v1"] = string.Join(";", list); } internal static void LoadPersistentState() { ResetRuntimeState(); LoggedMission[] array = MissionLog.instance?.loggedMissions; if (array == null || GameState.modData == null || !GameState.modData.TryGetValue("PostalExpansion.SpecialMail.History.v1", out var value)) { return; } string[] array2 = value.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array2.Length; i++) { string[] array3 = array2[i].Split(new char[1] { ',' }); if (array3.Length == 4 && int.TryParse(array3[0], out var result) && int.TryParse(array3[1], out var result2) && int.TryParse(array3[2], out var result3) && int.TryParse(array3[3], out var result4) && result >= 0 && result < array.Length && Enum.IsDefined(typeof(SpecialMailKind), result2)) { LoggedMission val = array[result]; SpecialMailKind specialMailKind = (SpecialMailKind)result2; if (specialMailKind != SpecialMailKind.None && val != null && val.day == result3 && val.goodIndex == result4) { Mark(val, specialMailKind); } } } MissionLog.instance.UpdateTexts(); } internal static void ResetRuntimeState() { historyKinds = new ConditionalWeakTable(); } internal static void Mark(LoggedMission mission, SpecialMailKind kind) { if (mission != null && kind != SpecialMailKind.None) { historyKinds.Remove(mission); historyKinds.Add(mission, new Marker(kind)); } } } [HarmonyPatch(typeof(MissionLog), "AddToLog")] internal static class SpecialMailHistoryAddPatch { internal struct AddState { internal SpecialMailKind Kind; internal string DestinationName; internal bool ForceSeparateEntry; internal AddState(SpecialMailKind kind, string destinationName) { Kind = kind; DestinationName = destinationName; ForceSeparateEntry = false; } } private const string TemporaryDestinationPrefix = "\u001fPostalExpansion:"; private static void Prefix(MissionLog __instance, int goodIndex, ref string destinationName, out AddState __state) { __state = new AddState(SpecialMailDisplay.CurrentDeliveryKind, destinationName); LoggedMission val = ((__instance.loggedMissions != null && __instance.loggedMissions.Length != 0) ? __instance.loggedMissions[0] : null); if (val != null && val.day == GameState.day && val.goodIndex == goodIndex && !(val.destinationName != destinationName)) { SpecialMailKind kind = SpecialMailKind.None; SpecialMailHistory.TryGetKind(val, out kind); if (kind != __state.Kind) { int kind2 = (int)__state.Kind; destinationName = "\u001fPostalExpansion:" + kind2 + ":" + destinationName; __state.ForceSeparateEntry = true; } } } private static void Postfix(MissionLog __instance, AddState __state) { LoggedMission val = ((__instance.loggedMissions != null && __instance.loggedMissions.Length != 0) ? __instance.loggedMissions[0] : null); if (val != null) { if (__state.ForceSeparateEntry) { val.destinationName = __state.DestinationName; } if (__state.Kind != SpecialMailKind.None) { SpecialMailHistory.Mark(val, __state.Kind); } if (__state.ForceSeparateEntry || __state.Kind != SpecialMailKind.None) { __instance.UpdateTexts(); } } } } [HarmonyPatch(typeof(MissionLog), "UpdateTexts")] internal static class SpecialMailHistoryTextPatch { private static void Postfix(MissionLog __instance) { if (__instance.loggedMissions == null || __instance.texts == null) { return; } int num = Math.Min(__instance.loggedMissions.Length, __instance.texts.Length); for (int i = 0; i < num; i++) { LoggedMission val = __instance.loggedMissions[i]; if (SpecialMailHistory.TryGetKind(val, out var kind)) { __instance.texts[i].text = "day " + val.day + ": " + SpecialMailDisplay.GetDisplayName(kind) + " to " + val.destinationName + " (" + val.deliveredGoodCount + ")"; } } } } internal sealed class SunspireSpyglassMakerMission : RegisteredLetterMissionDefinition { internal static readonly SunspireSpyglassMakerMission Instance = new SunspireSpyglassMakerMission(); private SunspireSpyglassMakerMission() : base("sunspire_spyglass_maker", "Sunspire", 16, new Vector3(37.768f, 6.183f, 133.613f), new Vector3(0f, 67.185f, 0f), new Vector3(1.5f, 1.5f, 1.5f), LetterDeliveryWindow.Day, new int[11] { 5, 4, 9, 15, 26, 28, 20, 12, 22, 21, 0 }, "Spyglass Maker") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 5: return "I'll make this my priority. Thank you for the letter."; case 0: case 4: case 9: case 12: case 15: case 20: case 21: case 22: case 28: return "I already have a two-month queue! Oh God..."; case 26: return "I wonder where they get all these coins..."; default: return UnexpectedOrigin(mission); } } } internal sealed class TurtleIslandPonderingPeakMission : AnonymousLetterMissionDefinition { internal static readonly TurtleIslandPonderingPeakMission Instance = new TurtleIslandPonderingPeakMission(); private TurtleIslandPonderingPeakMission() : base("turtle_island_pondering_peak_tea_merchant", "Turtle Island", 29, new Vector3(2273.436f, 42.729f, 234.471f), new Vector3(0f, 219.358f, 0f), new Vector3(1.5f, 1.5f, 1.5f), 2287.5f, new LetterDeliveryWindow(1f, 4f, "01-04"), new int[1] { 6 }, "Pondering Peak white tea merchant") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { if (LetterMissionDefinition.GetOriginPortIndex(mission) != 6) { return UnexpectedOrigin(mission); } return "You and your (Yawn)... Stupid secrecy. Can i go to sleep now?"; } } internal sealed class TurtleIslandSunShamanMission : RegisteredLetterMissionDefinition { internal static readonly TurtleIslandSunShamanMission Instance = new TurtleIslandSunShamanMission(); private TurtleIslandSunShamanMission() : base("turtle_island_sun_shaman", "Turtle Island", 29, new Vector3(-12.675f, 4.695f, 38f), new Vector3(0f, 49.052f, 0f), new Vector3(2.5f, 2.5f, 2.5f), LetterDeliveryWindow.Day, new int[11] { 24, 25, 2, 5, 21, 6, 27, 17, 4, 13, 31 }, "Sun Shaman") { }//IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) internal override string GetDeliveryDialogue(Mission mission) { switch (LetterMissionDefinition.GetOriginPortIndex(mission)) { case 24: return "....No."; case 2: case 5: return "Sun is good."; case 4: case 6: case 13: case 17: case 21: case 25: case 27: case 31: return "Uh-um."; default: return UnexpectedOrigin(mission); } } } internal static class UrgentExpressMail { internal const int RequiredReputation = 6; internal const float RewardMultiplier = 2f; private const float DueDayBaseSpeed = 3.2f; private const string NamePrefix = "Urgent "; private const string ActiveMissionDataKey = "PostalExpansion.UrgentExpressMail.Active.v1"; private const string ClaimedOfferDataKey = "PostalExpansion.UrgentExpressMail.Claimed.v1"; private static readonly DailySpecialMissionState State = new DailySpecialMissionState("PostalExpansion.UrgentExpressMail.Active.v1", "PostalExpansion.UrgentExpressMail.Claimed.v1", SerializeActiveMission, MatchesSavedMission, ApplyMissionName); internal static bool IsUrgent(Mission mission) { return State.IsMarked(mission); } internal static bool CanOfferAt(Port origin) { return State.CanOfferAt(origin); } internal static void MarkGenerated(Mission mission) { if (IsExpressMission(mission) && !IsUrgent(mission)) { Good component = mission.goodPrefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { MarkRuntime(mission); mission.totalPrice = Mathf.RoundToInt((float)mission.totalPrice * 2f); mission.dueDay = MailDueDateCalculator.Calculate(mission.originPort, mission.destinationPort, component, 3.2f); mission.pricePerKm = (float)mission.totalPrice / mission.distance; } } } internal static void MissionAccepted(Mission mission) { State.MissionAccepted(mission); } internal static void SavePersistentState() { State.SavePersistentState(); } internal static void LoadPersistentState() { State.LoadPersistentState(); } internal static void ResetRuntimeState() { State.ResetRuntimeState(); } private static void MarkRuntime(Mission mission) { State.Mark(mission); ApplyMissionName(mission); } private static void ApplyMissionName(Mission mission) { if (string.IsNullOrEmpty(mission.missionName)) { mission.missionName = "Urgent ".TrimEnd(Array.Empty()); } else if (!mission.missionName.StartsWith("Urgent ", StringComparison.Ordinal)) { mission.missionName = "Urgent " + mission.missionName; } } private static bool IsExpressMission(Mission mission) { if (mission == null || (Object)(object)mission.goodPrefab == (Object)null) { return false; } return PostalMail.IsExpressMail(mission.goodPrefab.GetComponent()); } private static string SerializeActiveMission(int slot, Mission mission) { return string.Join(",", slot, mission.originPort.portIndex, mission.destinationPort.portIndex); } private static bool MatchesSavedMission(Mission mission, string[] fields) { if (fields.Length == 3 && int.TryParse(fields[1], out var result) && int.TryParse(fields[2], out var result2) && IsExpressMission(mission) && mission.originPort.portIndex == result) { return mission.destinationPort.portIndex == result2; } return false; } } internal static class VanillaQuestState { private const int HideoutQuestIndex = 0; private const int CompletedQuestState = -5; internal static bool IsHideoutQuestComplete() { Quests instance = Quests.instance; if ((Object)(object)instance != (Object)null && instance.currentQuests != null && instance.currentQuests.Length != 0) { return instance.currentQuests[0] == -5; } return false; } } }