Decompiled source of CrewScaling Mono v1.0.0

Mods/CrewScaling.Mono.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using CrewScaling;
using FishNet;
using FishNet.Connection;
using FishNet.Object;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using ScheduleOne.Cartel;
using ScheduleOne.Core.Items.Framework;
using ScheduleOne.DevUtilities;
using ScheduleOne.Dialogue;
using ScheduleOne.Economy;
using ScheduleOne.Law;
using ScheduleOne.Levelling;
using ScheduleOne.Map;
using ScheduleOne.Money;
using ScheduleOne.NPCs.CharacterClasses;
using ScheduleOne.Persistence;
using ScheduleOne.PlayerScripts;
using ScheduleOne.Property;
using ScheduleOne.UI;
using ScheduleOne.UI.Phone;
using ScheduleOne.UI.Shop;
using ScheduleOne.Variables;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(CrewScalingMod), "CrewScaling", "1.0.0", "Yousef", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CrewScaling
{
	public enum ScaledSystem
	{
		RankXp,
		CustomerUnlocks,
		CartelInfluence,
		CartelQuota,
		AmbushFrequency,
		AmbushSize,
		PolicePressure,
		DealVolume,
		Laundering,
		SupplierLimits,
		Prices
	}
	public sealed class SystemConfig
	{
		public MelonPreferences_Entry<bool> Enabled;

		public MelonPreferences_Entry<float> K;
	}
	public static class Config
	{
		public static MelonPreferences_Entry<bool> Enabled;

		public static MelonPreferences_Entry<int> DefaultCrewSize;

		public static MelonPreferences_Entry<int> OverrideCrewSize;

		public static MelonPreferences_Entry<bool> DebugLog;

		private static readonly Dictionary<ScaledSystem, SystemConfig> systems = new Dictionary<ScaledSystem, SystemConfig>();

		private static readonly Dictionary<ScaledSystem, (float k, string description)> defaults = new Dictionary<ScaledSystem, (float, string)>
		{
			{
				ScaledSystem.RankXp,
				(0.4f, "XP required per rank tier")
			},
			{
				ScaledSystem.CustomerUnlocks,
				(0.4f, "Difficulty of unlocking new customers")
			},
			{
				ScaledSystem.CartelInfluence,
				(0.28f, "Effort needed to reduce cartel influence in a region (also gates region unlocks)")
			},
			{
				ScaledSystem.CartelQuota,
				(0.5f, "Cartel truce weekly product quota")
			},
			{
				ScaledSystem.AmbushFrequency,
				(0.28f, "How often cartel activities/ambushes happen")
			},
			{
				ScaledSystem.AmbushSize,
				(0.3f, "Number of goons per ambush (capped by spawn points and goon pool)")
			},
			{
				ScaledSystem.PolicePressure,
				(0.25f, "Police intensity growth, patrol size and pursuit escalation speed")
			},
			{
				ScaledSystem.DealVolume,
				(0.5f, "Customer deal opportunities per day")
			},
			{
				ScaledSystem.Laundering,
				(0.75f, "Money laundering capacity per business")
			},
			{
				ScaledSystem.SupplierLimits,
				(0.75f, "Supplier dead-drop credit limit")
			},
			{
				ScaledSystem.Prices,
				(0.05f, "Purchase prices of properties, equipment, seeds, ingredients and vehicles")
			}
		};

		public static void Init()
		{
			MelonPreferences_Category obj = MelonPreferences.CreateCategory("CrewScaling", "Crew Scaling");
			Enabled = obj.CreateEntry<bool>("Enabled", true, "Enabled", "Master switch. When false the mod changes nothing.", false, false, (ValueValidator)null, (string)null);
			DefaultCrewSize = obj.CreateEntry<int>("DefaultCrewSize", 1, "Default crew size", "Crew size (1-4) given to any save that doesn't have one yet: new saves and saves from before the mod was installed. It is locked into the save the first time the host loads it. 1 = vanilla.", false, false, (ValueValidator)null, (string)null);
			OverrideCrewSize = obj.CreateEntry<int>("OverrideCrewSize", 0, "Override crew size", "0 = off. 1-4 = when the host loads ANY save, overwrite that save's stored crew size with this value. Use it to change an existing save, then set it back to 0.", false, false, (ValueValidator)null, (string)null);
			DebugLog = obj.CreateEntry<bool>("DebugLog", false, "Debug logging", "Log the base and scaled value whenever a scaled system is evaluated (each distinct value is logged once).", false, false, (ValueValidator)null, (string)null);
			MelonPreferences_Category val = MelonPreferences.CreateCategory("CrewScaling_Systems", "Crew Scaling - Systems");
			foreach (KeyValuePair<ScaledSystem, (float, string)> @default in defaults)
			{
				string text = @default.Key.ToString();
				systems[@default.Key] = new SystemConfig
				{
					Enabled = val.CreateEntry<bool>(text + "_Enabled", true, text + " enabled", @default.Value.Item2, false, false, (ValueValidator)null, (string)null),
					K = val.CreateEntry<float>(text + "_K", @default.Value.Item1, text + " coefficient", "multiplier = 1 + K * (CrewSize - 1). " + @default.Value.Item2, false, false, (ValueValidator)null, (string)null)
				};
			}
		}

		public static SystemConfig Get(ScaledSystem system)
		{
			return systems[system];
		}
	}
	public static class Crew
	{
		public const string CrewSizeVar = "crewscaling_crewsize";

		public const string XpNormVar = "crewscaling_xpnorm";

		private const string MultVarPrefix = "crewscaling_m_";

		private static readonly ScaledSystem[] AllSystems = (ScaledSystem[])Enum.GetValues(typeof(ScaledSystem));

		private static readonly Dictionary<ScaledSystem, int> syncedMultiplier = new Dictionary<ScaledSystem, int>();

		public const int MinCrew = 1;

		public const int MaxCrew = 4;

		private static bool resolvedThisSession;

		private static float nextPoll;

		private static bool fieldRefreshErrorLogged;

		public static int CrewSize { get; private set; } = 1;

		public static bool IsHost => InstanceFinder.IsServer;

		private static string MultVar(ScaledSystem s)
		{
			return "crewscaling_m_" + s.ToString().ToLowerInvariant();
		}

		public static float Multiplier(ScaledSystem system)
		{
			if (CrewSize <= 1 || !Config.Enabled.Value)
			{
				return 1f;
			}
			if (IsHost)
			{
				return (float)HostMultiplierMilli(system) / 1000f;
			}
			if (!syncedMultiplier.TryGetValue(system, out var value) || value <= 0)
			{
				return 1f;
			}
			return (float)value / 1000f;
		}

		private static int HostMultiplierMilli(ScaledSystem system)
		{
			if (CrewSize <= 1)
			{
				return 1000;
			}
			SystemConfig systemConfig = Config.Get(system);
			if (!systemConfig.Enabled.Value)
			{
				return 1000;
			}
			return Mathf.RoundToInt((1f + Mathf.Max(0f, systemConfig.K.Value) * (float)(CrewSize - 1)) * 1000f);
		}

		public static bool Active(ScaledSystem system)
		{
			return Multiplier(system) != 1f;
		}

		internal static void RegisterVariables(VariableDatabase db)
		{
			Register(db, "crewscaling_crewsize", persistent: true);
			Register(db, "crewscaling_xpnorm", persistent: true);
			ScaledSystem[] allSystems = AllSystems;
			foreach (ScaledSystem s in allSystems)
			{
				Register(db, MultVar(s), persistent: false);
			}
		}

		private static void Register(VariableDatabase db, string name, bool persistent)
		{
			if (!db.VariableDict.ContainsKey(name))
			{
				db.CreateVariable(name, (EVariableType)1, "0", persistent, (EVariableMode)0, (Player)null, (EVariableReplicationMode)1);
			}
		}

		internal static float ReadVar(string name)
		{
			VariableDatabase instance = NetworkSingleton<VariableDatabase>.Instance;
			if ((Object)(object)instance == (Object)null || !instance.VariableDict.ContainsKey(name))
			{
				return 0f;
			}
			BaseVariable obj = instance.VariableDict[name];
			return ((Variable<float>)(object)((obj is NumberVariable) ? obj : null))?.Value ?? 0f;
		}

		internal static void WriteVar(string name, float value)
		{
			VariableDatabase instance = NetworkSingleton<VariableDatabase>.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				instance.SetVariableValue(name, Mathf.RoundToInt(value).ToString(CultureInfo.InvariantCulture), true);
			}
		}

		internal static void Update()
		{
			if (Time.unscaledTime < nextPoll)
			{
				return;
			}
			nextPoll = Time.unscaledTime + 0.5f;
			LoadManager instance = Singleton<LoadManager>.Instance;
			if ((Object)(object)instance == (Object)null || !instance.IsGameLoaded || GameManager.IS_TUTORIAL || (Object)(object)NetworkSingleton<VariableDatabase>.Instance == (Object)null)
			{
				if (resolvedThisSession || CrewSize != 1)
				{
					Log.Info("Left game session; crew scaling inactive.");
				}
				resolvedThisSession = false;
				CrewSize = 1;
				syncedMultiplier.Clear();
				LaunderingAndSupplierScaling.ResetSession();
				PriceScaling.ResetSession();
				DealVolumeScaling.RestoreAll();
				return;
			}
			if (IsHost && !resolvedThisSession)
			{
				resolvedThisSession = true;
				ResolveForLoadedSave();
			}
			int num = Mathf.RoundToInt(ReadVar("crewscaling_crewsize"));
			if (IsHost && num != Mathf.Clamp(num, 1, 4))
			{
				num = Mathf.Clamp(num, 1, 4);
				WriteVar("crewscaling_crewsize", num);
			}
			int num2 = ((num < 1) ? 1 : Mathf.Min(num, 4));
			if (num2 != CrewSize)
			{
				CrewSize = num2;
				Log.Info(string.Format("Crew size is now {0} ({1}).", CrewSize, IsHost ? "host" : "received from host"));
				if (IsHost)
				{
					XpScaling.NormalizeTotalXp();
				}
			}
			SyncMultipliers();
			try
			{
				LaunderingAndSupplierScaling.RefreshLaunderCapacity();
				PriceScaling.RefreshFieldPrices();
				DealVolumeScaling.Refresh();
			}
			catch (Exception ex)
			{
				if (!fieldRefreshErrorLogged)
				{
					Log.Error("Refreshing scaled world values failed: " + ex);
				}
				fieldRefreshErrorLogged = true;
			}
		}

		private static void SyncMultipliers()
		{
			ScaledSystem[] allSystems = AllSystems;
			foreach (ScaledSystem scaledSystem in allSystems)
			{
				if (IsHost)
				{
					int num = HostMultiplierMilli(scaledSystem);
					if (Mathf.RoundToInt(ReadVar(MultVar(scaledSystem))) != num)
					{
						WriteVar(MultVar(scaledSystem), num);
					}
				}
				else
				{
					syncedMultiplier[scaledSystem] = Mathf.RoundToInt(ReadVar(MultVar(scaledSystem)));
				}
			}
		}

		private static void ResolveForLoadedSave()
		{
			int num = Mathf.RoundToInt(ReadVar("crewscaling_crewsize"));
			int value = Config.OverrideCrewSize.Value;
			int num2;
			string arg;
			if (value >= 1 && value <= 4)
			{
				num2 = value;
				arg = "OverrideCrewSize";
			}
			else if (num >= 1)
			{
				num2 = Mathf.Min(num, 4);
				arg = "stored in save";
			}
			else
			{
				num2 = Mathf.Clamp(Config.DefaultCrewSize.Value, 1, 4);
				arg = "save had no crew size; using DefaultCrewSize";
			}
			if (!NetworkSingleton<VariableDatabase>.Instance.VariableDict.ContainsKey("crewscaling_crewsize"))
			{
				Log.Error("Crew size variable is missing - crew size can't be stored in this save, so scaling is OFF. Please report this.");
				return;
			}
			if (num2 != num)
			{
				WriteVar("crewscaling_crewsize", num2);
			}
			Log.Info($"Loaded save crew size = {num2} ({arg}).");
			CrewSize = num2;
			XpScaling.NormalizeTotalXp();
		}
	}
	internal static class Log
	{
		private static Instance logger;

		private static readonly Dictionary<string, string> lastLogged = new Dictionary<string, string>();

		internal static bool On => Config.DebugLog.Value;

		internal static void Init(Instance instance)
		{
			logger = instance;
		}

		internal static void Info(string msg)
		{
			logger.Msg(msg);
		}

		internal static void Warn(string msg)
		{
			logger.Warning(msg);
		}

		internal static void Error(string msg)
		{
			logger.Error(msg);
		}

		internal static void Scaled(ScaledSystem system, string what, object baseValue, object scaledValue)
		{
			if (Config.DebugLog.Value)
			{
				string key = system.ToString() + ":" + what;
				string text = baseValue?.ToString() + "->" + scaledValue;
				if (!lastLogged.TryGetValue(key, out var value) || !(value == text))
				{
					lastLogged[key] = text;
					logger.Msg($"[{system} x{Crew.Multiplier(system):0.###}] {what}: {baseValue} -> {scaledValue}");
				}
			}
		}
	}
	public class CrewScalingMod : MelonMod
	{
		public const string Version = "1.0.0";

		public override void OnInitializeMelon()
		{
			Log.Init(((MelonBase)this).LoggerInstance);
			Config.Init();
			Log.Info("CrewScaling 1.0.0 (Mono).");
			Harmony h = ((MelonBase)this).HarmonyInstance;
			Patching.ApplySystem("CrewSize storage", delegate
			{
				Patching.Postfix(h, AccessTools.Method(typeof(VariableDatabase), "Awake", (Type[])null, (Type[])null), typeof(CrewScalingMod), "VariableDatabaseAwake_Postfix");
			});
			Patching.ApplySystem("RankXp", delegate
			{
				XpScaling.Apply(h);
			});
			Patching.ApplySystem("CustomerUnlocks", delegate
			{
				CustomerUnlockScaling.Apply(h);
			});
			Patching.ApplySystem("CartelInfluence", delegate
			{
				CartelInfluenceScaling.Apply(h);
			});
			Patching.ApplySystem("CartelQuota", delegate
			{
				CartelQuotaScaling.Apply(h);
			});
			Patching.ApplySystem("Ambush", delegate
			{
				AmbushScaling.Apply(h);
			});
			Patching.ApplySystem("PolicePressure", delegate
			{
				PoliceScaling.Apply(h);
			});
			Patching.ApplySystem("DealVolume", delegate
			{
				DealVolumeScaling.Apply(h);
			});
			Patching.ApplySystem("Laundering/Supplier", delegate
			{
				LaunderingAndSupplierScaling.Apply(h);
			});
			Patching.ApplySystem("Prices", delegate
			{
				PriceScaling.Apply(h);
			});
			if (Patching.Failed == 0)
			{
				Log.Info($"All {Patching.Applied} patches applied.");
			}
			else
			{
				Log.Warn($"{Patching.Applied} patches applied, {Patching.Failed} FAILED - see errors above. The game version may have changed.");
			}
		}

		public override void OnUpdate()
		{
			Crew.Update();
		}

		private static void VariableDatabaseAwake_Postfix(VariableDatabase __instance)
		{
			Crew.RegisterVariables(__instance);
		}
	}
	internal static class Patching
	{
		internal static int Applied;

		internal static int Failed;

		internal static T Require<T>(T member, string description) where T : MemberInfo
		{
			if (member == null)
			{
				throw new MissingMemberException("Game member not found: " + description);
			}
			return member;
		}

		internal static MethodInfo FindRpcLogic(Type type, string rpcName, params Type[] parameterTypes)
		{
			return FindGenerated(type, "RpcLogic___" + rpcName + "_", parameterTypes);
		}

		internal static MethodInfo FindServerRpcWriter(Type type, string rpcName, params Type[] parameterTypes)
		{
			return FindGenerated(type, "RpcWriter___Server_" + rpcName + "_", parameterTypes);
		}

		private static MethodInfo FindGenerated(Type type, string prefix, Type[] parameterTypes)
		{
			IEnumerable<MethodInfo> source = from m in AccessTools.GetDeclaredMethods(type)
				where m.Name.StartsWith(prefix, StringComparison.Ordinal)
				select m;
			if (parameterTypes != null && parameterTypes.Length != 0)
			{
				source = source.Where((MethodInfo m) => (from p in m.GetParameters()
					select p.ParameterType).SequenceEqual(parameterTypes));
			}
			List<MethodInfo> list = source.ToList();
			if (list.Count != 1)
			{
				throw new MissingMethodException($"{type.Name}: expected one {prefix}* method, found {list.Count}");
			}
			return list[0];
		}

		internal static void Prefix(Harmony h, MethodBase target, Type patchType, string patchMethod)
		{
			Patch(h, target, patchType, patchMethod, prefix: true);
		}

		internal static void Postfix(Harmony h, MethodBase target, Type patchType, string patchMethod)
		{
			Patch(h, target, patchType, patchMethod, prefix: false);
		}

		private static void Patch(Harmony h, MethodBase target, Type patchType, string patchMethod, bool prefix)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			string text = patchType.Name + "." + patchMethod;
			try
			{
				if (target == null)
				{
					throw new MissingMethodException("target method not found");
				}
				HarmonyMethod val = new HarmonyMethod(AccessTools.Method(patchType, patchMethod, (Type[])null, (Type[])null));
				if (prefix)
				{
					h.Patch(target, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else
				{
					h.Patch(target, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				Applied++;
			}
			catch (Exception ex)
			{
				Failed++;
				Log.Error("Patch " + text + " -> " + target?.DeclaringType?.Name + "." + target?.Name + " FAILED: " + ex.Message);
			}
		}

		internal static void ApplySystem(string name, Action apply)
		{
			try
			{
				apply();
			}
			catch (Exception ex)
			{
				Failed++;
				Log.Error("System " + name + " could not be patched and is disabled: " + ex.Message);
			}
		}
	}
	internal static class DealVolumeScaling
	{
		private sealed class Vanilla
		{
			public CustomerData Data;

			public float MinSpend;

			public float MaxSpend;

			public int MinOrders;

			public int MaxOrders;
		}

		private const ScaledSystem Sys = ScaledSystem.DealVolume;

		private static readonly Dictionary<int, Vanilla> vanilla = new Dictionary<int, Vanilla>();

		private static float appliedMultiplier = 1f;

		internal static void Apply(Harmony h)
		{
		}

		internal static void Refresh()
		{
			float num = Crew.Multiplier(ScaledSystem.DealVolume);
			if (num == 1f && appliedMultiplier == 1f)
			{
				return;
			}
			foreach (Customer item in Customers())
			{
				CustomerData val = (((Object)(object)item != (Object)null) ? item.CustomerData : null);
				if (!((Object)(object)val == (Object)null))
				{
					int instanceID = ((Object)val).GetInstanceID();
					if (!vanilla.TryGetValue(instanceID, out var value))
					{
						value = new Vanilla
						{
							Data = val,
							MinSpend = val.MinWeeklySpend,
							MaxSpend = val.MaxWeeklySpend,
							MinOrders = val.MinOrdersPerWeek,
							MaxOrders = val.MaxOrdersPerWeek
						};
						vanilla[instanceID] = value;
					}
					Set(value, num);
				}
			}
			if (appliedMultiplier != num && Log.On)
			{
				Log.Scaled(ScaledSystem.DealVolume, "customer orders/week and weekly spend multiplier", 1, num);
			}
			appliedMultiplier = num;
		}

		internal static void RestoreAll()
		{
			if (appliedMultiplier == 1f)
			{
				return;
			}
			foreach (Vanilla value in vanilla.Values)
			{
				if ((Object)(object)value.Data != (Object)null)
				{
					Set(value, 1f);
				}
			}
			appliedMultiplier = 1f;
		}

		private static void Set(Vanilla v, float m)
		{
			CustomerData data = v.Data;
			data.MinWeeklySpend = v.MinSpend * m;
			data.MaxWeeklySpend = v.MaxSpend * m;
			data.MinOrdersPerWeek = ((m == 1f) ? v.MinOrders : Mathf.Min(7, Mathf.RoundToInt((float)v.MinOrders * m)));
			data.MaxOrdersPerWeek = ((m == 1f) ? v.MaxOrders : Mathf.Min(7, Mathf.RoundToInt((float)v.MaxOrders * m)));
		}

		private static IEnumerable<Customer> Customers()
		{
			List<Customer>[] array = new List<Customer>[2]
			{
				Customer.UnlockedCustomers,
				Customer.LockedCustomers
			};
			foreach (List<Customer> list in array)
			{
				if (list != null)
				{
					for (int j = 0; j < list.Count; j++)
					{
						yield return list[j];
					}
				}
			}
		}
	}
	internal static class LaunderingAndSupplierScaling
	{
		private static readonly Dictionary<int, float> vanillaCapacity = new Dictionary<int, float>();

		internal static void Apply(Harmony h)
		{
			Patching.Postfix(h, AccessTools.Method(typeof(Supplier), "GetDeadDropLimit", (Type[])null, (Type[])null), typeof(LaunderingAndSupplierScaling), "GetDeadDropLimit_Postfix");
		}

		private static void GetDeadDropLimit_Postfix(ref float __result)
		{
			if (Crew.Active(ScaledSystem.SupplierLimits))
			{
				float num = __result * Crew.Multiplier(ScaledSystem.SupplierLimits);
				Log.Scaled(ScaledSystem.SupplierLimits, "supplier dead-drop credit", Mathf.RoundToInt(__result), Mathf.RoundToInt(num));
				__result = num;
			}
		}

		internal static void RefreshLaunderCapacity()
		{
			float num = Crew.Multiplier(ScaledSystem.Laundering);
			foreach (Business business in Business.Businesses)
			{
				if ((Object)(object)business == (Object)null)
				{
					continue;
				}
				int instanceID = ((Object)business).GetInstanceID();
				if (!vanillaCapacity.TryGetValue(instanceID, out var value))
				{
					if (num == 1f)
					{
						continue;
					}
					value = business.LaunderCapacity;
					vanillaCapacity[instanceID] = value;
				}
				float num2 = ((num == 1f) ? value : Mathf.Round(value * num));
				if (business.LaunderCapacity != num2)
				{
					if (Log.On)
					{
						Log.Scaled(ScaledSystem.Laundering, "launder capacity " + ((Property)business).PropertyName, value, num2);
					}
					business.LaunderCapacity = num2;
				}
			}
		}

		internal static void ResetSession()
		{
			vanillaCapacity.Clear();
		}
	}
	internal static class CartelQuotaScaling
	{
		private const ScaledSystem Sys = ScaledSystem.CartelQuota;

		private static (int min, int max)? vanilla;

		internal static void Apply(Harmony h)
		{
			MethodInfo target = AccessTools.Method(typeof(CartelDealManager), "StartDeal", (Type[])null, (Type[])null);
			Patching.Prefix(h, target, typeof(CartelQuotaScaling), "StartDeal_Prefix");
			Patching.Postfix(h, target, typeof(CartelQuotaScaling), "StartDeal_Postfix");
		}

		private static void StartDeal_Prefix(CartelDealManager __instance)
		{
			if (Crew.Active(ScaledSystem.CartelQuota) && Crew.IsHost)
			{
				if (!vanilla.HasValue)
				{
					vanilla = (__instance.ProductQuantityMin, __instance.ProductQuantityMax);
				}
				float num = Crew.Multiplier(ScaledSystem.CartelQuota);
				__instance.ProductQuantityMin = Mathf.RoundToInt((float)vanilla.Value.min * num);
				__instance.ProductQuantityMax = Mathf.RoundToInt((float)vanilla.Value.max * num);
				Log.Scaled(ScaledSystem.CartelQuota, "quota range", $"{vanilla.Value.min}-{vanilla.Value.max}", $"{__instance.ProductQuantityMin}-{__instance.ProductQuantityMax}");
			}
		}

		private static void StartDeal_Postfix(CartelDealManager __instance)
		{
			if (vanilla.HasValue)
			{
				__instance.ProductQuantityMin = vanilla.Value.min;
				__instance.ProductQuantityMax = vanilla.Value.max;
			}
		}
	}
	internal static class AmbushScaling
	{
		private const int MaxAmbushGoons = 6;

		private static bool inAmbushSpawn;

		internal static void Apply(Harmony h)
		{
			Patching.Postfix(h, AccessTools.Method(typeof(CartelRegionActivities), "GetNewCooldown", new Type[1] { typeof(EMapRegion) }, (Type[])null), typeof(AmbushScaling), "RegionCooldown_Postfix");
			Patching.Postfix(h, AccessTools.Method(typeof(CartelActivities), "GetNewCooldown", new Type[0], (Type[])null), typeof(AmbushScaling), "GlobalCooldown_Postfix");
			MethodInfo target = AccessTools.Method(typeof(Ambush), "SpawnAmbush", (Type[])null, (Type[])null);
			Patching.Prefix(h, target, typeof(AmbushScaling), "SpawnAmbush_Prefix");
			Patching.Postfix(h, target, typeof(AmbushScaling), "SpawnAmbush_Postfix");
			Patching.Prefix(h, AccessTools.Method(typeof(GoonPool), "SpawnMultipleGoons", (Type[])null, (Type[])null), typeof(AmbushScaling), "SpawnMultipleGoons_Prefix");
		}

		private static int ScaleCooldown(int hours, string what)
		{
			if (!Crew.Active(ScaledSystem.AmbushFrequency))
			{
				return hours;
			}
			int num = Mathf.Max(1, Mathf.RoundToInt((float)hours / Crew.Multiplier(ScaledSystem.AmbushFrequency)));
			Log.Scaled(ScaledSystem.AmbushFrequency, what + " cooldown (h)", hours, num);
			return num;
		}

		private static void RegionCooldown_Postfix(ref int __result)
		{
			__result = ScaleCooldown(__result, "regional activity");
		}

		private static void GlobalCooldown_Postfix(ref int __result)
		{
			__result = ScaleCooldown(__result, "global activity");
		}

		private static void SpawnAmbush_Prefix(ref Vector3[] __1)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			if (!Crew.Active(ScaledSystem.AmbushSize) || __1 == null || __1.Length == 0)
			{
				return;
			}
			inAmbushSpawn = true;
			if (__1.Length < 6)
			{
				List<Vector3> list = new List<Vector3>();
				for (int i = 0; i < __1.Length; i++)
				{
					list.Add(__1[i]);
				}
				int num = 0;
				NavMeshHit val3 = default(NavMeshHit);
				while (list.Count < 6)
				{
					Vector3 val = __1[num++ % __1.Length];
					Vector3 val2 = val + Quaternion.Euler(0f, 137f * (float)list.Count, 0f) * Vector3.forward * 1.5f;
					list.Add(NavMesh.SamplePosition(val2, ref val3, 2f, -1) ? ((NavMeshHit)(ref val3)).position : val);
				}
				__1 = list.ToArray();
			}
		}

		private static void SpawnAmbush_Postfix()
		{
			inAmbushSpawn = false;
		}

		private static void SpawnMultipleGoons_Prefix(ref int __1)
		{
			if (inAmbushSpawn && Crew.Active(ScaledSystem.AmbushSize))
			{
				int num = Mathf.Clamp(Mathf.RoundToInt((float)__1 * Crew.Multiplier(ScaledSystem.AmbushSize)), __1, 6);
				Log.Scaled(ScaledSystem.AmbushSize, "ambush goons", __1, num);
				__1 = num;
			}
		}
	}
	internal static class PoliceScaling
	{
		private const ScaledSystem Sys = ScaledSystem.PolicePressure;

		private static float? vanillaIncrease;

		internal static void Apply(Harmony h)
		{
			MethodInfo target = AccessTools.Method(typeof(LawController), "DayPass", (Type[])null, (Type[])null);
			Patching.Prefix(h, target, typeof(PoliceScaling), "DayPass_Prefix");
			Patching.Postfix(h, target, typeof(PoliceScaling), "DayPass_Postfix");
			Patching.Prefix(h, AccessTools.Method(typeof(LawManager), "StartFootpatrol", (Type[])null, (Type[])null), typeof(PoliceScaling), "StartFootpatrol_Prefix");
			Patching.Postfix(h, AccessTools.Method(typeof(PlayerCrimeData), "Update", (Type[])null, (Type[])null), typeof(PoliceScaling), "CrimeDataUpdate_Postfix");
		}

		private static void DayPass_Prefix(LawController __instance)
		{
			if (Crew.Active(ScaledSystem.PolicePressure) && Crew.IsHost)
			{
				if (!vanillaIncrease.HasValue)
				{
					vanillaIncrease = __instance.IntensityIncreasePerDay;
				}
				__instance.IntensityIncreasePerDay = vanillaIncrease.Value * Crew.Multiplier(ScaledSystem.PolicePressure);
				Log.Scaled(ScaledSystem.PolicePressure, "daily law intensity increase", vanillaIncrease.Value, __instance.IntensityIncreasePerDay);
			}
		}

		private static void DayPass_Postfix(LawController __instance)
		{
			if (vanillaIncrease.HasValue)
			{
				__instance.IntensityIncreasePerDay = vanillaIncrease.Value;
			}
		}

		private static void StartFootpatrol_Prefix(ref int __1)
		{
			if (Crew.Active(ScaledSystem.PolicePressure))
			{
				int num = Mathf.Min(Mathf.RoundToInt((float)__1 * Crew.Multiplier(ScaledSystem.PolicePressure)), __1 + 1);
				Log.Scaled(ScaledSystem.PolicePressure, "foot patrol members", __1, num);
				__1 = num;
			}
		}

		private static void CrimeDataUpdate_Postfix(PlayerCrimeData __instance)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Invalid comparison between Unknown and I4
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Invalid comparison between Unknown and I4
			if (Crew.Active(ScaledSystem.PolicePressure) && !((Object)(object)__instance.Player == (Object)null) && ((NetworkBehaviour)__instance.Player).IsOwner && !(__instance.TimeSinceSighted > 1f))
			{
				EPursuitLevel currentPursuitLevel = __instance.CurrentPursuitLevel;
				if ((int)currentPursuitLevel == 2 || (int)currentPursuitLevel == 3)
				{
					__instance.CurrentPursuitLevelDuration += Time.deltaTime * (Crew.Multiplier(ScaledSystem.PolicePressure) - 1f);
				}
			}
		}
	}
	internal static class PriceScaling
	{
		private const ScaledSystem Sys = ScaledSystem.Prices;

		private static readonly Dictionary<int, float> vanillaFieldPrice = new Dictionary<int, float>();

		private static readonly HashSet<EItemCategory> MarkedUpCategories = new HashSet<EItemCategory>
		{
			(EItemCategory)1,
			(EItemCategory)2,
			(EItemCategory)3,
			(EItemCategory)4,
			(EItemCategory)5,
			(EItemCategory)8,
			(EItemCategory)9,
			(EItemCategory)12
		};

		private static readonly Dictionary<int, bool> excludedShop = new Dictionary<int, bool>();

		private static readonly FieldRef<PhoneShopInterface, List<CartEntry>> supplierCart = AccessTools.FieldRefAccess<PhoneShopInterface, List<CartEntry>>("_cart");

		private static DialogueController_Ming[] mingCache;

		private static float nextMingScan;

		internal static void Apply(Harmony h)
		{
			Patching.Postfix(h, AccessTools.PropertyGetter(typeof(ShopListing), "Price"), typeof(PriceScaling), "ShopListingPrice_Postfix");
			Patching.Postfix(h, AccessTools.PropertyGetter(typeof(Listing), "Price"), typeof(PriceScaling), "SupplierListingPrice_Postfix");
			Patching.Postfix(h, AccessTools.Method(typeof(PhoneShopInterface), "GetOrderTotal", (Type[])null, (Type[])null), typeof(PriceScaling), "SupplierOrderTotal_Postfix");
			Patching.Postfix(h, AccessTools.Method(typeof(PhoneShopInterface), "Open", (Type[])null, (Type[])null), typeof(PriceScaling), "SupplierShopOpen_Postfix");
			Patching.Postfix(h, AccessTools.PropertyGetter(typeof(DealershipListing), "price"), typeof(PriceScaling), "VehiclePrice_Postfix");
		}

		private static float MarkUp(float price)
		{
			return Mathf.Round(price * Crew.Multiplier(ScaledSystem.Prices) * 100f) / 100f;
		}

		private static bool IsExcludedShop(ShopInterface shop)
		{
			if ((Object)(object)shop == (Object)null)
			{
				return false;
			}
			int instanceID = ((Object)shop).GetInstanceID();
			if (!excludedShop.TryGetValue(instanceID, out var value))
			{
				value = (shop.ShopName + "|" + shop.ShopCode).IndexOf("arms", StringComparison.OrdinalIgnoreCase) >= 0;
				excludedShop[instanceID] = value;
			}
			return value;
		}

		private static void ShopListingPrice_Postfix(ShopListing __instance, ref float __result)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (Crew.Active(ScaledSystem.Prices) && !((Object)(object)__instance.Item == (Object)null) && MarkedUpCategories.Contains(((BaseItemDefinition)__instance.Item).Category) && !IsExcludedShop(__instance.Shop))
			{
				float num = MarkUp(__result);
				if (Log.On)
				{
					Log.Scaled(ScaledSystem.Prices, "shop price " + ((BaseItemDefinition)__instance.Item).Name, __result, num);
				}
				__result = num;
			}
		}

		private static void SupplierListingPrice_Postfix(Listing __instance, ref float __result)
		{
			if (Crew.Active(ScaledSystem.Prices))
			{
				float num = MarkUp(__result);
				if (Log.On)
				{
					Log.Scaled(ScaledSystem.Prices, "supplier price " + ((BaseItemDefinition)(__instance.Item?)).Name, __result, num);
				}
				__result = num;
			}
		}

		private static void SupplierOrderTotal_Postfix(PhoneShopInterface __instance, ref float __result)
		{
			if (!Crew.Active(ScaledSystem.Prices))
			{
				return;
			}
			List<CartEntry> list = supplierCart.Invoke(__instance);
			if (list == null)
			{
				return;
			}
			float num = 0f;
			for (int i = 0; i < list.Count; i++)
			{
				CartEntry val = list[i];
				if ((Object)(object)val?.Listing?.Item != (Object)null)
				{
					num += MarkUp(val.Listing.Item.BasePurchasePrice) * (float)val.Quantity;
				}
			}
			if (Log.On)
			{
				Log.Scaled(ScaledSystem.Prices, "supplier order total", __result, num);
			}
			__result = num;
		}

		private static void SupplierShopOpen_Postfix(PhoneShopInterface __instance, List<Listing> __3)
		{
			if (!Crew.Active(ScaledSystem.Prices) || __3 == null || (Object)(object)__instance.EntryContainer == (Object)null)
			{
				return;
			}
			RectTransform entryContainer = __instance.EntryContainer;
			int num = ((Transform)entryContainer).childCount - __3.Count;
			for (int i = 0; i < __3.Count && num + i >= 0; i++)
			{
				Transform obj = ((Transform)entryContainer).GetChild(num + i).Find("Price");
				Text val = ((obj != null) ? ((Component)obj).GetComponent<Text>() : null);
				if ((Object)(object)val != (Object)null && (Object)(object)__3[i]?.Item != (Object)null)
				{
					val.text = MoneyManager.FormatAmount(MarkUp(__3[i].Item.BasePurchasePrice), false, false);
				}
			}
		}

		private static void VehiclePrice_Postfix(DealershipListing __instance, ref float __result)
		{
			if (Crew.Active(ScaledSystem.Prices))
			{
				float num = MarkUp(__result);
				if (Log.On)
				{
					Log.Scaled(ScaledSystem.Prices, "vehicle price " + __instance.vehicleCode, __result, num);
				}
				__result = num;
			}
		}

		internal static void RefreshFieldPrices()
		{
			float m = Crew.Multiplier(ScaledSystem.Prices);
			foreach (Property property in Property.Properties)
			{
				if ((Object)(object)property != (Object)null)
				{
					property.Price = ScaledField(((Object)property).GetInstanceID(), property.Price, m, property.PropertyName);
				}
			}
			if (mingCache == null || Time.unscaledTime >= nextMingScan)
			{
				mingCache = Object.FindObjectsOfType<DialogueController_Ming>();
				nextMingScan = Time.unscaledTime + 10f;
			}
			DialogueController_Ming[] array = mingCache;
			foreach (DialogueController_Ming val in array)
			{
				if ((Object)(object)val != (Object)null)
				{
					val.Price = ScaledField(((Object)val).GetInstanceID(), val.Price, m, "motel room price");
				}
			}
		}

		private static float ScaledField(int id, float current, float m, string what)
		{
			if (!vanillaFieldPrice.TryGetValue(id, out var value))
			{
				if (m == 1f)
				{
					return current;
				}
				value = current;
				vanillaFieldPrice[id] = value;
			}
			float num = ((m == 1f) ? value : Mathf.Round(value * m));
			if (current != num && Log.On)
			{
				Log.Scaled(ScaledSystem.Prices, "price of " + what, value, num);
			}
			return num;
		}

		internal static void ResetSession()
		{
			vanillaFieldPrice.Clear();
			excludedShop.Clear();
			mingCache = null;
		}
	}
	internal static class CustomerUnlockScaling
	{
		private const ScaledSystem Sys = ScaledSystem.CustomerUnlocks;

		private static bool inClientSampleRoll;

		internal static void Apply(Harmony h)
		{
			Patching.Postfix(h, AccessTools.Method(typeof(Customer), "GetSampleSuccess", (Type[])null, (Type[])null), typeof(CustomerUnlockScaling), "GetSampleSuccess_Postfix");
			MethodInfo target = AccessTools.Method(typeof(Customer), "SampleConsumed", (Type[])null, (Type[])null);
			Patching.Prefix(h, target, typeof(CustomerUnlockScaling), "SampleConsumed_Prefix");
			Patching.Postfix(h, target, typeof(CustomerUnlockScaling), "SampleConsumed_Postfix");
			Patching.Prefix(h, Patching.FindServerRpcWriter(typeof(LevelManager), "AddXP", typeof(int)), typeof(CustomerUnlockScaling), "ClientAddXP_Prefix");
		}

		private static void SampleConsumed_Prefix()
		{
			inClientSampleRoll = !Crew.IsHost && Crew.CrewSize > 1 && Config.Enabled.Value;
		}

		private static void SampleConsumed_Postfix()
		{
			inClientSampleRoll = false;
		}

		private static bool ClientAddXP_Prefix(int __0)
		{
			if (!inClientSampleRoll)
			{
				return true;
			}
			if (Log.On)
			{
				Log.Scaled(ScaledSystem.CustomerUnlocks, "duplicate client-side sample XP skipped", __0, 0);
			}
			return false;
		}

		private static void GetSampleSuccess_Postfix(ref float __result)
		{
			if (Crew.Active(ScaledSystem.CustomerUnlocks) && !(__result <= 0f))
			{
				float num = __result / Crew.Multiplier(ScaledSystem.CustomerUnlocks);
				if (Log.On)
				{
					Log.Scaled(ScaledSystem.CustomerUnlocks, "sample success chance", __result.ToString("0.###"), num.ToString("0.###"));
				}
				__result = num;
			}
		}
	}
	internal static class CartelInfluenceScaling
	{
		private const ScaledSystem Sys = ScaledSystem.CartelInfluence;

		private static bool suppressClientInfluenceCall;

		internal static void Apply(Harmony h)
		{
			Type typeFromHandle = typeof(CartelInfluence);
			Patching.Prefix(h, Patching.FindRpcLogic(typeFromHandle, "ChangeInfluence", typeof(EMapRegion), typeof(float)), typeof(CartelInfluenceScaling), "ServerChangeInfluence_Prefix");
			Patching.Prefix(h, AccessTools.Method(typeof(Customer), "OnCustomerUnlocked", (Type[])null, (Type[])null), typeof(CartelInfluenceScaling), "OnCustomerUnlocked_Prefix");
			Patching.Postfix(h, AccessTools.Method(typeof(Customer), "OnCustomerUnlocked", (Type[])null, (Type[])null), typeof(CartelInfluenceScaling), "OnCustomerUnlocked_Postfix");
			Patching.Prefix(h, Patching.FindServerRpcWriter(typeFromHandle, "ChangeInfluence", typeof(EMapRegion), typeof(float)), typeof(CartelInfluenceScaling), "ClientChangeInfluence_Prefix");
		}

		private unsafe static void ServerChangeInfluence_Prefix(EMapRegion __0, ref float __1)
		{
			if (Crew.Active(ScaledSystem.CartelInfluence) && !(__1 >= 0f))
			{
				float num = __1 / Crew.Multiplier(ScaledSystem.CartelInfluence);
				if (Log.On)
				{
					Log.Scaled(ScaledSystem.CartelInfluence, "influence change in " + ((object)(*(EMapRegion*)(&__0))/*cast due to .constrained prefix*/).ToString(), __1.ToString("0.####"), num.ToString("0.####"));
				}
				__1 = num;
			}
		}

		private static void OnCustomerUnlocked_Prefix(bool __1)
		{
			suppressClientInfluenceCall = __1 && !Crew.IsHost && Crew.Active(ScaledSystem.CartelInfluence);
		}

		private static void OnCustomerUnlocked_Postfix()
		{
			suppressClientInfluenceCall = false;
		}

		private static bool ClientChangeInfluence_Prefix(EMapRegion __0, float __1)
		{
			if (!suppressClientInfluenceCall)
			{
				return true;
			}
			Log.Scaled(ScaledSystem.CartelInfluence, "duplicate client-side new-customer influence call skipped", __1, 0);
			return false;
		}
	}
	internal static class XpScaling
	{
		private const ScaledSystem Sys = ScaledSystem.RankXp;

		private static int rankCount;

		private static PropertyInfo pXP;

		private static PropertyInfo pTotalXP;

		private static PropertyInfo pRank;

		private static PropertyInfo pTier;

		private static MethodInfo mIncreaseTierNetworked;

		internal static void Apply(Harmony h)
		{
			Type typeFromHandle = typeof(LevelManager);
			rankCount = Enum.GetValues(typeof(ERank)).Length;
			pXP = AccessTools.Property(typeFromHandle, "XP");
			pTotalXP = AccessTools.Property(typeFromHandle, "TotalXP");
			pRank = AccessTools.Property(typeFromHandle, "Rank");
			pTier = AccessTools.Property(typeFromHandle, "Tier");
			mIncreaseTierNetworked = Patching.Require(AccessTools.Method(typeFromHandle, "IncreaseTierNetworked", (Type[])null, (Type[])null), "LevelManager.IncreaseTierNetworked");
			Patching.Prefix(h, Patching.FindRpcLogic(typeFromHandle, "AddXPLocal"), typeof(XpScaling), "AddXPLocal_Prefix");
			Patching.Postfix(h, AccessTools.PropertyGetter(typeFromHandle, "XPToNextTier"), typeof(XpScaling), "XPToNextTier_Postfix");
			Patching.Postfix(h, AccessTools.Method(typeFromHandle, "GetXPForTier", new Type[1] { typeof(ERank) }, (Type[])null), typeof(XpScaling), "GetXPForTier_Postfix");
		}

		internal static int VanillaXpForTier(ERank rank)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			return Mathf.RoundToInt(Mathf.Round(Mathf.Lerp(200f, 2500f, (float)rank / (float)rankCount) / 25f) * 25f);
		}

		internal unsafe static int ScaledXpForTier(ERank rank)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			int num = VanillaXpForTier(rank);
			float num2 = Crew.Multiplier(ScaledSystem.RankXp);
			if (num2 == 1f)
			{
				return num;
			}
			int num3 = Mathf.Max(25, Mathf.RoundToInt((float)num * num2 / 25f) * 25);
			if (Log.On)
			{
				Log.Scaled(ScaledSystem.RankXp, "XP per tier @" + ((object)(*(ERank*)(&rank))/*cast due to .constrained prefix*/).ToString(), num, num3);
			}
			return num3;
		}

		private static int TotalXpAtStartOf(ERank rank, int tier)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between I4 and Unknown
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			for (int i = 0; i < (int)rank; i++)
			{
				num += ScaledXpForTier((ERank)i) * 5;
			}
			return num + ScaledXpForTier(rank) * Mathf.Max(0, tier - 1);
		}

		private static void XPToNextTier_Postfix(LevelManager __instance, ref float __result)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (Crew.Active(ScaledSystem.RankXp))
			{
				__result = ScaledXpForTier(__instance.Rank);
			}
		}

		private static void GetXPForTier_Postfix(ERank __0, ref int __result)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (Crew.Active(ScaledSystem.RankXp))
			{
				__result = ScaledXpForTier(__0);
			}
		}

		private static bool AddXPLocal_Prefix(LevelManager __instance, int __0)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: 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_00e8: 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_00a0: Invalid comparison between Unknown and I4
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			if (!Crew.Active(ScaledSystem.RankXp) || !Crew.IsHost)
			{
				return true;
			}
			bool flag = false;
			try
			{
				flag = true;
				NetworkSingleton<DailySummary>.Instance.AddXP(__0);
				pXP.SetValue(__instance, __instance.XP + __0);
				pTotalXP.SetValue(__instance, __instance.TotalXP + __0);
				__instance.HasChanged = true;
				FullRank fullRank = __instance.GetFullRank();
				bool flag2 = false;
				while (__instance.XP >= ScaledXpForTier(__instance.Rank))
				{
					pXP.SetValue(__instance, __instance.XP - ScaledXpForTier(__instance.Rank));
					int num = __instance.Tier + 1;
					if (num > 5 && (int)__instance.Rank != 10)
					{
						num = 1;
						pRank.SetValue(__instance, (object)(ERank)(__instance.Rank + 1));
					}
					pTier.SetValue(__instance, num);
					flag2 = true;
				}
				__instance.SetData((NetworkConnection)null, __instance.Rank, __instance.Tier, __instance.XP, __instance.TotalXP);
				if (flag2)
				{
					FullRank fullRank2 = __instance.GetFullRank();
					Log.Scaled(ScaledSystem.RankXp, "rank up", fullRank, fullRank2);
					mIncreaseTierNetworked.Invoke(__instance, new object[2] { fullRank, fullRank2 });
				}
				return false;
			}
			catch (Exception ex)
			{
				Log.Error("XP scaling failed" + (flag ? " part-way through a grant" : ", using vanilla for this grant") + ": " + ex);
				return !flag;
			}
		}

		internal static void NormalizeTotalXp()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			if (!Crew.IsHost)
			{
				return;
			}
			LevelManager instance = NetworkSingleton<LevelManager>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			float num = Crew.Multiplier(ScaledSystem.RankXp);
			float num2 = Crew.ReadVar("crewscaling_xpnorm");
			float num3 = ((num2 <= 0f) ? 1f : ((num2 >= 100f) ? (num2 / 1000f) : num2));
			if (!(Mathf.Abs(num - num3) < 0.0001f))
			{
				int num4 = TotalXpAtStartOf(instance.Rank, instance.Tier) + instance.XP;
				if (num4 != instance.TotalXP)
				{
					Log.Info($"Rank XP table changed (x{num3:0.###} -> x{num:0.###}); TotalXP {instance.TotalXP} -> {num4}. Rank {instance.GetFullRank()} kept.");
					instance.SetData((NetworkConnection)null, instance.Rank, instance.Tier, instance.XP, num4);
					instance.HasChanged = true;
				}
				Crew.WriteVar("crewscaling_xpnorm", num * 1000f);
			}
		}
	}
}