Decompiled source of EconomyOverhaul v0.8.5

BepInEx/plugins/EconomyOverhaul/EconomyOverhaul.dll

Decompiled 8 hours ago
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