Decompiled source of EconomyOverhaul v0.8.5
BepInEx/plugins/EconomyOverhaul/EconomyOverhaul.dll
Decompiled 8 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Serialization.Json; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Crest; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("EconomyOverhaul.QA")] [assembly: InternalsVisibleTo("EconomyOverhaul.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("EconomyOverhaul")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.8.5.0")] [assembly: AssemblyInformationalVersion("0.8.5")] [assembly: AssemblyProduct("EconomyOverhaul")] [assembly: AssemblyTitle("EconomyOverhaul")] [assembly: AssemblyVersion("0.8.5.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace EconomyOverhaul { public static class Rules { public const int SinkingLevels = 2; public const double BottomryBoxExtra = 0.1; public const int RentalLevel = 3; public const int RentalPeriod = 20; public const double RentalCleanShare = 0.95; public const double ImmersionPerHour = 12.5; public const double RainAtImmersionRate = 8.0; public const float RainThreshold = 0.1f; public const float RuinedCondition = 19.5f; public const float DryingCeiling = 80f; public const float LuxuryDryingCeiling = 50f; public const double ClearDayDryingPerHour = 1.4457831325301205; internal static double NormalizePenaltyResidue(double value) { if (!(value < 0.0) || !(value >= -1E-08)) { return value; } return 0.0; } internal static int SplitPenalty(double penalty, out double remainder) { int num = checked((int)Math.Floor(penalty + 1E-08)); remainder = NormalizePenaltyResidue(penalty - (double)num); return num; } internal static int RepaymentPenalty(double penalty, int reputation, out double remainder) { if (double.IsNaN(penalty) || penalty < 0.0) { throw new ArgumentOutOfRangeException("penalty"); } if (penalty >= (double)reputation) { remainder = 0.0; return Math.Max(0, reputation); } return SplitPenalty(penalty, out remainder); } public static double CreditLimit(int level) { double[] array = new double[9] { 0.0, 10000.0, 20000.0, 40000.0, 60000.0, 80000.0, 100000.0, 200000.0, 400000.0 }; if (level < 9) { return array[Math.Max(0, level)]; } return double.PositiveInfinity; } public static double Interest(int level) { if (level < 10) { return (10.0 - Clamp(level, 1.0, 9.0)) / 10.0; } return 0.05; } public static Settlement Settle(double debt, int proceeds, double lenderRate, double saleRate) { if (!(lenderRate > 0.0) || !(saleRate > 0.0)) { throw new ArgumentOutOfRangeException("lenderRate"); } if (lenderRate == saleRate) { return new Settlement { Payout = (int)Math.Floor(Math.Max(0.0, (double)proceeds - debt)), Shortfall = (int)Math.Ceiling(Math.Max(0.0, debt - (double)proceeds)) }; } double num = debt * saleRate / lenderRate; return new Settlement { Payout = (((double)proceeds >= num) ? ((int)Math.Floor((double)proceeds - num + 1E-08)) : 0), Shortfall = (((double)proceeds < num) ? ((int)Math.Ceiling(debt - (double)proceeds * lenderRate / saleRate - 1E-08)) : 0) }; } public static double NewlyUnpaid(int before, int after, double rate) { return Math.Max(0.0, Math.Max(0.0, 0.0 - (double)after) - Math.Max(0.0, 0.0 - (double)before)) / rate; } public static double BottomryRate(int level) { return (new double[11] { 0.0, 0.4, 0.4, 0.4, 0.35, 0.3, 0.25, 0.2, 0.15, 0.1, 0.05 })[Math.Max(0, Math.Min(10, level))]; } public static int BottomryTerm(int level) { if (level >= 1) { if (level > 3) { if (level > 6) { return 30; } return 25; } return 20; } return 0; } public static double BottomryLateRate(double baseRate, int daysLate) { return baseRate * (1.0 + 0.1 * (double)Math.Max(0, Math.Min(10, daysLate))); } public static int BottomryOwed(int principal, double rate) { return Round((double)principal * (1.0 + rate)); } public static int BottomryRemote(int principal, double rate) { return Round((double)principal * (1.0 + 2.0 * rate)); } public static int BottomryBoxOwed(int principal, double rate) { return Round((double)principal * (1.0 + rate + 0.1)); } public static int PurchaseDeficit(double cap, int tier) { double num; switch (tier) { default: return 0; case 3: case 4: num = 0.02; break; case 2: num = 0.05; break; case 1: num = 0.1; break; } double num2 = num; return checked((int)Math.Round(cap * num2, MidpointRounding.AwayFromZero)); } public static bool PurchaseOriginAllowed(int port, int good) { if (good == 64) { return port == 33; } return true; } public static double FlowFactor(double supply, double production, double cap, bool saffron, double productionDeficitLimit) { double num = Clamp(1.0 - Math.Abs(supply) / cap, 0.0, 1.0); if (saffron) { return num * num; } if (production > 0.0) { if (!(supply >= 0.0)) { return 1.0 + ((productionDeficitLimit > 0.0) ? Clamp((0.0 - supply) / productionDeficitLimit, 0.0, 1.0) : 1.0); } return num * num; } if (!(supply >= 0.0)) { return num * num; } return 1.0; } public static double Price(double value, double supply, double cap, double surplus, double deficit, int good = 0) { double num = PriceCurves.Response(supply, cap, good); if (PriceCurves.TryProfile(good, out var _)) { surplus = PriceCurves.SurplusMultiplier((float)surplus, good); deficit = PriceCurves.DeficitMultiplier((float)deficit, good); float num2 = (float)Math.Sqrt(1.0 - num); float num3 = 1f - num2 * num2; float num4 = ((supply >= 0.0) ? num3 : (0f - num3)); float num5 = (float)value; return Round(num5 - num5 * (float)((supply >= 0.0) ? surplus : deficit) * num4); } return Math.Round(value * ((supply >= 0.0) ? (1.0 - surplus * num) : (1.0 + deficit * num)), MidpointRounding.ToEven); } public static double Spread(double price) { return 0.005 + -0.0049 * Clamp((price - 1000.0) / 29000.0, 0.0, 1.0); } public static int Quote(double value, double supply, double cap, double surplus, double deficit, bool buy, int good = 0) { double num = Price(value, supply, cap, surplus, deficit, good); return Round(num * (1.0 + (double)(buy ? 1 : (-1)) * Spread(num))); } public static int BookTier(string group) { switch (group) { default: return int.MaxValue; case "chronos": return 5; case "independent": return 4; case "regional": return 3; case "small2": case "medium": case "heavy": case "shuttle": case "happy": return 2; case "small": return 1; } } public static bool BookUnlocked(string group, int home, int[] reps) { if (group == "happy") { int[] array = reps; for (int i = 0; i < array.Length; i++) { if (array[i] >= 2) { return true; } } return false; } if (group == "independent") { int num = 0; int[] array = reps; for (int i = 0; i < array.Length; i++) { if (array[i] >= 4) { num++; } } if (home >= 0) { return num > home; } return false; } if (home >= 0 && home < reps.Length) { return reps[home] >= BookTier(group); } return false; } public static bool BookReleases(int portLevel) { return portLevel > 0; } public static bool BookCovers(string fg, int fh, string eg, int eh) { if (fg == "happy") { return eg != "small"; } if (fg == "independent") { if (!(eg == "independent") || eh < fh) { if (fh == 0) { return eg == "chronos"; } return false; } return true; } if (eg == "happy" || eg == "independent") { return false; } if (fh == eh) { return BookTier(fg) <= BookTier(eg); } return false; } public static bool RumorAccepted(int drink) { if (drink >= 2 && drink <= 13) { return drink != 9; } return false; } public static bool RumorBoatType(int drink) { if (drink >= 2) { return drink <= 8; } return false; } public static bool RumorLoadSize(int drink) { if (drink >= 2) { return drink <= 4; } return false; } public static bool ValidRoute(string group, int origin, int destination, int home, int originRegion, int destinationRegion, double distance) { if (origin == destination || destination == 7 || destination == 33) { return false; } if (group == "chronos") { if (origin != 21 || destination != home) { if (origin == home) { return destination == 21; } return false; } return true; } if (destination == 21) { return false; } switch (group) { case "happy": return origin == 20 != (destination == 20); case "heavy": if (origin != 19 && origin != 26 && origin != 28 && destination != 19 && destination != 26) { return destination == 28; } return true; case "regional": case "independent": if (distance > 140.0) { if (!(group == "independent")) { return originRegion == home != (destinationRegion == home); } return true; } return false; case "shuttle": return (origin >= 22 && origin <= 25) != (destination >= 22 && destination <= 25); default: return true; } } public static double Clamp(double x, double lo, double hi) { return Math.Max(lo, Math.Min(hi, x)); } public static float MissionReward(float profit, float distance, bool world) { if (!world) { return 0.2f * profit + 2f * distance; } return 0.35f * profit + 1.75f * distance; } public static int Round(double x) { return checked((int)Math.Round(x, MidpointRounding.ToEven)); } public static double RentalValue(int price, double goldRate, double localRate) { return (double)price / goldRate * localRate; } public static int RentalDeposit(double value) { return Round(0.2 * value); } public static int RentalDaily(double value) { return Round(0.05 * value); } public static int RentalDays(int startDay, int day, int charges) { int val = day - startDay - 20 * charges; if (charges != 0) { return Math.Max(0, val); } return Math.Max(1, val); } public static int NextRentDay(int startDay, int charges) { return startDay + 20 * (charges + 1); } public static int ShipyardBase(int price) { return (int)Math.Round((float)price * 3.5f); } public static int CleaningFee(int price, float rate) { return (int)Math.Round((float)ShipyardBase(price) * rate * 2.25f); } public static int RepairFee(int price, float rate, float hullDamage, float capacity) { return (int)Math.Round((float)ShipyardBase(price) * rate * hullDamage * capacity * 0.1f); } public static double ConditionFactor(double health, bool alcohol, bool exempt = false) { health = Clamp(health, 0.0, 100.0); if (!exempt && !(alcohol ? (health > 0.0) : (health >= 90.0))) { if (!alcohol) { return 0.1 + health / 100.0; } return 0.1; } return 1.0; } public static bool Ruined(double health) { return health < 19.5; } public static float Ceiling(int good) { if (!PriceCurves.TryProfile(good, out var profile) || profile != PriceProfile.Luxury) { return 80f; } return 50f; } public static float EffectiveRain(float raw) { if (!float.IsNaN(raw) && !float.IsInfinity(raw) && !(raw < 0.1f)) { return raw; } return 0f; } public static bool DamagingImmersion(double depth, double cargoHeight) { if (cargoHeight > 0.0 && depth > 0.0) { return depth >= cargoHeight * 0.05; } return false; } public static double RainDamagePerHour(double rain, double exposed) { return 12.5 * Math.Max(0.0, rain) / 8.0 * Clamp(exposed, 0.0, 1.0); } public static double OpenDryingPerHour(double clouds, double hour) { bool flag = hour >= 6.0 && hour < 17.0; bool flag2 = clouds > 2.0; return 1.4457831325301205 * ((!flag) ? (flag2 ? 0.5 : 0.75) : (flag2 ? 0.75 : 1.0)); } public static double DryingPerHour(double exposed, double clouds, double hour) { return Clamp(exposed, 0.0, 1.0) * OpenDryingPerHour(clouds, hour); } public static float AdvanceCondition(float health, double gameHours, bool noDry, bool waterContact, bool damagingWater, double rain, bool coverKnown, double exposed, double clouds, double hour, float ceiling = 80f) { gameHours = Math.Max(0.0, gameHours); double num = (damagingWater ? 12.5 : 0.0) + (coverKnown ? RainDamagePerHour(rain, exposed) : 0.0); if (num > 0.0) { return (float)Math.Max(0.0, (double)health - gameHours * num); } if (waterContact || rain > 0.0 || noDry || !coverKnown || Ruined(health) || health >= ceiling) { return health; } return (float)Math.Min(ceiling, (double)health + gameHours * DryingPerHour(exposed, clouds, hour)); } public static string HintSuffix(string description, string lookText, int percent, bool loan) { List<string> list = new List<string>(2); if (percent >= 0) { list.Add("Water Damage: " + percent.ToString(CultureInfo.InvariantCulture) + "%"); } if (loan) { list.Add("Respondentia"); } if (list.Count == 0) { return ""; } int num = TextLines(lookText); int num2 = TextLines(description); int num3 = Math.Max(1, num - num2 + 1); if (num >= 2) { num3 = Math.Max(num3, 3 + (list.Count - 1) + ((num - 2) * 10 + 2) / 3 - Math.Max(1, num2) + 1); } return new string('\n', num3) + string.Join("\n", list); } private static int TextLines(string text) { if (string.IsNullOrEmpty(text)) { return 0; } int num = 1; for (int i = 0; i < text.Length; i++) { if (text[i] == '\n') { num++; } } return num; } public static int PlanksPerPercent(int boatPrice, double capacity, double lumberValue) { double num = ((boatPrice > 0 && lumberValue > 0.0) ? ((double)boatPrice * 3.5 * 0.01 * capacity * 0.1 / (lumberValue / 128.0)) : (capacity / 120.0)); if (!double.IsNaN(num) && !(num < 1.5)) { return (int)Math.Min(1000000.0, Math.Round(num, MidpointRounding.AwayFromZero)); } return 1; } public static bool Caulked(double oakum, double damage, double capacity) { if (capacity > 0.0 && damage > 0.0) { return oakum + 0.01 >= Math.Min(0.01, damage) * capacity; } return false; } public static string HullHint(double progress, double damage, int planks) { int num = ((progress >= 1.0) ? 100 : ((progress > 0.0) ? Math.Min(99, (int)Math.Floor(progress * 100.0 + 0.0001)) : 0)); return num + "%\nShip hull damage: " + (damage * 100.0).ToString("0.00", CultureInfo.InvariantCulture) + "%\nPlanks required to fix 1%: " + planks; } public static int PlankLook(bool lumber, int i, int k, int j) { if (!lumber) { return 4 * i + 2 * k + j; } return 8 + 4 * i + j; } } public struct Settlement { public int Payout; public int Shortfall; } internal static class Loans { private static List<CargoState> plundered; internal static int Currency(CargoState loan) { if (!loan.goldLoan) { return loan.region; } return 3; } internal static double Rate(int region) { return Fx.State.smooth[region]; } internal static double Reserved(int region) { return World.State.cargo.Where((CargoState c) => c.loan && c.region == region).Sum((CargoState c) => c.reservation); } internal static double Limit(int region) { return Rules.CreditLimit(Reputations.Level(region)); } internal static double Available(int region) { return Math.Max(0.0, Limit(region) - Reserved(region)); } internal static CargoState Find(ShipItem item) { SaveablePrefab val = (Object.op_Implicit((Object)(object)item) ? ((Component)item).GetComponent<SaveablePrefab>() : null); if (!Object.op_Implicit((Object)(object)val) || !World.Cargo.TryGetValue(val.instanceId, out var value) || !value.loan) { return null; } return value; } internal static string BlockReason(int region, int currency = -1) { if (!World.Ready || World.SaveBlocked) { return "Economy unavailable"; } if (PlayerGold.currency[(currency < 0) ? region : currency] < 0) { return "Negative balance"; } object obj = LoanGate.Reason(region); if (obj == null) { if (!(Available(region) <= 0.0)) { return null; } obj = "No available credit"; } return (string)obj; } internal static void Notify(string text) { if (Object.op_Implicit((Object)(object)NotificationUi.instance)) { NotificationUi.instance.ShowNotification(NoticeText.Wrap(text)); } } internal static int DebtPenalty(double penalty, int region, out double remainder) { return Rules.RepaymentPenalty(penalty, Math.Max(0, Reputations.Points(region)), out remainder); } internal static void Debit(int region, int amount, int currency = -1, TransactionCategory category = (TransactionCategory)13) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) if (amount > 0) { if (currency < 0) { currency = region; } int num = PlayerGold.currency[currency]; int num2 = checked(num - amount); double remainder; int num3 = DebtPenalty(Rules.NewlyUnpaid(num, num2, Rate(currency)) * 0.5 + World.State.fractionalPenalty[region], region, out remainder); PlayerGold.currency[currency] = num2; World.State.fractionalPenalty[region] = remainder; if (num3 > 0) { Reputations.Change(-num3, region); } if (Reputations.Points(region) <= 0) { World.State.fractionalPenalty[region] = 0.0; } if (Object.op_Implicit((Object)(object)DayLogs.instance)) { DayLogs.instance.dayLogs[currency].LogTransaction(-amount, category); } if (Object.op_Implicit((Object)(object)MoneyNotification.instance)) { MoneyNotification.instance.PlayNotif(-amount, currency); } } } internal static void Close(CargoState c) { if (c == null || !c.loan) { return; } c.loan = false; if (!World.State.cargo.Any((CargoState x) => x.loan && x.region == c.region)) { double num = World.State.fractionalPenalty[c.region]; World.State.fractionalPenalty[c.region] = 0.0; double remainder; int num2 = DebtPenalty(Math.Floor(num + 0.5 + 1E-08), c.region, out remainder); if (num2 > 0) { Reputations.Change(-num2, c.region); } } } internal static int SettleSale(ShipItem item, int gross, int currency) { CargoState cargoState = Find(item); if (cargoState == null) { return gross; } Settlement settlement = Rules.Settle((double)cargoState.principal + (double)cargoState.interest, gross, Rate(Currency(cargoState)), Rate(currency)); Debit(cargoState.region, settlement.Shortfall, Currency(cargoState), (TransactionCategory)13); Close(cargoState); return settlement.Payout; } internal static bool Use(ShipItem item) { CargoState cargoState = Find(item); if (cargoState == null) { return true; } if (PlayerGold.currency[Currency(cargoState)] < 0) { Notify("Cannot use financed cargo while its lender-currency balance is negative."); return false; } Collect(cargoState); return true; } internal static void Collect(CargoState loan) { if (loan != null && loan.loan) { Debit(loan.region, checked(loan.principal + loan.interest), Currency(loan), (TransactionCategory)13); Close(loan); } } internal static void Cleanup(ShipItem item) { Collect(Find(item)); item.DestroyItem(); } internal static void Sacrifice(ShipItem item) { Collect(Find(item)); item.DestroyItem(); } internal static void UnexpectedRemoval(ShipItem item) { CartDelivery.Detach(item); Good component = ((Component)item).GetComponent<Good>(); if (Object.op_Implicit((Object)(object)component) && Port.ports != null) { int num = PrefabsDirectory.ItemToGoodIndex(((Component)item).GetComponent<SaveablePrefab>().prefabIndex); Port[] ports = Port.ports; foreach (Port val in ports) { if (!Object.op_Implicit((Object)(object)val)) { continue; } IslandMarket component2 = ((Component)val).GetComponent<IslandMarket>(); if (!Object.op_Implicit((Object)(object)component2)) { continue; } IslandMarketWarehouseArea warehouseArea = component2.GetWarehouseArea(); if (Object.op_Implicit((Object)(object)warehouseArea)) { List<Good> list = Fields.Get<List<Good>>(warehouseArea, "goodsInArea"); if (list != null && list.Remove(component) && num >= 0 && num < component2.currentPlayerGoods.Length) { component2.currentPlayerGoods[num] = Math.Max(0, component2.currentPlayerGoods[num] - 1); } } } } if (!World.Ready || World.Loading || GameState.currentlyLoading) { return; } SaveablePrefab component3 = ((Component)item).GetComponent<SaveablePrefab>(); if (Object.op_Implicit((Object)(object)component3) && component3.GetParentObject() != -2 && component3.GetParentObject() != -3) { CargoState cargoState = Find(item); if (plundered != null && cargoState != null && cargoState.loan) { plundered.Add(cargoState); } else { Close(cargoState); } } } internal static void BeginPlunder() { plundered = new List<CargoState>(); } internal static void EndPlunder() { List<CargoState> list = plundered; plundered = null; if (list != null && list.Count > 0 && World.Ready) { SinkLoans(list.Distinct().ToArray()); } } private static void MarkRemoved(CargoState c) { Close(c); if (World.Removed.Add(c.id)) { World.State.removedCargo.Add(c.id); } } internal static string BankruptcyRefusal(int region) { RentalState current = Rentals.Current; if (current == null || (region >= 0 && current.region != region)) { return null; } return "Return the " + Rentals.Name(current.ship) + " first"; } internal static void Bankruptcy(int region) { if (!World.Ready || World.SaveBlocked) { return; } string text = BankruptcyRefusal(region); if (text != null) { Notify(text); return; } Rentals.Bankruptcy(region); Bottomry.Bankruptcy(region); for (int i = 0; i < 4; i++) { if (region < 0 || i == region) { World.State.fractionalPenalty[i] = 0.0; } } CargoState[] array = World.State.cargo.Where((CargoState c) => c.loan && (region < 0 || c.region == region)).ToArray(); for (int num = 0; num < array.Length; num++) { MarkRemoved(array[num]); } for (int num2 = 0; num2 < 4; num2++) { if (region < 0 || num2 == region) { PlayerGold.currency[num2] = 0; Reputations.Change(-Reputations.Points(num2), num2); World.State.fractionalPenalty[num2] = 0.0; } } PurgeRemoved(); Notify((region < 0) ? "Declared bankruptcy in all regions." : ("Declared bankruptcy in " + BookUI.RegionName(region) + ".")); if (Object.op_Implicit((Object)(object)EconomyUI.instance)) { EconomyUI.instance.RefreshPage(); } } internal static void Sinking(BoatLocalItems boat) { if (!World.Ready) { return; } int parent = ((Component)boat).GetComponent<SaveableObject>().sceneIndex; foreach (CargoCondition item in CargoCondition.Active) { item.CaptureParent(); } SinkLoans(World.State.cargo.Where((CargoState c) => c.loan && c.parent == parent).ToArray()); } internal static void SinkLoans(CargoState[] affected) { if (affected.Length != 0) { Dictionary<int, int> levels = Reputations.Levels(affected.Select((CargoState c) => c.region)); for (int num = 0; num < affected.Length; num++) { MarkRemoved(affected[num]); } Reputations.Sink(levels); LoanGate.StartPause(); } } internal static void PurgeRemoved() { if (!Object.op_Implicit((Object)(object)SaveLoadManager.instance)) { return; } SaveablePrefab[] array = SaveLoadManager.instance.GetCurrentPrefabs().ToArray(); foreach (SaveablePrefab val in array) { if (Object.op_Implicit((Object)(object)val) && World.Removed.Contains(val.instanceId)) { ((Component)val).GetComponent<ShipItem>().DestroyItem(); } } SaveableObject[] currentObjects = SaveLoadManager.instance.GetCurrentObjects(); foreach (SaveableObject val2 in currentObjects) { if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.localItems) && val2.localItems.GetCachedItems() != null) { val2.localItems.GetCachedItems().RemoveAll((SavePrefabData d) => World.Removed.Contains(d.instanceId)); } } CargoRecords.Prune(); } internal static Good Next(IslandMarket m, int g) { EconomyUI instance = EconomyUI.instance; BulkTradeUI bulkTradeUI = (Object.op_Implicit((Object)(object)instance) ? ((Component)instance).GetComponent<BulkTradeUI>() : null); if (Object.op_Implicit((Object)(object)bulkTradeUI) && bulkTradeUI.CartSelected && (Object)(object)Fields.Get<IslandMarket>(instance, "currentIsland") == (Object)(object)m) { string error; CargoCarrier val = CartDelivery.Hired(m, out error); if (!Object.op_Implicit((Object)(object)val)) { return null; } return BulkTrade.SaleGoods(m, g, val).FirstOrDefault(); } return BulkTrade.SaleGoods(m, g).FirstOrDefault(); } internal static int SaleProceedsQuote(EconomyUI ui, IslandMarket market, int goodIndex) { return (int)Fields.Call(ui, "GetSellPrice", market.GetPortIndex(), goodIndex); } internal static void Sell(EconomyUI ui) { BulkTrade.Execute(ui, TradeAction.Sell, BulkTrade.Quantity((GoPointerButton)(object)Fields.Get<EconomyUIButton>(ui, "sellButton"))); } internal static IEnumerable<CodeInstruction> ReplaceDestroy(IEnumerable<CodeInstruction> code, string method) { int count = 0; foreach (CodeInstruction item in code) { if (CodeInstructionExtensions.Calls(item, AccessTools.Method(typeof(ShipItem), "DestroyItem", (Type[])null, (Type[])null))) { item.opcode = OpCodes.Call; item.operand = AccessTools.Method(typeof(Loans), method, (Type[])null, (Type[])null); count++; } yield return item; } if (count != 1) { throw new InvalidOperationException("Cargo removal hook does not match this game version: " + method); } } internal static int PositiveFee(float value) { return Mathf.RoundToInt(Mathf.Max(0f, value)); } } [HarmonyPatch(typeof(EconomyUI), "SellGood")] internal static class WarehouseSalePatch { private static bool Prefix(EconomyUI __instance) { Loans.Sell(__instance); return false; } } [HarmonyPatch(typeof(ShipItem), "DestroyItem")] internal static class RemovalFallbackPatch { private static void Prefix(ShipItem __instance) { Loans.UnexpectedRemoval(__instance); } } [HarmonyPatch(typeof(ItemRigidbody), "FixedUpdate")] internal static class CleanupDebtPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return Loans.ReplaceDestroy(instructions, "Cleanup"); } } [HarmonyPatch(typeof(KiciaAltar), "OnTriggerEnter")] internal static class SacrificeDebtPatch { private static bool Prefix(Collider other) { CargoState cargoState = Loans.Find(((Component)other).GetComponent<ShipItem>()); if (cargoState == null || PlayerGold.currency[Loans.Currency(cargoState)] >= 0) { return true; } Loans.Notify("Cannot sacrifice financed cargo while its lender-currency balance is negative."); return false; } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { return Loans.ReplaceDestroy(instructions, "Sacrifice"); } } [HarmonyPatch(typeof(ShipItemCrate), "UnsealCrate")] internal static class UnsealDebtPatch { private static bool Prefix(ShipItemCrate __instance) { if (!(((ShipItem)__instance).amount <= 0f) && Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<CrateInventory>())) { return Loans.Use((ShipItem)(object)__instance); } return true; } } [HarmonyPatch(typeof(ShipItemBottle), "Drink")] internal static class DrinkDebtPatch { private static bool Prefix(ShipItemBottle __instance) { if (!(((ShipItem)__instance).health <= 0f)) { return Loans.Use((ShipItem)(object)__instance); } return true; } } [HarmonyPatch(typeof(ShipItemSalt), "SaltFood")] internal static class SaltDebtPatch { private static bool Prefix(ShipItemSalt __instance, FoodState food) { if (!(((ShipItem)__instance).amount <= 0f) && !(food.GetRequiredSalt() <= 0f)) { return Loans.Use((ShipItem)(object)__instance); } return true; } } [HarmonyPatch(typeof(ShipItemBottle), "OnItemClick")] internal static class PourDebtPatch { private static bool Prefix(ShipItemBottle __instance, PickupableItem heldItem, ref bool __result) { ShipItemBottle val = (ShipItemBottle)(object)((heldItem is ShipItemBottle) ? heldItem : null); Good component = ((Component)__instance).GetComponent<Good>(); if (!((ShipItem)__instance).sold || (Object.op_Implicit((Object)(object)component) && component.GetMissionIndex() >= 0)) { return true; } ShipItemBottle val2 = __instance; ShipItemBottle val3 = val; if (Object.op_Implicit((Object)(object)val)) { Good component2 = ((Component)val).GetComponent<Good>(); if (!((ShipItem)val).sold || (Object.op_Implicit((Object)(object)component2) && component2.GetMissionIndex() >= 0)) { return true; } if (val.GetCapacity() > __instance.GetCapacity()) { val2 = val; val3 = __instance; } if (((ShipItem)val2).health <= 0f || ((ShipItem)val3).health >= val3.GetCapacity() || (((ShipItem)val3).health > 0f && ((ShipItem)val3).amount != ((ShipItem)val2).amount)) { return true; } } else if (!(heldItem is ShipItemSoup) || ((ShipItem)__instance).amount != 1f || ((ShipItem)__instance).health <= 0f) { return true; } if (Loans.Use((ShipItem)(object)val2)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(BoatLocalItems), "CacheItemsOnSinking")] internal static class SinkingDebtPatch { private static void Prefix(BoatLocalItems __instance) { Loans.Sinking(__instance); } private static void Postfix() { Loans.PurgeRemoved(); } } [HarmonyPatch(typeof(BoatLocalItems), "DestroySunkCargo")] internal static class RemoveFinancedSunkPatch { private static bool Prefix(SaveablePrefab prefab, ref bool __result) { if (!World.Removed.Contains(prefab.instanceId)) { return true; } __result = true; return false; } } [HarmonyPatch] internal static class RecoveryFeePatch { private static MethodBase TargetMethod() { return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(Recovery), "DoRecoverPlayer", (Type[])null, (Type[])null)); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { int count = 0; foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, AccessTools.Method(typeof(Mathf), "RoundToInt", (Type[])null, (Type[])null))) { instruction.operand = AccessTools.Method(typeof(Loans), "PositiveFee", (Type[])null, (Type[])null); count++; } yield return instruction; } if (count != 1) { throw new InvalidOperationException("Recovery fee hook does not match this game version"); } } } [HarmonyPatch(typeof(ShipItemOakum), "OnAltActivate", new Type[] { })] internal static class OakumLoanPatch { private static bool Prefix(ShipItemOakum __instance) { if (!((ShipItem)__instance).sold || !Object.op_Implicit((Object)(object)GameState.currentBoat) || ((ShipItem)__instance).amount <= 0.01f) { return true; } BoatDamage component = ((Component)GameState.currentBoat.parent).GetComponent<BoatDamage>(); if (Object.op_Implicit((Object)(object)component) && !(component.hullDamage * component.waterUnitsCapacity - component.oakum <= 0.01f)) { return Loans.Use((ShipItem)(object)__instance); } return true; } } [HarmonyPatch(typeof(CrateSealUI), "ShowUI")] internal static class SealPromptPatch { private static TextMesh prompt; private static void Postfix(CrateSealUI __instance, ShipItemCrate crate) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)prompt)) { TextMesh componentInChildren = ((Component)__instance).GetComponentInChildren<TextMesh>(true); if (!Object.op_Implicit((Object)(object)componentInChildren)) { return; } prompt = Object.Instantiate<TextMesh>(componentInChildren, ((Component)componentInChildren).transform.parent); ((Object)prompt).name = "Respondentia repayment prompt"; Transform transform = ((Component)prompt).transform; transform.localPosition += Vector3.down * 0.085f; TextMesh obj = prompt; obj.characterSize *= 0.8f; } CargoState cargoState = Loans.Find((ShipItem)(object)crate); ((Component)prompt).gameObject.SetActive(cargoState != null); if (cargoState != null) { prompt.text = "Opening repays " + (cargoState.principal + cargoState.interest).ToString("N0") + " " + PlayerGold.GetCurrencyName(Loans.Currency(cargoState)); } } } internal static class NoticeText { internal const float Fill = 0.92f; private static float width; internal static string Wrap(string text) { NotificationUi instance = NotificationUi.instance; TextMesh mesh = (Object.op_Implicit((Object)(object)instance) ? Fields.Get<TextMesh>(instance, "text") : null); if (!Object.op_Implicit((Object)(object)mesh) || !Object.op_Implicit((Object)(object)mesh.font) || string.IsNullOrEmpty(text)) { return text; } if (width <= 0f) { width = BoxWidth(instance, mesh) * 0.92f; } if (width <= 0f) { return text; } return string.Join("\n", (from line in text.Split(new char[1] { '\n' }) select WrapLine(mesh, line)).ToArray()); } internal static float Measure(TextMesh mesh, string line) { //IL_000e: 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) mesh.font.RequestCharactersInTexture(line, mesh.fontSize, mesh.fontStyle); float num = 0f; CharacterInfo val = default(CharacterInfo); foreach (char c in line) { if (mesh.font.GetCharacterInfo(c, ref val, mesh.fontSize, mesh.fontStyle)) { num += (float)((CharacterInfo)(ref val)).advance * 0.1f * mesh.characterSize; } } return num; } private static string WrapLine(TextMesh mesh, string line) { List<string> list = new List<string>(); string text = ""; string[] array = line.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { string text3 = ((text.Length == 0) ? text2 : (text + " " + text2)); if (text.Length > 0 && Measure(mesh, text3) > width) { list.Add(text); text = text2; } else { text = text3; } } if (text.Length > 0) { list.Add(text); } return string.Join("\n", list.ToArray()); } internal static float BoxWidth(NotificationUi ui, TextMesh mesh) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_009c: Unknown result type (might be due to invalid IL or missing references) GameObject val = Fields.Get<GameObject>(ui, "UI"); if (!Object.op_Implicit((Object)(object)val)) { return 0f; } float num = 0f; Transform val2 = null; MeshFilter[] componentsInChildren = val.GetComponentsInChildren<MeshFilter>(true); foreach (MeshFilter val3 in componentsInChildren) { if ((Object)(object)((Component)val3).gameObject != (Object)(object)((Component)mesh).gameObject && Object.op_Implicit((Object)(object)val3.sharedMesh)) { Bounds bounds = val3.sharedMesh.bounds; if (((Bounds)(ref bounds)).size.x * Scale(((Component)val3).transform, val.transform) > num) { bounds = val3.sharedMesh.bounds; num = ((Bounds)(ref bounds)).size.x * Scale(((Component)val3).transform, val.transform); val2 = ((Component)val3).transform; } } } float num2 = Scale(((Component)mesh).transform, val.transform); if (!Object.op_Implicit((Object)(object)val2) || !(num2 > 0f)) { return 0f; } return num / num2; } private static float Scale(Transform t, Transform root) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) float num = 1f; while (Object.op_Implicit((Object)(object)t) && (Object)(object)t != (Object)(object)root) { num *= Mathf.Abs(t.localScale.x); t = t.parent; } return num; } } internal static class LoanGate { internal const double PauseHours = 168.0; internal static bool Paused => World.State.loanPauseUntil > World.CalendarHour; internal static int PauseDays => (int)Math.Ceiling((World.State.loanPauseUntil - World.CalendarHour) / 24.0 - 1E-09); internal static void StartPause() { World.State.loanPauseUntil = Math.Max(World.State.loanPauseUntil, World.CalendarHour + 168.0); } internal static void EndDefaults() { if (PlayerGold.currency != null) { World.State.defaults.RemoveAll((DefaultBlock d) => PlayerGold.currency[d.currency] >= 0); } } internal static DefaultBlock Default(int region) { EndDefaults(); return World.State.defaults.FirstOrDefault((DefaultBlock d) => d.region == region); } internal static string Reason(int region) { if (Paused) { return "Loans paused: " + Days(PauseDays) + " left (a ship sank)"; } DefaultBlock defaultBlock = Default(region); if (defaultBlock != null) { return "No loans while your " + PlayerGold.GetCurrencyName(defaultBlock.currency) + " are negative"; } if (Reputations.Level(region) >= 1) { return null; } return "Requires reputation level 1"; } internal static string Status(int region) { if (Paused) { return "Paused " + Days(PauseDays); } DefaultBlock defaultBlock = Default(region); if (defaultBlock != null) { return Rentals.Coin(defaultBlock.currency) + " negative"; } if (Reputations.Level(region) >= 1) { return null; } return "Needs reputation 1"; } internal static string Days(int days) { return days + ((days == 1) ? " day" : " days"); } } [Serializable] public sealed class BottomryLoan { public int id; public int pledge; public int region; public int currency; public int principal; public int startDay; public int dueDay; public double baseRate; } [Serializable] public sealed class SeizedPledge { public int pledge; } [Serializable] public sealed class DefaultBlock { public int region; public int currency; } internal sealed class LoanBox { internal Port port; internal Transform island; internal int region; internal string PortName { get { if (!Object.op_Implicit((Object)(object)port)) { return "this port"; } return port.GetPortName(); } } internal static LoanBox Of(Component chest, int region) { PortDude dude = (Object.op_Implicit((Object)(object)chest) ? chest.GetComponentInParent<PortDude>() : null); Port val = ((Object.op_Implicit((Object)(object)dude) && Port.ports != null) ? ((IEnumerable<Port>)Port.ports).FirstOrDefault((Func<Port, bool>)((Port p) => Object.op_Implicit((Object)(object)p) && p.portIndex != 7 && (Object)(object)p.GetDude() == (Object)(object)dude)) : null); if (!Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)chest)) { val = chest.GetComponentInParent<Port>(); } if (!Object.op_Implicit((Object)(object)val)) { return new LoanBox { island = Pledges.Island(chest), region = region }; } return At(val); } internal static LoanBox At(Port port) { return new LoanBox { port = port, island = Pledges.Island((Component)(object)port), region = Reputations.Account(port) }; } } internal static class Pledges { private static readonly Dictionary<PurchasableBoat, KeyValuePair<Vector3, Quaternion>> berths = new Dictionary<PurchasableBoat, KeyValuePair<Vector3, Quaternion>>(); private static PurchasableBoat[] cache; private static int cacheFrame = -1; private static readonly HashSet<PurchasableBoat> waiting = new HashSet<PurchasableBoat>(); internal static PurchasableBoat[] All() { if (cache != null && cacheFrame == Time.frameCount) { return cache; } cacheFrame = Time.frameCount; SaveableObject[] array = (Object.op_Implicit((Object)(object)SaveLoadManager.instance) ? SaveLoadManager.instance.GetCurrentObjects() : null); return cache = (PurchasableBoat[])((array == null) ? ((Array)new PurchasableBoat[0]) : ((Array)(from o in array where Object.op_Implicit((Object)(object)o) && o.sceneIndex > 0 select ((Component)o).GetComponent<PurchasableBoat>() into b where Object.op_Implicit((Object)(object)b) select b).ToArray())); } internal static PurchasableBoat Find(int index) { return ((IEnumerable<PurchasableBoat>)All()).FirstOrDefault((Func<PurchasableBoat, bool>)((PurchasableBoat b) => Index(b) == index)); } internal static int Index(PurchasableBoat b) { return ((Component)b).GetComponent<SaveableObject>().sceneIndex; } internal static bool IsShip(PurchasableBoat b) { if (Object.op_Implicit((Object)(object)b) && !b.isHouse) { return Object.op_Implicit((Object)(object)((Component)b).GetComponent<BoatRefs>()); } return false; } internal static bool Owned(PurchasableBoat b) { if (Object.op_Implicit((Object)(object)b)) { return ((Component)b).GetComponent<SaveableObject>().extraSetting; } return false; } internal static bool Sunk(PurchasableBoat b) { BoatDamage val = (Object.op_Implicit((Object)(object)b) ? ((Component)b).GetComponent<BoatDamage>() : null); if (Object.op_Implicit((Object)(object)val)) { return val.sunk; } return false; } internal static int RentedFrom(PurchasableBoat b) { return Rentals.RegionOf(b); } internal static IEnumerable<PurchasableBoat> OwnShips() { return from b in All() where IsShip(b) && Owned(b) && RentedFrom(b) < 0 && !Sunk(b) select b; } internal static int HouseRegion(int index) { switch (index) { default: return -1; case 203: case 206: return 2; case 202: case 204: return 1; case 201: case 205: return 0; } } internal static string Name(int index) { switch (index) { case 10: return "Dhow"; case 20: return "Sanbuq"; case 30: return "Baghlah"; case 40: return "Cog"; case 50: return "Brig"; case 70: return "Jong"; case 80: return "Junk"; case 90: return "Kakam"; case 201: return "Gold Rock City house"; case 202: return "Dragon Cliffs house"; case 203: return "Fort Aestrin house"; case 204: return "Serpent Isle house"; case 205: return "Oasis house"; case 206: return "Firefly Grotto house"; default: { PurchasableBoat val = Find(index); if (!Object.op_Implicit((Object)(object)val)) { return "ship " + index; } return ModdedName(((Object)val).name); } } } internal static string ModdedName(string name) { name = Regex.Replace(name, "\\(Clone\\)|\\(\\d+\\)", "").Replace("BOAT ", "").Trim(); switch (name) { case "happybayboat": return "Happy Bay boat"; case "DNG Cutter": return "Dinghy cutter"; case "Shroud Large": return "Clipper"; case "Shroud Small": return "Sh'ba"; default: if (name.Length <= 1 || !(name.ToUpperInvariant() == name)) { return name; } return name.Substring(0, 1) + name.Substring(1).ToLowerInvariant(); } } internal static Transform Island(Component c) { if (!Object.op_Implicit((Object)(object)c) || Refs.islands == null) { return null; } IslandSceneryScene componentInParent = c.GetComponentInParent<IslandSceneryScene>(); IslandHorizon componentInParent2 = c.GetComponentInParent<IslandHorizon>(); int num = (Object.op_Implicit((Object)(object)componentInParent) ? componentInParent.parentIslandIndex : (Object.op_Implicit((Object)(object)componentInParent2) ? componentInParent2.islandIndex : (-1))); if (num < 0 || num >= Refs.islands.Length) { return null; } return Refs.islands[num]; } internal static Port PortOf(Transform island) { if (!Object.op_Implicit((Object)(object)island) || Port.ports == null) { return null; } return ((IEnumerable<Port>)Port.ports).FirstOrDefault((Func<Port, bool>)((Port p) => Object.op_Implicit((Object)(object)p) && p.portIndex != 7 && (Object)(object)Island((Component)(object)p) == (Object)(object)island)); } internal static bool TiedAt(PurchasableBoat b, Transform island) { BoatMooringRopes val = (Object.op_Implicit((Object)(object)b) ? ((Component)b).GetComponent<BoatMooringRopes>() : null); if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)island)) { return val.ropes.Any((PickupableBoatMooringRope r) => Object.op_Implicit((Object)(object)r) && r.IsMoored() && Object.op_Implicit((Object)(object)((Component)r).transform.parent) && ((Component)r).transform.parent.IsChildOf(island)); } return false; } internal static Transform TiedPort(PurchasableBoat b) { BoatMooringRopes val = (Object.op_Implicit((Object)(object)b) ? ((Component)b).GetComponent<BoatMooringRopes>() : null); if (!Object.op_Implicit((Object)(object)val)) { return null; } PickupableBoatMooringRope[] ropes = val.ropes; foreach (PickupableBoatMooringRope val2 in ropes) { if (Object.op_Implicit((Object)(object)val2) && val2.IsMoored() && Object.op_Implicit((Object)(object)((Component)val2).transform.parent)) { Transform val3 = Island((Component)(object)((Component)val2).transform.parent); if (Object.op_Implicit((Object)(object)val3) && Object.op_Implicit((Object)(object)PortOf(val3))) { return val3; } } } return null; } internal static bool TieTo(PurchasableBoat b, Transform cleat) { BoatMooringRopes val = (Object.op_Implicit((Object)(object)b) ? ((Component)b).GetComponent<BoatMooringRopes>() : null); GPButtonDockMooring button = (Object.op_Implicit((Object)(object)cleat) ? ((Component)cleat).GetComponent<GPButtonDockMooring>() : null); if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)button)) { return false; } if (Object.op_Implicit((Object)(object)((Joint)button.spring).connectedBody)) { Transform val2 = Island((Component)(object)cleat); button = (Object.op_Implicit((Object)(object)val2) ? ((Component)val2).GetComponentsInChildren<GPButtonDockMooring>().Where(delegate(GPButtonDockMooring c) { //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) if ((Object)(object)c != (Object)(object)button && !Object.op_Implicit((Object)(object)((Joint)c.spring).connectedBody)) { Vector3 val4 = ((Component)c).transform.position - cleat.position; return ((Vector3)(ref val4)).sqrMagnitude < 900f; } return false; }).OrderBy(delegate(GPButtonDockMooring c) { //IL_0006: 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_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) Vector3 val4 = ((Component)c).transform.position - cleat.position; return ((Vector3)(ref val4)).sqrMagnitude; }) .FirstOrDefault() : null); if (!Object.op_Implicit((Object)(object)button)) { return false; } } PickupableBoatMooringRope val3 = val.ropes.Where((PickupableBoatMooringRope r) => Object.op_Implicit((Object)(object)r) && !r.IsMoored()).OrderBy(delegate(PickupableBoatMooringRope r) { //IL_0006: 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) //IL_0020: Unknown result type (might be due to invalid IL or missing references) Vector3 val4 = ((Component)r).transform.position - ((Component)button).transform.position; return ((Vector3)(ref val4)).sqrMagnitude; }).FirstOrDefault(); if (!Object.op_Implicit((Object)(object)val3)) { return false; } val3.MoorTo(button); return true; } internal static bool CleatsUp(PurchasableBoat b) { BoatMooringRopes val = (Object.op_Implicit((Object)(object)b) ? ((Component)b).GetComponent<BoatMooringRopes>() : null); if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.mooringFront) && Object.op_Implicit((Object)(object)val.mooringBack) && Up(val.mooringFront)) { return Up(val.mooringBack); } return false; bool Up(Transform c) { //IL_000e: 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) if (((Component)c).gameObject.activeInHierarchy) { return Mathf.Abs(c.position.y - ((Component)b).transform.position.y) < 15f; } return false; } } internal static void TieHome(PurchasableBoat b) { BoatMooringRopes val = (Object.op_Implicit((Object)(object)b) ? ((Component)b).GetComponent<BoatMooringRopes>() : null); if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.mooringFront) && Object.op_Implicit((Object)(object)val.mooringBack)) { if (!((Component)b).gameObject.activeInHierarchy || !CleatsUp(b)) { waiting.Add(b); return; } waiting.Remove(b); TieTo(b, val.mooringFront); TieTo(b, val.mooringBack); } } internal static void TieWaiting() { PurchasableBoat[] array = waiting.ToArray(); foreach (PurchasableBoat val in array) { BoatMooringRopes val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<BoatMooringRopes>() : null); if (!Object.op_Implicit((Object)(object)val2) || Owned(val) || val2.ropes.Any((PickupableBoatMooringRope x) => Object.op_Implicit((Object)(object)x) && x.IsMoored())) { waiting.Remove(val); } else if (CleatsUp(val)) { TieHome(val); } } } internal static bool BerthTaken(PurchasableBoat b, Vector3 berth) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) return All().Any(delegate(PurchasableBoat s) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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) if ((Object)(object)s != (Object)(object)b && IsShip(s)) { Vector3 val = ((Component)s).transform.position - berth; return ((Vector3)(ref val)).sqrMagnitude < 100f; } return false; }); } internal static void HomeLater(PurchasableBoat b) { if (Object.op_Implicit((Object)(object)b) && !World.State.homeLater.Contains(Index(b))) { World.State.homeLater.Add(Index(b)); } } internal static bool GoingHomeLater(PurchasableBoat b) { if (Object.op_Implicit((Object)(object)b)) { return World.State.homeLater.Contains(Index(b)); } return false; } internal static void SendHomeLater() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) if (!World.Ready || World.Loading || GameState.currentlyLoading || World.State.homeLater.Count == 0) { return; } Vector3 val = (Object.op_Implicit((Object)(object)Refs.observerMirror) ? ((Component)Refs.observerMirror).transform.position : Vector3.zero); int[] array = World.State.homeLater.ToArray(); foreach (int num in array) { PurchasableBoat val2 = Find(num); if (!Object.op_Implicit((Object)(object)val2) || Owned(val2) || !Berth(val2, out var position, out var _)) { World.State.homeLater.Remove(num); } else { if (BerthTaken(val2, position)) { continue; } Vector3 val3 = position - val; if (!(((Vector3)(ref val3)).sqrMagnitude < 1000000f)) { val3 = ((Component)val2).transform.position - val; if (!(((Vector3)(ref val3)).sqrMagnitude < 1000000f)) { World.State.homeLater.Remove(num); ForcedMove.Home(val2); } } } } } internal static void RecordBerths() { //IL_0018: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) Vector3 val = (Object.op_Implicit((Object)(object)FloatingOriginManager.instance) ? FloatingOriginManager.instance.outCurrentOffset : Vector3.zero); PurchasableBoat[] array = All(); foreach (PurchasableBoat b in array) { if (IsShip(b) && !Owned(b) && !berths.ContainsKey(b) && World.State.seized.All((SeizedPledge s) => s.pledge != Index(b))) { berths[b] = new KeyValuePair<Vector3, Quaternion>(((Component)b).transform.position - val, ((Component)b).transform.rotation); } } } internal static bool Berth(PurchasableBoat b, out Vector3 position, out Quaternion rotation) { //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_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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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) position = Vector3.zero; rotation = Quaternion.identity; if (!Object.op_Implicit((Object)(object)b) || !berths.TryGetValue(b, out var value)) { return false; } position = value.Key + (Object.op_Implicit((Object)(object)FloatingOriginManager.instance) ? FloatingOriginManager.instance.outCurrentOffset : Vector3.zero); rotation = value.Value; return true; } internal static GPButtonPurchaseBoat SaleButton(PurchasableBoat b) { GameObject val = (Object.op_Implicit((Object)(object)b) ? Fields.Get<GameObject>(b, "purchaseUI") : null); if (!Object.op_Implicit((Object)(object)val)) { return null; } return val.GetComponentInChildren<GPButtonPurchaseBoat>(true); } internal static void ShowSale(PurchasableBoat b) { GameObject val = Fields.Get<GameObject>(b, "purchaseUI"); if (Object.op_Implicit((Object)(object)val)) { val.SetActive(!Owned(b)); } GPButtonPurchaseBoat val2 = SaleButton(b); if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.priceText)) { val2.priceText.text = "FOR SALE\n\n" + b.price + " gold"; } } internal static void Unown(PurchasableBoat b) { BoatMooringRopes component = ((Component)b).GetComponent<BoatMooringRopes>(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.GetAnchorController())) { component.GetAnchorController().ResetAnchor(); } ((Component)b).GetComponent<SaveableObject>().extraSetting = false; ShowSale(b); if ((Object)(object)GameState.lastOwnedBoat == (Object)(object)((Component)b).transform) { GameState.lastOwnedBoat = (from s in OwnShips() select ((Component)s).transform).FirstOrDefault(); } } } internal static class Bottomry { internal static bool Valid(BottomryLoan l) { if (l != null && l.id > 0 && l.pledge > 0 && l.region >= 0 && l.region < 4 && l.currency >= 0 && l.currency < 4 && l.principal > 0 && l.baseRate > 0.0 && l.baseRate <= 1.0) { return l.dueDay >= l.startDay; } return false; } internal static string Name(int pledge) { return Pledges.Name(pledge); } internal static string Percent(double rate) { return (rate * 100.0).ToString("0.#") + "%"; } internal static int DaysLate(BottomryLoan l) { return Math.Max(0, GameState.day - l.dueDay); } internal static double Rate(BottomryLoan l) { return Rules.BottomryLateRate(l.baseRate, DaysLate(l)); } internal static int Owed(BottomryLoan l) { return Rules.BottomryOwed(l.principal, Rate(l)); } internal static int RemoteCost(BottomryLoan l) { return Rules.BottomryRemote(l.principal, Rate(l)); } internal static int BoxCost(BottomryLoan l, int boxRegion) { if (boxRegion != l.region) { return Rules.BottomryBoxOwed(l.principal, Rate(l)); } return Owed(l); } internal static IEnumerable<BottomryLoan> On(int pledge) { return World.State.bottomry.Where((BottomryLoan l) => l.pledge == pledge); } internal static bool Pledged(int pledge) { return World.State.bottomry.Any((BottomryLoan l) => l.pledge == pledge); } internal static SeizedPledge Seized(int pledge) { return World.State.seized.FirstOrDefault((SeizedPledge s) => s.pledge == pledge); } internal static int LocalCurrency(int region) { if (region != 3) { return region; } return 3; } internal static int LocalPrincipal(PurchasableBoat b, int region) { return Rules.Round(2.0 * (double)b.price / Fx.State.smooth[3] * Fx.State.smooth[LocalCurrency(region)]); } internal static int Payout(int localPrincipal, int region, int currency) { int num = LocalCurrency(region); if (currency == num) { return localPrincipal; } double num2 = FxRules.BoothFee(num, currency, Reputations.Level(region), region == 3); double num3 = Math.Floor((double)localPrincipal * Fx.State.rates[currency] / Fx.State.rates[num] * (1.0 - num2)); if (!(num3 > 2147483647.0)) { return (int)num3; } return int.MaxValue; } internal static string Refusal(PurchasableBoat b, LoanBox box, int currency) { if (!World.Ready || World.SaveBlocked) { return "Economy unavailable"; } string text = BookUI.RegionName(box.region); if (Reputations.Level(box.region) < 1) { return "No loans: reputation 0 in " + text; } if (LoanGate.Paused) { return "Loans paused: " + LoanGate.Days(LoanGate.PauseDays) + " left (a ship sank)"; } DefaultBlock defaultBlock = LoanGate.Default(box.region); if (defaultBlock != null) { return "No loans in " + text + " while your " + PlayerGold.GetCurrencyName(defaultBlock.currency) + " are negative"; } if (PlayerGold.currency[currency] < 0) { return "No loans while your " + PlayerGold.GetCurrencyName(currency) + " are negative"; } int index = Pledges.Index(b); string text2 = Name(index); if (World.State.burned.Contains(index)) { return "The " + text2 + " can never be pledged again"; } if (World.State.bottomry.Any((BottomryLoan l) => l.pledge == index && l.region == box.region)) { return "Already pledged in " + text; } if (b.isHouse) { if (!Pledges.Owned(b)) { return "Not your house"; } if (Pledges.HouseRegion(index) != box.region) { return "A house is pledged in its own region only"; } } else { int num = Pledges.RentedFrom(b); if (!Pledges.Owned(b)) { return "Not your ship"; } if (Pledges.Sunk(b)) { return "The " + text2 + " has sunk"; } if (!Pledges.TiedAt(b, box.island)) { return "The " + text2 + " is not tied here"; } if (num == box.region) { return "Rented from " + text + ": can't be pledged here"; } if (!Pledged(index) && !Pledges.OwnShips().Any((PurchasableBoat s) => (Object)(object)s != (Object)(object)b && !Pledged(Pledges.Index(s)))) { if (num < 0) { return "You need another free ship"; } return "Keep one ship of your own unpledged"; } } if (LocalPrincipal(b, box.region) <= 0) { return "The " + text2 + " is worth nothing to lend on"; } return null; } internal static BottomryLoan Take(PurchasableBoat b, LoanBox box, int currency) { Fx.AdvanceDay(GameState.day); string text = Refusal(b, box, currency); if (text != null) { Loans.Notify(text); return null; } int level = Reputations.Level(box.region); int num = Payout(LocalPrincipal(b, box.region), box.region, currency); if (num <= 0) { Loans.Notify("The " + Name(Pledges.Index(b)) + " is worth nothing to lend on"); return null; } if ((long)PlayerGold.currency[currency] + (long)num > int.MaxValue) { Loans.Notify("Amount exceeds wallet limit"); return null; } BottomryLoan bottomryLoan = new BottomryLoan { id = ++World.State.bottomryNextId, pledge = Pledges.Index(b), region = box.region, currency = currency, principal = num, baseRate = Rules.BottomryRate(level), startDay = GameState.day, dueDay = GameState.day + Rules.BottomryTerm(level) }; World.State.bottomry.Add(bottomryLoan); Receive(currency, num); return bottomryLoan; } private static void Receive(int currency, int amount) { PlayerGold.currency[currency] += amount; if (Object.op_Implicit((Object)(object)DayLogs.instance)) { DayLogs.instance.dayLogs[currency].LogTransaction(amount, (TransactionCategory)13); } if (Object.op_Implicit((Object)(object)MoneyNotification.instance)) { MoneyNotification.instance.PlayNotif(amount, currency); } if (Object.op_Implicit((Object)(object)UISoundPlayer.instance)) { UISoundPlayer.instance.PlayGoldSound(); } Fx.Record(currency, amount, buying: false, TradeSource.Cargo); } private static void Pay(int currency, int amount) { if (amount > 0) { PlayerGold.currency[currency] -= amount; if (Object.op_Implicit((Object)(object)DayLogs.instance)) { DayLogs.instance.dayLogs[currency].LogTransaction(-amount, (TransactionCategory)13); } if (Object.op_Implicit((Object)(object)MoneyNotification.instance)) { MoneyNotification.instance.PlayNotif(-amount, currency); } Fx.Record(currency, amount, buying: true, TradeSource.Cargo); } } internal static bool RepayRemote(BottomryLoan l) { if (l == null || !World.Ready || World.SaveBlocked || !World.State.bottomry.Contains(l)) { return false; } int num = RemoteCost(l); string currencyName = PlayerGold.GetCurrencyName(l.currency); if (PlayerGold.currency[l.currency] < num) { Loans.Notify("Not enough " + currencyName + " to repay the " + Name(l.pledge)); return false; } Pay(l.currency, num); World.State.bottomry.Remove(l); if (Object.op_Implicit((Object)(object)UISoundPlayer.instance)) { UISoundPlayer.instance.PlayGoldSound(); } return true; } internal static bool InShipyard(PurchasableBoat b) { if (!Object.op_Implicit((Object)(object)b) || b.isHouse) { return false; } Shipyard[] array = Resources.FindObjectsOfTypeAll<Shipyard>(); foreach (Shipyard val in array) { if (Object.op_Implicit((Object)(object)val) && ((Component)val).gameObject.activeInHierarchy) { List<GameObject> list = Fields.Get<List<GameObject>>(val, "boatsInTrigger"); if (list != null && list.Contains(((Component)b).gameObject)) { return true; } } } return false; } internal static void RepayAtBox(BottomryLoan l, LoanBox box) { if (l == null || !World.Ready || World.SaveBlocked || !World.State.bottomry.Contains(l)) { return; } PurchasableBoat b = Pledges.Find(l.pledge); if (InShipyard(b)) { Loans.Notify("Move the " + Name(l.pledge) + " out of the shipyard first"); return; } int num = BoxCost(l, box.region); int num2 = Math.Max(0, PlayerGold.currency[l.currency]); if (num2 >= num) { Pay(l.currency, num); World.State.bottomry.Remove(l); if (Object.op_Implicit((Object)(object)UISoundPlayer.instance)) { UISoundPlayer.instance.PlayGoldSound(); } } else { int rest = Math.Max(0, Rules.Round((double)Owed(l) * (1.0 - (double)num2 / (double)num))); Pay(l.currency, num2); World.State.bottomry.Remove(l); Default(b, l, rest); } } internal static int PriceIn(PurchasableBoat b, int currency) { return Rules.Round((double)b.price / Fx.State.smooth[3] * Fx.State.smooth[currency]); } internal static void Default(PurchasableBoat b, BottomryLoan l, int rest) { if (Rentals.Is(b)) { Rentals.TakenByLender(b, l, rest); return; } int num; int num2; if (Object.op_Implicit((Object)(object)b) && Pledges.Owned(b)) { num = ((Seized(l.pledge) == null) ? 1 : 0); if (num != 0) { num2 = rest - Math.Min(rest, PriceIn(b, l.currency)); goto IL_004c; } } else { num = 0; } num2 = rest; goto IL_004c; IL_004c: int num3 = num2; if (num != 0) { Take(b, l.pledge); } Charge(l, num3); string text = ((num3 > 0) ? (num3.ToString("N0") + " " + PlayerGold.GetCurrencyName(l.currency)) : null); if (num != 0) { Loans.Notify("The " + BookUI.RegionName(l.region) + " lender took the " + Name(l.pledge) + ((text != null) ? (": " + text + " still owed") : "")); } else if (text != null) { Loans.Notify("Bottomry on the " + Name(l.pledge) + ": " + text + " charged"); } } internal static void Charge(BottomryLoan l, int amount) { if (amount > 0) { int num = Math.Min(amount, Math.Max(0, PlayerGold.currency[l.currency])); if (num > 0) { Fx.Record(l.currency, num, buying: true, TradeSource.Cargo); } Loans.Debit(l.region, amount, l.currency, (TransactionCategory)13); if (PlayerGold.currency[l.currency] < 0 && !World.State.defaults.Any((DefaultBlock d) => d.region == l.region && d.currency == l.currency)) { World.State.defaults.Add(new DefaultBlock { region = l.region, currency = l.currency }); } } } private static void Take(PurchasableBoat b, int index) { if (Seized(index) == null) { World.State.seized.Add(new SeizedPledge { pledge = index }); } ForcedMove.Release(b); } internal static void AdvanceDay(int day) { if (!World.Ready || World.Loading || World.SaveBlocked || Startup.BlocksGameplay || GameState.currentlyLoading) { return; } LoanGate.EndDefaults(); if (World.State.bottomryDay == 0) { World.State.bottomryDay = day; } bool num = day > World.State.bottomryDay; World.State.bottomryDay = Math.Max(World.State.bottomryDay, day); if (num) { foreach (BottomryLoan item in World.State.bottomry.OrderBy((BottomryLoan x) => x.dueDay)) { int num2 = item.dueDay - day; int num3 = -num2; if (num2 == 1) { Loans.Notify("Bottomry: the " + Name(item.pledge) + " is due tomorrow (" + BookUI.RegionName(item.region) + ")"); } else if (num3 >= 1 && num3 <= 9) { Loans.Notify("Bottomry: the " + Name(item.pledge) + " is " + LoanGate.Days(num3) + " overdue, interest " + Percent(Rate(item))); } else if (num3 == 10) { Loans.Notify("Bottomry: last day before the " + Name(item.pledge) + " is seized"); } } } if (!GameState.playing || GameState.recovering || ForcedMove.Moving || Object.op_Implicit((Object)(object)GameState.currentShipyard)) { return; } BottomryLoan[] array = (from x in World.State.bottomry where day >= x.dueDay + 11 orderby x.dueDay, x.id select x).ToArray(); foreach (BottomryLoan bottomryLoan in array) { if (World.State.bottomry.Contains(bottomryLoan)) { int num5 = Owed(bottomryLoan); int num6 = Math.Min(num5, Math.Max(0, PlayerGold.currency[bottomryLoan.currency])); Pay(bottomryLoan.currency, num6); World.State.bottomry.Remove(bottomryLoan); Default(Pledges.Find(bottomryLoan.pledge), bottomryLoan, num5 - num6); } } } internal static void Sinking(SaveableObject hull, List<ShipItem> ghosts = null) { if (!World.Ready || !Object.op_Implicit((Object)(object)hull)) { return; } if (Rentals.IsIndex(hull.sceneIndex)) { Rentals.Sank(hull, ghosts); return; } int sceneIndex = hull.sceneIndex; BottomryLoan[] array = On(sceneIndex).ToArray(); if (array.Length == 0) { return; } foreach (int item2 in array.Select((BottomryLoan l) => l.region).Distinct()) { int num = Reputations.Level(item2); Reputations.Change(Reputations.Required(item2, Math.Max(0, num - 2)) - Reputations.Points(item2), item2); } BottomryLoan[] array2 = array; foreach (BottomryLoan item in array2) { World.State.bottomry.Remove(item); } if (!World.State.burned.Contains(sceneIndex)) { World.State.burned.Add(sceneIndex); } LoanGate.StartPause(); Loans.Notify("The " + Name(sceneIndex) + " sank: its Bottomry loans are cancelled"); } internal static void Bankruptcy(int region) { BottomryLoan[] array = World.State.bottomry.Where((BottomryLoan l) => region < 0 || l.region == region).ToArray(); foreach (BottomryLoan bottomryLoan in array) { TakeBack(bottomryLoan); World.State.bottomry.Remove(bottomryLoan); if (!World.State.burned.Contains(bottomryLoan.pledge)) { World.State.burned.Add(bottomryLoan.pledge); } PurchasableBoat val = Pledges.Find(bottomryLoan.pledge); if (Object.op_Implicit((Object)(object)val) && Pledges.Owned(val) && Seized(bottomryLoan.pledge) == null) { Take(val, bottomryLoan.pledge); } } } internal static void TakeBack(BottomryLoan l) { Pay(l.currency, Math.Min(l.principal, Math.Max(0, PlayerGold.currency[l.currency]))); } internal static void BoughtBack(PurchasableBoat b) { SeizedPledge seizedPledge = Seized(Pledges.Index(b)); if (seizedPledge != null && Pledges.Owned(b)) { World.State.seized.Remove(seizedPledge); } } internal static void Loaded() { Pledges.RecordBerths(); foreach (SeizedPledge item in World.State.seized) { PurchasableBoat val = Pledges.Find(item.pledge); if (Object.op_Implicit((Object)(object)val)) { ((Component)val).GetComponent<SaveableObject>().extraSetting = false; if ((Object)(object)GameState.lastOwnedBoat == (Object)(object)((Component)val).transform) { GameState.lastOwnedBoat = null; } Pledges.ShowSale(val); BoatMooringRopes component = ((Component)val).GetComponent<BoatMooringRopes>(); if (Object.op_Implicit((Object)(object)component) && !component.ropes.Any((PickupableBoatMooringRope r) => Object.op_Implicit((Object)(object)r) && r.IsMoored()) && Rentals.NearBerth(val, 15f)) { Pledges.TieHome(val); } } } } internal static List<SaveableObject> MarkOwnedForSave() { List<SaveableObject> list = new List<SaveableObject>(); if (!World.Ready) { return list; } foreach (SeizedPledge item in World.State.seized) { PurchasableBoat val = Pledges.Find(item.pledge); SaveableObject val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<SaveableObject>() : null); if (Object.op_Implicit((Object)(object)val2) && !val2.extraSetting && !val.isHouse) { val2.extraSetting = true; list.Add(val2); } } return list; } } [HarmonyPatch] internal static class SeizedSavePatch { private static MethodBase TargetMethod() { return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(SaveLoadManager), "DoSaveGame", (Type[])null, (Type[])null)); } private static void Prefix(out List<SaveableObject> __state) { __state = Bottomry.MarkOwnedForSave(); } private static Exception Finalizer(Exception __exception, List<SaveableObject> __state) { if (__state != null) { foreach (SaveableObject item in __state) { if (Object.op_Implicit((Object)(object)item)) { item.extraSetting = false; } } } return __exception; } } [HarmonyPatch(typeof(GPButtonPurchaseBoat), "OnActivate")] internal static class BuyBackPatch { private static Exception Finalizer(Exception __exception, GPButtonPurchaseBoat __instance) { PurchasableBoat boat = __instance.boat; if (Object.op_Implicit((Object)(object)boat) && World.Ready && Pledges.Owned(boat)) { Bottomry.BoughtBack(boat); } return __exception; } } [HarmonyPatch(typeof(BoatLocalItems), "CacheItemsOnSinking")] internal static class BottomrySinkingPatch { private static void Prefix(BoatLocalItems __instance, out List<ShipItem> __state) { __state = null; SaveableObject hull = ((Component)__instance).GetComponent<SaveableObject>(); if (World.Ready && Object.op_Implicit((Object)(object)hull) && Rentals.IsIndex(hull.sceneIndex) && Object.op_Implicit((Object)(object)SaveLoadManager.instance)) { __state = (from p in SaveLoadManager.instance.GetCurrentPrefabs() where Object.op_Implicit((Object)(object)p) && p.GetParentObject() == hull.sceneIndex select ((Component)p).GetComponent<ShipItem>() into i where Object.op_Implicit((Object)(object)i) select i).ToList(); } } private static void Postfix(BoatLocalItems __instance, List<ShipItem> __state) { Bottomry.Sinking(((Component)__instance).GetComponent<SaveableObject>(), __state); } } internal static class ForcedMove { private sealed class Trip { internal PurchasableBoat ship; internal bool sunk; internal bool restore; internal List<ShipItem> ghosts; } internal static bool Busy; internal static bool Moving; internal const string BlackScreen = "\n\n\nThey've come\nto take your debt"; private static bool movePlayer; private static readonly List<Trip> trips = new List<Trip>(); private static readonly MethodInfo disembark = AccessTools.Method(typeof(PlayerEmbarkerNew), "PlayerDisembark", (Type[])null, (Type[])null); internal static bool Aboard(PurchasableBoat b) { if (Object.op_Implicit((Object)(object)b) && Object.op_Implicit((Object)(object)GameState.currentBoat)) { return GameState.currentBoat.IsChildOf(((Component)b).transform); } return false; } internal static void Release(PurchasableBoat b) { if (b.isHouse) { if (Object.op_Implicit((Object)(object)GameState.currentHouse) && ((Component)GameState.currentHouse).transform.IsChildOf(((Component)b).transform)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(OutOfHouse(b)); } ((Component)b).GetComponent<SaveableObject>().extraSetting = false; Pledges.ShowSale(b); } else { bool player = Aboard(b); Pledges.Unown(b); Queue(player, ((Object)(object)Pledges.TiedPort(b) != (Object)null) ? null : b); } } internal static void Queue(bool player, PurchasableBoat ship, bool sunk = false, bool restore = false, List<ShipItem> ghosts = null) { if (player || Object.op_Implicit((Object)(object)ship)) { movePlayer |= player; if (Object.op_Implicit((Object)(object)ship) && trips.All((Trip t) => (Object)(object)t.ship != (Object)(object)ship)) { trips.Add(new Trip { ship = ship, sunk = sunk, restore = restore, ghosts = ghosts }); } if (!Busy) { Busy = true; ((MonoBehaviour)Plugin.Instance).StartCoroutine(Run()); } } } private static IEnumerator Run() { bool faded = false; try { yield return null; Moving = true; while (movePlayer || trips.Count > 0) { bool player = movePlayer; Trip[] ships = trips.ToArray(); movePlayer = false; trips.Clear(); if (Object.op_Implicit((Object)(object)CurrencyExchangeUI.instance) && CurrencyExchangeUI.instance.uiActive) { CurrencyExchangeUI.instance.CloseUI(); } RentalPanel.Close(); if (GameState.sleeping && Object.op_Implicit((Object)(object)Sleep.instance)) { Sleep.instance.WakeUp(); } faded = true; SetText("\n\n\nThey've come\nto take your debt"); yield return Blackout.FadeTo(1f, 0.3f); if (player) { yield return ToNearestShip(); } Trip[] array = ships; foreach (Trip trip in array) { if (Object.op_Implicit((Object)(object)trip.ship)) { yield return ToBerth(trip); } } yield return Blackout.FadeTo(0f, 0.3f); faded = false; SetText(""); } } finally { Busy = false; Moving = false; movePlayer = false; trips.Clear(); SetText(""); if (faded && Object.op_Implicit((Object)(object)Plugin.Instance)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(Blackout.FadeTo(0f, 0.3f)); } } } private static void SetText(string text) { if (Object.op_Implicit((Object)(object)Sleep.instance) && Object.op_Implicit((Object)(object)Sleep.instance.recoveryText)) { Sleep.instance.recoveryText.text = text; } } internal static IEnumerator ToNearestShip() { Transform view = (Object.op_Implicit((Object)(object)Refs.observerMirror) ? ((Component)Refs.observerMirror).transform : null); Transform body = (Object.op_Implicit((Object)(object)Refs.charController) ? ((Component)Refs.charController).transform : null); if (!Object.op_Implicit((Object)(object)view) || !Object.op_Implicit((Object)(object)body)) { yield break; } PurchasableBoat ship = (from s in Pledges.OwnShips() where !Object.op_Implicit((Object)(object)GameState.currentBoat) || !GameState.currentBoat.IsChildOf(((Component)s).transform) select s).OrderBy(delegate(PurchasableBoat s) { //IL_0006: 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_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) Vector3 val2 = ((Component)s).transform.position - view.position; return ((Vector3)(ref val2)).sqrMagnitude; }).FirstOrDefault(); Transform port = (Object.op_Implicit((Object)(object)ship) ? null : NearestRecoveryPort(view.position)); if (!Object.op_Implicit((Object)(object)ship) && !Object.op_Implicit((Object)(object)port)) { yield break; } bool recovering = GameState.recovering; GameState.recovering = true; try { if (Object.op_Implicit((Object)(object)GameState.currentBoat)) { PlayerEmbarkerNew val = Object.FindObjectOfType<PlayerEmbarkerNew>(); if (Object.op_Implicit((Object)(object)val) && disembark != null) { disembark.Invoke(val, null); } } PutBody(Target() + Vector3.up * 200f); int i; for (i = 0; i < 3; i++) { yield return (object)new WaitForFixedUpdate(); } yield return (object)new WaitForSecondsRealtime(0.5f); float waited = 0f; while (GameState.loadingScenes > 0 && waited < 30f) { yield return null; waited += Time.unscaledDeltaTime; } i = 0; while (Object.op_Implicit((Object)(object)ship) && i < 60 && !((Component)ship).gameObject.activeInHierarchy) { yield return null; i++; } if (Object.op_Implicit((Object)(object)ship) && !((Component)ship).gameObject.activeInHierarchy) { BoatHorizon componentInChildren = ((Component)ship).GetComponentInChildren<BoatHorizon>(true); if (Object.op_Implicit((Object)(object)componentInChildren)) { ((Component)((Component)componentInChildren).transform.parent).gameObject.SetActive(true); } yield return (object)new WaitForFixedUpdate(); } i = 0; while (Object.op_Implicit((Object)(object)ship) && i < 10 && !DeckDrop(ship).HasValue) { yield return (object)new WaitForFixedUpdate(); i++; } PutBody(Target()); if (!Object.op_Implicit((Object)(object)ship)) { body.rotation = port.rotation; } yield return (object)new WaitForSeconds(0.5f); if (!Object.op_Implicit((Object)(object)ship) || !GameState.playing) { yield break; } for (waited = 0f; waited < 1f; waited += Time.deltaTime) { if (Aboard(ship)) { break; } yield return null; } if (!Aboard(ship)) { PutBody(Target()); yield return null; Board(ship); yield return (object)new WaitForSeconds(0.5f); } if (!Aboard(ship)) { PutBody(((Component)ship).transform.position + Vector3.up * 10f); yield return (object)new WaitForSeconds(1f); } } finally { GameState.recovering = recovering; } Vector3 Target() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ship)) { return port.position; } return DropPoint(ship); } } private static void Board(PurchasableBoat ship) { PlayerEmbarkerNew val = Object.FindObjectOfType<PlayerEmbarkerNew>(); BoatEmbarkCollider link = Link(ship); Collider val2 = (Object.op_Implicit((Object)(object)link) ? ((Component)link).GetComponent<Collider>() : null); Collider val3 = (Object.op_Implicit((Object)(object)link) ? ((IEnumerable<Collider>)((Component)link.walkCollider).GetComponentsInChildren<Collider>()).FirstOrDefault((Func<Collider, bool>)((Collider c) => !c.isTrigger && (Object)(object)((Component)c).transform != (Object)(object)link.walkCollider)) : null); if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val3)) { val.ObserverTriggerEnter(val2); val.OnTriggerEnter(val3); } } private static void PutBody(Vector3 position) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) CharacterController charController = Refs.charController; bool enabled = ((Collider)charController).enabled; ((Collider)charController).enabled = false; ((Component)charController).transform.position = position; ((Collider)charController).enabled = enabled; } internal static Vector3 DropPoint(PurchasableBoat ship) { //IL_0032: 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) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) return (Vector3)(((??)DeckDrop(ship)) ?? (((Component)ship).transform.position + Vector3.up * 10f)); } private static Vector3? DeckDrop(PurchasableBoat ship) { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_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_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) BoatEmbarkCollider val = Link(ship); Transform walk = (Object.op_Implicit((Object)(object)val) ? val.walkCollider : null); Transform val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).transform.parent : null); if (Object.op_Implicit((Object)(object)walk) && Object.op_Implicit((Object)(object)val2)) { float[] array = new float[5] { 0f, 1.5f, 3f, 4.5f, 6f }; foreach (float num in array) { Vector2[] array2 = (Vector2[])(object)((num != 0f) ? new Vector2[4] { new Vector2(num, 0f), new Vector2(0f - num, 0f), new Vector2(0f, num), new Vector2(0f, 0f - num) } : new Vector2[1] { Vector2.zero }); for (int j = 0; j < array2.Length; j++) { Vector2 at = array2[j]; RaycastHit? val3 = Surface(walk, at.x, at.y); if (val3.HasValue) { RaycastHit top = val3.Value; float y = walk.InverseTransformPoint(((RaycastHit)(ref top)).point).y; if (((IEnumerable<Vector2>)(object)new Vector2[4] { new Vector2(0.4f, 0f), new Vector2(-0.4f, 0f), new Vector2(0f, 0.4f), new Vector2(0f, -0.4f) }).All(delegate(Vector2 o) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //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) RaycastHit? val4 = Surface(walk, at.x + o.x, at.y + o.y); if (val4.HasValue) { RaycastHit value = val4.Value; if ((Object)(object)((RaycastHit)(ref value)).collider == (Object)(object)((RaycastHit)(ref top)).collider) { Transform obj = walk; value = val4.Value; return Mathf.Abs(obj.InverseTransformPoint(((RaycastHit)(ref value)).point).y - y) <= 0.5f; } } return false; })) { return val2.TransformPoint(walk.InverseTransformPoint(((RaycastHit)(ref top)).point)) + Vector3.up * 1.5f; } } } } } return null; } private static BoatEmbarkCollider Link(PurchasableBoat ship) { return (from c in ((Component)ship).GetComponentsInChildren<BoatEmbarkCollider>(true) where Object.op_Implicit((Object)(object)c.walkCollider) orderby (!((Component)c).CompareTag("EmbarkColPlayer")) ? 1 : 0 select c).FirstOrDefault(); } private static RaycastHit? Surface(Transform walk, float x, float z) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) RaycastHit[] array = (from h in Physics.RaycastAll(walk.TransformPoint(new Vector3(x, 15f, z)), -walk.up, 40f, -1, (QueryTriggerInteraction)1) where ((Component)((RaycastHit)(ref h)).collider).transform.IsChildOf(walk) orderby ((RaycastHit)(ref h)).distance select h).ToArray(); if (array.Length == 0) { return null; } return array[0]; } internal static void Home(PurchasableBoat b) { if (Object.op_Implicit((Object)(object)b) && Object.op_Implicit((Object)(object)Plugin.Instance)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(ToBerth(new Trip { ship = b })); } } private static Transform NearestRecoveryPort(Vector3 from) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) return (from p in (Fields.Find(typeof(Recovery), "ports").GetValue(null) as List<RecoveryPort>)?.Where((RecoveryPort p) => Object.op_Implicit((Object)(object)p)).OrderBy(delegate(RecoveryPort p) { //IL_0006: 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_0011: 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) Vector3 val = ((Component)p).transform.position - @from; return ((Vector3)(ref val)).sqrMagnitude; }) select ((Component)p).transform).FirstOrDefault(); } private static IEnumerator OutOfHouse(PurchasableBoat house) { Transform spot = NearestRecoveryPort(((Component)house).transform.position); if (Object.op_Implicit((Object)(object)spot)) { yield return Blackout.FadeTo(1f, 0.3f); ((Component)Refs.observerMirror).transform.position = spot.position; ((Component)Refs.observerMirror).transform.rotation = spot.rotation; PutBody(spot.position); ((Component)Refs.charController).transform.rotation = spot.rotation; yield return (object)new WaitForFixedUpdate(); yield return Blackout.FadeTo(0f, 0.3f); } } private static IEnumerator ToBerth(Trip trip) { PurchasableBoat b = trip.ship; if (trip.ghosts != null) { foreach (ShipItem ghost in trip.ghosts) { SaveablePrefab val = (Object.op_Implicit((Object)(object)ghost) ? ((Component)ghost).GetComponent<SaveablePrefab>() : null); if (Object.op_Implicit((Object)(object)val) && val.GetParentObject() == -3) { ghost.DestroyItem(); } } } BoatMooringRopes component = ((Component)b).GetComponent<BoatMooringRopes>(); BoatDamage damage = ((Component)b).GetComponent<BoatDamage>(); if (trip.restore && Object.op_Implicit((Object)(object)damage)) { damage.hullDamage = 0f; } Vector3 position; Quaternion rotation; bool known = Pledges.Berth(b, out position, out rotation); bool flag = !trip.sunk && (!known || (trip.restore && Rentals.AtHomeCleats(b))); if (!flag && known && !trip.sunk && Pledges.BerthTaken(b, position)) { Pledges.HomeLater(b); flag = true; } if (!flag) { bool recovering = GameState.recovering; GameState.recovering = true; try { if (Object.op_Implicit((Object)(object)component)) { component.UnmoorAllRopes(); if (Object.op_Implicit((Object)(object)component.GetAnchorController())) { component.GetAnchorController().ResetAnchor(); } } if (Object.op_Implicit((Object)(object)damage)) { damage.waterLevel = 0f; ((Behaviour)damage).enabled = true; } yield return (object)new WaitForFixedUpdate(); yield return (object)new WaitForFixedUpdate(); if (known && Pledges.Berth(b, out position, out rotation)) { ((Component)b).transform.position = position; } if (trip.sunk && Object.op_Implicit((Object)(object)damage)) { Quaternion sinkRotation = damage.GetSinkRotation(); if (sinkRotation.x * sinkRotation.x + sinkRotation.y * sinkRotation.y + sinkRotation.z * sinkRotation.z + sinkRotation.w * sinkRotation.w > 0.5f) { ((Component)b).transform.rotation = sinkRotation; } BoatLocalItems component2 = ((Component)b).GetComponent<BoatLocalItems>(); if (Object.op_Implicit((Object)(object)component2)) { component2.SetItemsLoaded(false); } } yield return (object)new WaitForFixedUpdate(); yield return (object)new WaitForFixedUpdate(); if (known) { ((Component)b).transform.rotation = rotation; } Pledges.TieHome(b); yield return (object)new WaitForFixedUpdate(); yield return (object)new WaitForFixedUpdate(); } finally { GameState.recovering = recovering; } } if (trip.restore) { Rentals.Restore(b); } } } internal static class GameText { internal static GameObject Bare(GameObject source, Transform parent, string name) { GameObject val = Object.Instantiate<GameObject>(source, parent); ((Object)val).name = name; Component[] components = val.GetComponents<Component>(); foreach (Component val2 in components) { if (!(val2 is Transform) && !(val2 is TextMesh) && !(val2 is MeshRenderer)) { Object.DestroyImmediate((Object)(object)val2); } } Transform[] array = ((IEnumerable)val.transform).Cast<Transform>().ToArray(); for (int i = 0; i < array.Length; i++) { Object.DestroyImmediate((Object)(object)((Component)array[i]).gameObject); } return val; } } public sealed class BottomryPage : MonoBehaviour { private sealed class Row { internal PurchasableBoat pledge; internal BottomryLoan loan; } internal static Component OpenedFrom; private CurrencyExchangeUI ui; private Transform root; private Transform top; private Transform bottom; private LoanAction tab; private TextMesh tabText; private TextMesh title; private TextMesh caption; private TextMesh note; private TextMesh terms; private TextMesh ribbon; private readonly LoanAction[] rowCards = new LoanAction[4]; private readonly MeshRenderer[] rowPlates = (MeshRenderer[])(object)new MeshRenderer[4]; private readonly TextMesh[,] rowTexts = new TextMesh[4, 3]; private readonly TextMesh[] coins = (TextMesh[])(object)new TextMesh[4]; private LoanAction previous; private LoanAction next; private TextMesh previousLabel; private TextMesh nextLabel; private MeshRenderer ribbonPlate; private Material ribbonMaterial; private Material ribbonShared; private Color ribbonColor; private readonly List<GameObject> hidden = new List<GameObject>(); private readonly List<GameObject> shown = new List<GameObject>(); private string ribbonOriginal; private Material normalRow; private Material selectedRow; private List<Row> rows = new List<Row>(); private int selected; private int page; private int currency = -1; private int shownCost = -1; private LoanBox box; private float timer; private bool ready; internal bool Open { get; private set; } internal static BottomryPage For(CurrencyExchangeUI ui) { BottomryPage bottomryPage = ((Component)ui).GetComponent<BottomryPage>(); if (!Object.op_Implicit((Object)(object)bottomryPage)) { bottomryPage = ((Component)ui).gameObject.AddComponent<BottomryPage>(); bottomryPage.ui = ui; } return bottomryPage; } private void Build() { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Un