Decompiled source of DealersRetaliate v1.0.0

Mods/DealersRetaliate.dll

Decompiled a day 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.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using DealersRetaliate;
using HarmonyLib;
using Il2CppFishNet;
using Il2CppFishNet.Connection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppScheduleOne;
using Il2CppScheduleOne.Cartel;
using Il2CppScheduleOne.Core.Items.Framework;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Economy;
using Il2CppScheduleOne.GameTime;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.Map;
using Il2CppScheduleOne.Messaging;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.Persistence;
using Il2CppScheduleOne.Product;
using Il2CppScheduleOne.Product.Packaging;
using Il2CppScheduleOne.Quests;
using Il2CppScheduleOne.Storage;
using Il2CppScheduleOne.UI;
using Il2CppScheduleOne.UI.Compass;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using MelonLoader;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(/*Could not decode attribute arguments.*/)]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: MelonInfo(typeof(Mod), "Dealers Retaliate", "1.0.0", "Primal", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : System.Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(/*Could not decode attribute arguments.*/)]
	internal sealed class NullableAttribute : System.Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(/*Could not decode attribute arguments.*/)]
	internal sealed class NullableContextAttribute : System.Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(/*Could not decode attribute arguments.*/)]
	internal sealed class RefSafetyRulesAttribute : System.Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DealersRetaliate
{
	public static class DealerProfiles
	{
		public static readonly string[] Dealers = new string[6] { "benji_coleman", "molly_presley", "brad_crosby", "jane_lucero", "wei_long", "leo_rivers" };

		public static readonly string[] Categories = new string[4] { "weed", "shroom", "meth", "cocaine" };

		public static readonly string[] Strains = new string[4] { "ogkush", "sourdiesel", "greencrack", "granddaddypurple" };

		public static int[] ProductWeights(string dealer)
		{
			if (!(dealer == "benji_coleman"))
			{
				if (!(dealer == "molly_presley"))
				{
					if (!(dealer == "brad_crosby"))
					{
						if (!(dealer == "jane_lucero"))
						{
							if (!(dealer == "wei_long"))
							{
								if (dealer == "leo_rivers")
								{
									int[] array = new int[4];
									RuntimeHelpers.InitializeArray((System.Array)array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
									return array;
								}
								int[] array2 = new int[4];
								RuntimeHelpers.InitializeArray((System.Array)array2, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
								return array2;
							}
							int[] array3 = new int[4];
							RuntimeHelpers.InitializeArray((System.Array)array3, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
							return array3;
						}
						int[] array4 = new int[4];
						RuntimeHelpers.InitializeArray((System.Array)array4, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
						return array4;
					}
					int[] array5 = new int[4];
					RuntimeHelpers.InitializeArray((System.Array)array5, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
					return array5;
				}
				int[] array6 = new int[4];
				RuntimeHelpers.InitializeArray((System.Array)array6, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
				return array6;
			}
			int[] array7 = new int[4];
			RuntimeHelpers.InitializeArray((System.Array)array7, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			return array7;
		}

		public static int[] QualityWeights(string dealer, string category)
		{
			if (!(dealer == "wei_long"))
			{
				if (!(dealer == "leo_rivers"))
				{
					if (!(dealer == "benji_coleman"))
					{
						if (!(dealer == "molly_presley"))
						{
							if (!(dealer == "brad_crosby"))
							{
								if (dealer == "jane_lucero")
								{
									int[] array = new int[5];
									RuntimeHelpers.InitializeArray((System.Array)array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
									return array;
								}
								int[] array2 = new int[5];
								RuntimeHelpers.InitializeArray((System.Array)array2, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
								return array2;
							}
							int[] array3 = new int[5];
							RuntimeHelpers.InitializeArray((System.Array)array3, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
							return array3;
						}
						int[] array4 = new int[5];
						RuntimeHelpers.InitializeArray((System.Array)array4, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
						return array4;
					}
					int[] array5 = new int[5];
					RuntimeHelpers.InitializeArray((System.Array)array5, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
					return array5;
				}
				if (category == "cocaine")
				{
					int[] array6 = new int[5];
					RuntimeHelpers.InitializeArray((System.Array)array6, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
					return array6;
				}
				int[] array7 = new int[5];
				RuntimeHelpers.InitializeArray((System.Array)array7, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
				return array7;
			}
			if (category == "cocaine")
			{
				int[] array8 = new int[5];
				RuntimeHelpers.InitializeArray((System.Array)array8, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
				return array8;
			}
			int[] array9 = new int[5];
			RuntimeHelpers.InitializeArray((System.Array)array9, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			return array9;
		}

		public static int WeightedIndex(System.Collections.Generic.IReadOnlyList<int> weights, double roll)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (((System.Collections.Generic.IReadOnlyCollection<int>)weights).Count == 0 || Enumerable.Any<int>((System.Collections.Generic.IEnumerable<int>)weights, (Func<int, bool>)((int w) => w < 0)) || Enumerable.Sum((System.Collections.Generic.IEnumerable<int>)weights) <= 0 || double.IsNaN(roll) || roll < 0.0 || roll >= 1.0)
			{
				throw new ArgumentOutOfRangeException("weights");
			}
			double num = roll * (double)Enumerable.Sum((System.Collections.Generic.IEnumerable<int>)weights);
			for (int num2 = 0; num2 < ((System.Collections.Generic.IReadOnlyCollection<int>)weights).Count; num2++)
			{
				num -= (double)weights[num2];
				if (num < 0.0)
				{
					return num2;
				}
			}
			return ((System.Collections.Generic.IReadOnlyCollection<int>)weights).Count - 1;
		}

		public static string PickCategory(string dealer, System.Collections.Generic.IReadOnlyList<string> available, double roll)
		{
			int[] weights = ProductWeights(dealer);
			return available[WeightedIndex(Enumerable.ToArray<int>(Enumerable.Select<string, int>((System.Collections.Generic.IEnumerable<string>)available, (Func<string, int>)((string c) => weights[System.Array.IndexOf<string>(Categories, c)]))), roll)];
		}
	}
	public sealed class Mod : MelonMod
	{
		private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
		{
			WriteIndented = true
		};

		private Settings settings = new Settings();

		private SaveState? state;

		private string saveFolder = "";

		private bool disabled;

		private float nextPoll;

		private int lastDeliveryMinute = -1;

		private SaveManager? hookedSaveManager;

		private UnityAction? saveCompleteAction;

		private readonly LootCompassMarkers lootMarkers = new LootCompassMarkers();

		private bool markersFailed;

		private readonly LootObjectives lootObjectives = new LootObjectives();

		private float nextObjectiveRetry;

		private string lastObjectiveError = "";

		private int diagnosticDay = -1;

		private readonly HashSet<string> scheduleNotices = new HashSet<string>();

		private string StatePath => Path.Combine(saveFolder, "ModData", "DealersRetaliate.json");

		public override void OnInitializeMelon()
		{
			try
			{
				settings = new Settings();
				settings = settings.ApplyBuildPolicy();
				settings.Validate();
				((MelonBase)this).LoggerInstance.Msg("v1.0.0 loaded. Autonomous retaliation; host authority; no S1API dependency.");
				((MelonBase)this).LoggerInstance.Msg("Balanced edition: fixed balance, hostile cartel and daily schedule. No editable config or testing shortcuts; legacy test configs are ignored.");
				((MelonBase)this).LoggerInstance.Msg("Collection objectives: scoped row reuse guard, native HUD rebuild, and saved identity recovery enabled.");
				((MelonBase)this).LoggerInstance.Msg("Dealer identity: individual message decks, turf-weighted products, four base weed strains, and variable quality enabled.");
				if (settings.DevMode)
				{
					((MelonBase)this).LoggerInstance.Warning("DEV MODE: F8 triggers an event; Shift+F8 guarantees success. These create real loot and messages.");
				}
				if (settings.EnableTestCleanup)
				{
					((MelonBase)this).LoggerInstance.Msg("TEST CLEANUP: Shift+F9 discards contents of tracked DR drops and completes their collection objectives. No rewards are granted.");
				}
				if (settings.AutomaticEventIntervalMinutes > 0)
				{
					((MelonBase)this).LoggerInstance.Msg("Automatic event interval: " + settings.AutomaticEventIntervalMinutes + " in-game minutes. Daily chance is applied per interval; daily time window and day cooldown are replaced.");
				}
			}
			catch (System.Exception error)
			{
				Stop("Configuration could not be loaded", error);
			}
			if (!disabled)
			{
				try
				{
					ObjectiveHudGuard.Install(((MelonBase)this).HarmonyInstance);
					((MelonBase)this).LoggerInstance.Msg("Native collection entry-row reuse guard installed.");
				}
				catch (System.Exception ex)
				{
					((MelonBase)this).LoggerInstance.Warning("Could not install the collection entry-row reuse guard. " + (object)ex);
				}
			}
		}

		public override void OnUpdate()
		{
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Invalid comparison between Unknown and I4
			if (disabled || !settings.Enabled)
			{
				return;
			}
			try
			{
				bool flag = false;
				bool flag2 = (byte)(0u | (flag ? 1u : 0u)) != 0;
				if (!flag2 && Time.realtimeSinceStartup < nextPoll)
				{
					return;
				}
				nextPoll = Time.realtimeSinceStartup + 1f;
				if (!Singleton<LoadManager>.InstanceExists || !Singleton<LoadManager>.Instance.IsGameLoaded || Singleton<LoadManager>.Instance.IsLoading)
				{
					ResetSession();
					if (flag2)
					{
						((MelonBase)this).LoggerInstance.Msg("TEST: Load a save before using test controls.");
					}
					return;
				}
				if (!InstanceFinder.IsServer || !NetworkSingleton<TimeManager>.InstanceExists || !Singleton<SaveManager>.InstanceExists)
				{
					if (flag2)
					{
						((MelonBase)this).LoggerInstance.Msg("TEST: Only the loaded server/host can use test controls.");
					}
					return;
				}
				string loadedGameFolderPath = Singleton<LoadManager>.Instance.LoadedGameFolderPath;
				if (string.IsNullOrWhiteSpace(loadedGameFolderPath))
				{
					return;
				}
				if (state == null || !string.Equals(loadedGameFolderPath, saveFolder, (StringComparison)5))
				{
					StartSession(loadedGameFolderPath);
				}
				UpdateLootMarkers();
				if (!Singleton<SaveManager>.Instance.IsSaving)
				{
					UpdateLootObjectives();
				}
				if (Singleton<SaveManager>.Instance.IsSaving || SaveManager.SaveError || state == null)
				{
					if (flag2)
					{
						((MelonBase)this).LoggerInstance.Msg("TEST: Cannot use test controls while saving or while a save error is active.");
					}
					return;
				}
				LootProducts.RepairTrackedDrops(state);
				int elapsedDays = NetworkSingleton<TimeManager>.Instance.ElapsedDays;
				int currentTime = NetworkSingleton<TimeManager>.Instance.CurrentTime;
				int num = currentTime / 100 * 60 + currentTime % 100;
				ReportDailySchedule(elapsedDays, num);
				if (settings.RequireHostileCartel && (!NetworkSingleton<Cartel>.InstanceExists || (int)NetworkSingleton<Cartel>.Instance.Status != 2))
				{
					ScheduleNotice(elapsedDays, "cartel", "Blocked: cartel is not hostile.");
				}
				else if (state.Pending != null)
				{
					ScheduleNotice(elapsedDays, "pending", "An existing reward is pending delivery; new event rolls wait for it.");
					int num2 = elapsedDays * 1440 + num;
					if (lastDeliveryMinute != num2)
					{
						lastDeliveryMinute = num2;
						DeliverPending();
					}
				}
				else
				{
					if (!EventRules.Due(state, elapsedDays, num))
					{
						return;
					}
					List<Dealer> val = EligibleDealers();
					if (val.Count == 0)
					{
						ScheduleNotice(elapsedDays, "dealers", "Roll deferred: no conscious recruited dealer with a conversation is available.");
						return;
					}
					bool flag3 = EventRules.DrySpellDue(state, settings);
					if (!EventRules.Attempt(state, settings, elapsedDays))
					{
						((MelonBase)this).LoggerInstance.Msg(string.Concat(new string[9]
						{
							"SCHEDULE: Day ",
							elapsedDays.ToString(),
							" daily event roll FAILED (chance ",
							settings.DailyChance.ToString("P0"),
							"). No event selected today. Dry-spell misses ",
							state.ConsecutiveDailyMisses.ToString(),
							"/",
							4.ToString(),
							EventRules.DrySpellDue(state, settings) ? "; next eligible day's attempt is guaranteed." : "."
						}));
					}
					else
					{
						Dealer val2 = val[EventRules.Pick(state.Seed, elapsedDays, "dealer", val.Count)];
						((MelonBase)this).LoggerInstance.Msg(string.Concat(new string[6]
						{
							"SCHEDULE: Day ",
							elapsedDays.ToString(),
							flag3 ? " DRY-SPELL SAFEGUARD selected an attempt. " : " daily event roll PASSED. ",
							"Selected ",
							((NPC)val2).FullName,
							". Dry-spell misses reset to 0."
						}));
						state.Pending = PlanReward(val2, elapsedDays);
						lastDeliveryMinute = elapsedDays * 1440 + num;
						DeliverPending();
					}
				}
			}
			catch (System.Exception error)
			{
				Stop("Retaliation stopped to prevent repeated or partial rewards", error);
			}
		}

		private static string ClockText(int minutes)
		{
			return (minutes / 60).ToString("00") + ":" + (minutes % 60).ToString("00");
		}

		private void ReportDailySchedule(int day, int minutes)
		{
			if (diagnosticDay == day)
			{
				return;
			}
			diagnosticDay = day;
			scheduleNotices.Clear();
			if (settings.AutomaticEventIntervalMinutes <= 0)
			{
				((MelonBase)this).LoggerInstance.Msg(string.Concat(new string[15]
				{
					"SCHEDULE: Current day ",
					day.ToString(),
					" at ",
					ClockText(minutes),
					". Daily roll time ",
					ClockText(EventRules.ScheduledMinute(state.Seed, day)),
					"; base event chance ",
					settings.DailyChance.ToString("P0"),
					"; next eligible day ",
					state.NextEligibleDay.ToString(),
					"; dry-spell misses ",
					state.ConsecutiveDailyMisses.ToString(),
					"/",
					4.ToString(),
					EventRules.DrySpellDue(state, settings) ? " (next eligible attempt guaranteed)." : "."
				}));
				if (state.Pending == null && day <= state.LastAttemptDay)
				{
					((MelonBase)this).LoggerInstance.Msg("SCHEDULE: Day " + day + " was already checked. No reroll on reload.");
				}
				else if (state.Pending == null && day < state.NextEligibleDay)
				{
					((MelonBase)this).LoggerInstance.Msg(string.Concat(new string[5]
					{
						"SCHEDULE: Day ",
						day.ToString(),
						" is in cooldown/grace period. No roll before day ",
						state.NextEligibleDay.ToString(),
						"."
					}));
				}
			}
		}

		private void ScheduleNotice(int day, string key, string message)
		{
			if (scheduleNotices.Add(key))
			{
				((MelonBase)this).LoggerInstance.Msg("SCHEDULE: Day " + day + ". " + message);
			}
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			try
			{
				ResetSession();
			}
			catch (System.Exception error)
			{
				Stop("Could not reset the previous session", error);
			}
		}

		private List<Dealer> EligibleDealers()
		{
			List<Dealer> val = new List<Dealer>();
			Enumerator<Dealer> enumerator = Dealer.AllPlayerDealers.GetEnumerator();
			while (enumerator.MoveNext())
			{
				Dealer current = enumerator.Current;
				if ((Object)(object)current != (Object)null && current.IsRecruited && ((NPC)current).IsConscious && ((NPC)current).MSGConversation != null)
				{
					val.Add(current);
				}
			}
			return Enumerable.ToList<Dealer>((System.Collections.Generic.IEnumerable<Dealer>)Enumerable.OrderBy<Dealer, string>((System.Collections.Generic.IEnumerable<Dealer>)val, (Func<Dealer, string>)((Dealer d) => ((NPC)d).ID), (IComparer<string>)(object)StringComparer.Ordinal));
		}

		private PendingReward PlanReward(Dealer dealer, int day, string? seedOverride = null, bool forceSuccess = false)
		{
			string seed = seedOverride ?? state.Seed;
			PendingReward pendingReward = new PendingReward
			{
				DealerId = ((NPC)dealer).ID,
				Success = (forceSuccess || EventRules.Roll(seed, day, "success") < settings.SuccessChance)
			};
			pendingReward.MessageVariant = DealerTexts.ChooseVariant(state, ((NPC)dealer).ID, pendingReward.Success, seed, day);
			if (!pendingReward.Success)
			{
				return pendingReward;
			}
			double num = EventRules.Roll(seed, day, "kind");
			bool flag = EventRules.Roll(seed, day, "brick") < settings.BrickChance;
			if (num < 0.45 || num >= 0.85)
			{
				pendingReward.Cash = settings.MinCash + EventRules.Pick(seed, day, "cash", settings.MaxCash - settings.MinCash + 1);
			}
			if ((num >= 0.45 || flag) && NetworkSingleton<ProductManager>.InstanceExists)
			{
				List<ProductDefinition> val = new List<ProductDefinition>();
				string[] strains = DealerProfiles.Strains;
				foreach (string id in strains)
				{
					LootProducts.Add(val, id);
				}
				Dictionary<string, List<ProductDefinition>> val2 = new Dictionary<string, List<ProductDefinition>>();
				if (val.Count > 0)
				{
					val2.Add("weed", val);
				}
				strains = new string[3] { "shroom", "meth", "cocaine" };
				foreach (string text in strains)
				{
					if ((text == "shroom") ? ProductManager.ShroomsDiscovered : ((text == "meth") ? ProductManager.MethDiscovered : ProductManager.CocaineDiscovered))
					{
						List<ProductDefinition> val3 = new List<ProductDefinition>();
						LootProducts.Add(val3, text);
						if (val3.Count > 0)
						{
							val2.Add(text, val3);
						}
					}
				}
				if (val2.Count > 0)
				{
					string text2 = DealerProfiles.PickCategory(((NPC)dealer).ID, Enumerable.ToArray<string>((System.Collections.Generic.IEnumerable<string>)val2.Keys), EventRules.Roll(seed, day, "product"));
					List<ProductDefinition> val4 = val2[text2];
					ProductDefinition val5 = val4[EventRules.Pick(seed, day, "strain", val4.Count)];
					int num2 = DealerProfiles.WeightedIndex(DealerProfiles.QualityWeights(((NPC)dealer).ID, text2), EventRules.Roll(seed, day, "quality"));
					if (flag)
					{
						ProductItemInstance val6 = LootProducts.Create(((BaseItemDefinition)val5).ID, 1, num2, "brick");
						if (val6 != null)
						{
							int num3 = LootPackagingRules.BrickCount(LootPackagingRules.MaxPackages(Math.Max((double)val5.BasePrice * 20.0, (double)((BaseItemInstance)val6).GetMonetaryValue()), settings.BrickValueBudget, 2, ((BaseItemDefinition)val5).StackLimit), EventRules.Roll(seed, day, "brickcount"), settings.TwoBrickChance);
							if (num3 > 0)
							{
								pendingReward.ProductId = ((BaseItemDefinition)val5).ID;
								pendingReward.ProductQuality = num2;
								pendingReward.ProductUnits = num3;
								pendingReward.PackagingId = "brick";
							}
						}
					}
					if (pendingReward.ProductUnits == 0)
					{
						ProductItemInstance val7 = LootProducts.Create(((BaseItemDefinition)val5).ID, 1, num2, "baggie");
						if (val7 != null)
						{
							int num4 = LootPackagingRules.MaxPackages(Math.Max(val5.BasePrice, ((BaseItemInstance)val7).GetMonetaryValue()), settings.ProductValueBudget, settings.MaxProductUnits, ((BaseItemDefinition)val5).StackLimit);
							if (num4 > 0)
							{
								pendingReward.ProductId = ((BaseItemDefinition)val5).ID;
								pendingReward.ProductQuality = num2;
								pendingReward.ProductUnits = LootPackagingRules.BaggieCount(num4, settings.MinProductUnits, EventRules.Roll(seed, day, "units"));
								pendingReward.PackagingId = "baggie";
							}
						}
					}
				}
			}
			if (LootPackagingRules.IsBrickHaul(pendingReward))
			{
				LootPackagingRules.EnforceExclusiveBricks(pendingReward);
				return pendingReward;
			}
			if (EventRules.Roll(seed, day, "gold") < settings.GoldBarChance)
			{
				ItemDefinition? obj = FindValuable("goldbar");
				pendingReward.ValuableId = ((obj != null) ? ((BaseItemDefinition)obj).ID : null) ?? "";
			}
			else if (EventRules.Roll(seed, day, "jewellery") < settings.JewelleryChance)
			{
				string[] array = new string[4] { "silverwatch", "goldwatch", "silverchain", "goldchain" };
				ItemDefinition? obj2 = FindValuable(array[EventRules.Pick(seed, day, "jewellerytype", array.Length)]);
				pendingReward.ValuableId = ((obj2 != null) ? ((BaseItemDefinition)obj2).ID : null) ?? "";
			}
			if (pendingReward.Cash == 0 && pendingReward.ProductUnits == 0 && pendingReward.ValuableId == "")
			{
				pendingReward.Cash = settings.MinCash;
			}
			return pendingReward;
		}

		private ItemDefinition? FindValuable(string normalized)
		{
			Enumerator<ItemDefinition> enumerator = Singleton<Registry>.Instance.GetAllItems().GetEnumerator();
			while (enumerator.MoveNext())
			{
				ItemDefinition current = enumerator.Current;
				if ((Object)(object)current != (Object)null && (Normalize(((BaseItemDefinition)current).ID) == normalized || Normalize(((BaseItemDefinition)current).Name) == normalized) && (Object)(object)((Il2CppObjectBase)current).TryCast<ProductDefinition>() == (Object)null)
				{
					return current;
				}
			}
			return null;
		}

		private static string Normalize(string value)
		{
			return new string(Enumerable.ToArray<char>(Enumerable.Where<char>((System.Collections.Generic.IEnumerable<char>)value.ToLowerInvariant(), (Func<char, bool>)char.IsLetterOrDigit)));
		}

		private void DeliverPending()
		{
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Expected O, but got Unknown
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0397: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0624: Unknown result type (might be due to invalid IL or missing references)
			//IL_0710: Unknown result type (might be due to invalid IL or missing references)
			//IL_0715: Unknown result type (might be due to invalid IL or missing references)
			//IL_0684: Unknown result type (might be due to invalid IL or missing references)
			//IL_064c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0653: Unknown result type (might be due to invalid IL or missing references)
			//IL_0401: Unknown result type (might be due to invalid IL or missing references)
			//IL_0408: Unknown result type (might be due to invalid IL or missing references)
			PendingReward reward = state.Pending;
			Dealer dealer = Enumerable.FirstOrDefault<Dealer>((System.Collections.Generic.IEnumerable<Dealer>)EligibleDealers(), (Func<Dealer, bool>)((Dealer d) => ((NPC)d).ID == reward.DealerId));
			if ((Object)(object)dealer == (Object)null)
			{
				return;
			}
			if (!reward.Success)
			{
				state.Pending = null;
				Send(dealer, DealerTexts.Failure(reward.DealerId, reward.MessageVariant));
				((MelonBase)this).LoggerInstance.Msg("Retaliation unsuccessful for " + ((NPC)dealer).FullName + ". No loot.");
				return;
			}
			LootPackagingRules.EnforceExclusiveBricks(reward);
			List<ItemInstance> val = new List<ItemInstance>();
			List<string> val2 = new List<string>();
			if (reward.Cash > 0)
			{
				CashInstance val3 = new CashInstance(FindValuable("cash") ?? throw new InvalidOperationException("Cash item definition missing."), 1);
				val3.SetBalance((float)reward.Cash, false);
				val.Add((ItemInstance)(object)val3);
				val2.Add("$" + reward.Cash);
			}
			if (reward.ProductUnits > 0)
			{
				ProductDefinition val4 = LootProducts.Resolve(reward.ProductId) ?? throw new InvalidOperationException("Initialized loot product definition missing: " + reward.ProductId);
				reward.ProductId = ((BaseItemDefinition)val4).ID;
				ProductItemInstance val5 = LootProducts.Create(((BaseItemDefinition)val4).ID, reward.ProductUnits, reward.ProductQuality, reward.PackagingId) ?? throw new InvalidOperationException("Loot product instance missing: " + ((BaseItemDefinition)val4).ID);
				val.Add((ItemInstance)(object)val5);
				string text = (string.IsNullOrEmpty(reward.PackagingId) ? "loose" : reward.PackagingId);
				val2.Add(string.Concat(new string[12]
				{
					reward.ProductUnits.ToString(),
					" x ",
					text,
					" of ",
					((object)((QualityItemInstance)val5).Quality/*cast due to .constrained prefix*/).ToString(),
					" ",
					((BaseItemDefinition)val4).Name,
					" [",
					((BaseItemDefinition)val4).ID,
					", ",
					((BaseItemInstance)val5).GetTotalAmount().ToString(),
					" product units total]"
				}));
			}
			if (reward.ValuableId != "")
			{
				ItemDefinition val6 = Registry.GetItem(reward.ValuableId) ?? throw new InvalidOperationException("Valuable definition missing: " + reward.ValuableId);
				val.Add(val6.GetDefaultInstance(1));
				val2.Add(string.Concat(new string[5]
				{
					"a ",
					((BaseItemDefinition)val6).Name,
					" [",
					((BaseItemDefinition)val6).ID,
					"]"
				}));
			}
			List<DeadDrop> val7 = new List<DeadDrop>();
			Enumerator<DeadDrop> enumerator = DeadDrop.DeadDrops.GetEnumerator();
			while (enumerator.MoveNext())
			{
				DeadDrop current = enumerator.Current;
				if ((Object)(object)current == (Object)null || (Object)(object)current.Storage == (Object)null || ((StorageEntity)current.Storage).IsOpened || ((StorageEntity)current.Storage).ItemCount != 0 || !Singleton<Map>.Instance.GetRegionData(current.Region).IsUnlocked || ((StorageEntity)current.Storage).ItemSlots.Count < val.Count)
				{
					continue;
				}
				bool flag = false;
				Enumerator<DeaddropQuest> enumerator2 = DeaddropQuest.DeaddropQuests.GetEnumerator();
				while (enumerator2.MoveNext())
				{
					DeaddropQuest current2 = enumerator2.Current;
					if ((Object)(object)current2 != (Object)null && (Object)(object)current2.Drop != (Object)null && current2.Drop.GUID == current.GUID)
					{
						flag = true;
					}
				}
				if (flag)
				{
					continue;
				}
				bool flag2 = true;
				Enumerator<ItemSlot> enumerator3 = ((StorageEntity)current.Storage).ItemSlots.GetEnumerator();
				while (enumerator3.MoveNext())
				{
					ItemSlot current3 = enumerator3.Current;
					if (current3.ItemInstance != null || current3.IsLocked || current3.IsAddLocked)
					{
						flag2 = false;
					}
				}
				for (int num = 0; num < val.Count; num++)
				{
					ItemSlot val8 = ((StorageEntity)current.Storage).ItemSlots[num];
					if (val8.ItemInstance != null || val8.IsLocked || val8.IsAddLocked || val8.GetCapacityForItem(val[num], true) < ((BaseItemInstance)val[num]).Quantity)
					{
						flag2 = false;
					}
				}
				if (flag2)
				{
					val7.Add(current);
				}
			}
			DeadDrop val9 = Enumerable.FirstOrDefault<DeadDrop>((System.Collections.Generic.IEnumerable<DeadDrop>)Enumerable.OrderBy<DeadDrop, float>((System.Collections.Generic.IEnumerable<DeadDrop>)val7, (Func<DeadDrop, float>)((DeadDrop d) => Vector3.Distance(((Component)d).transform.position, ((Component)dealer).transform.position))));
			if ((Object)(object)val9 == (Object)null)
			{
				return;
			}
			state.Pending = null;
			state.NextEligibleDay = Math.Max(state.NextEligibleDay, NetworkSingleton<TimeManager>.Instance.ElapsedDays + settings.CooldownDays);
			if (settings.AutomaticEventIntervalMinutes > 0)
			{
				int currentTime = NetworkSingleton<TimeManager>.Instance.CurrentTime;
				int now = NetworkSingleton<TimeManager>.Instance.ElapsedDays * 1440 + currentTime / 100 * 60 + currentTime % 100;
				AutomaticTiming.Delay(state.AutomaticSchedule, settings.AutomaticEventIntervalMinutes, now);
			}
			for (int num2 = 0; num2 < val.Count; num2++)
			{
				((StorageEntity)val9.Storage).SetStoredInstance((NetworkConnection)null, num2, val[num2]);
				ItemInstance itemInstance = ((StorageEntity)val9.Storage).ItemSlots[num2].ItemInstance;
				if (itemInstance == null || ((BaseItemDefinition)itemInstance.Definition).ID != ((BaseItemDefinition)val[num2].Definition).ID || ((BaseItemInstance)itemInstance).Quantity != ((BaseItemInstance)val[num2]).Quantity)
				{
					throw new InvalidOperationException("Dead-drop insertion was not confirmed. Event consumed to prevent a duplicate payout.");
				}
				ProductItemInstance val10 = ((Il2CppObjectBase)val[num2]).TryCast<ProductItemInstance>();
				if (val10 != null)
				{
					ProductItemInstance val11 = ((Il2CppObjectBase)itemInstance).TryCast<ProductItemInstance>();
					if (val11 == null || ((QualityItemInstance)val11).Quality != ((QualityItemInstance)val10).Quality || val11.PackagingID != val10.PackagingID || ((BaseItemInstance)val11).GetTotalAmount() != ((BaseItemInstance)val10).GetTotalAmount())
					{
						throw new InvalidOperationException("Dead-drop product quality/packaging was not confirmed. Event consumed to prevent a duplicate payout.");
					}
				}
			}
			((StorageEntity)val9.Storage).ContentsChanged();
			val9.UpdateDeadDrop();
			string text2 = ((object)val9.GUID/*cast due to .constrained prefix*/).ToString();
			if (!state.LootDropGuids.Contains(text2))
			{
				state.LootDropGuids.Add(text2);
			}
			string location = LootObjectives.DropName(val9);
			state.LootDeliveries.Add(new LootDelivery
			{
				DropGuid = text2,
				QuestGuid = ((object)Guid.NewGuid()/*cast due to .constrained prefix*/).ToString(),
				DealerId = ((NPC)dealer).ID,
				DealerName = ((NPC)dealer).FullName,
				Location = location
			});
			string text3 = string.Join(", ", (System.Collections.Generic.IEnumerable<string>)val2);
			Send(dealer, DealerTexts.Success(reward.DealerId, reward.MessageVariant, location));
			UpdateLootMarkers();
			UpdateLootObjectives();
			((MelonBase)this).LoggerInstance.Msg(string.Concat(new string[6]
			{
				"Retaliation delivered by ",
				((NPC)dealer).FirstName,
				" to ",
				val9.DeadDropName,
				": ",
				text3
			}));
		}

		private static void Send(Dealer dealer, string text)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			((NPC)dealer).MSGConversation.SendMessage(new Message(text, (ESenderType)1, true, -1), true, true);
		}

		private void StartSession(string folder)
		{
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			ResetSession();
			saveFolder = folder;
			string statePath = StatePath;
			if (File.Exists(statePath))
			{
				state = JsonSerializer.Deserialize<SaveState>(File.ReadAllText(statePath), (JsonSerializerOptions)null) ?? throw new FormatException("Empty save state.");
				if (state.Version != 1 || string.IsNullOrWhiteSpace(state.Seed))
				{
					throw new FormatException("Unsupported save state.");
				}
				SaveState saveState = state;
				if (saveState.LootDropGuids == null)
				{
					List<string> val = (saveState.LootDropGuids = new List<string>());
				}
				saveState = state;
				if (saveState.LootDeliveries == null)
				{
					List<LootDelivery> val3 = (saveState.LootDeliveries = new List<LootDelivery>());
				}
				saveState = state;
				if (saveState.AutomaticSchedule == null)
				{
					IntervalSchedule intervalSchedule = (saveState.AutomaticSchedule = new IntervalSchedule());
				}
				saveState = state;
				if (saveState.MessageHistory == null)
				{
					Dictionary<string, List<int>> val5 = (saveState.MessageHistory = new Dictionary<string, List<int>>());
				}
			}
			else
			{
				SaveState saveState2 = new SaveState();
				Guid val7 = Guid.NewGuid();
				saveState2.Seed = ((Guid)(ref val7)).ToString("N");
				saveState2.NextEligibleDay = NetworkSingleton<TimeManager>.Instance.ElapsedDays + settings.CooldownDays;
				state = saveState2;
				AtomicWrite(statePath, state);
			}
			hookedSaveManager = Singleton<SaveManager>.Instance;
			saveCompleteAction = UnityAction.op_Implicit(new Action(SaveCompleted));
			hookedSaveManager.onSaveComplete.AddListener(saveCompleteAction);
			((MelonBase)this).LoggerInstance.Msg("Save loaded. Next eligible day: " + state.NextEligibleDay + ".");
		}

		private void SaveCompleted()
		{
			if (state == null || SaveManager.SaveError)
			{
				return;
			}
			try
			{
				AtomicWrite(StatePath, state);
			}
			catch (System.Exception error)
			{
				Stop("Could not save retaliation state", error);
			}
		}

		private void ResetSession()
		{
			try
			{
				lootMarkers.Clear();
			}
			catch (System.Exception ex)
			{
				((MelonBase)this).LoggerInstance.Warning("Could not clear a previous compass marker: " + ex.Message);
			}
			markersFailed = false;
			try
			{
				lootObjectives.Clear();
			}
			catch (System.Exception ex2)
			{
				((MelonBase)this).LoggerInstance.Warning("Could not clear a previous objective icon: " + ex2.Message);
			}
			nextObjectiveRetry = 0f;
			lastObjectiveError = "";
			diagnosticDay = -1;
			scheduleNotices.Clear();
			if ((Object)(object)hookedSaveManager != (Object)null && (Delegate)(object)saveCompleteAction != (Delegate)null)
			{
				hookedSaveManager.onSaveComplete.RemoveListener(saveCompleteAction);
			}
			hookedSaveManager = null;
			saveCompleteAction = null;
			state = null;
			saveFolder = "";
			lastDeliveryMinute = -1;
		}

		private void UpdateLootMarkers()
		{
			if (state == null || markersFailed)
			{
				return;
			}
			try
			{
				lootMarkers.Sync(state);
			}
			catch (System.Exception ex)
			{
				markersFailed = true;
				((MelonBase)this).LoggerInstance.Warning("Loot compass markers disabled for this session; delivery remains available. " + (object)ex);
			}
		}

		private void UpdateLootObjectives()
		{
			if (state == null || Time.realtimeSinceStartup < nextObjectiveRetry)
			{
				return;
			}
			try
			{
				lootObjectives.Sync(state);
			}
			catch (System.Exception ex)
			{
				nextObjectiveRetry = Time.realtimeSinceStartup + 5f;
				if (lastObjectiveError != ex.Message)
				{
					((MelonBase)this).LoggerInstance.Warning("Could not refresh collection objectives; retrying in 5 seconds. Loot and compass remain available. " + (object)ex);
				}
				lastObjectiveError = ex.Message;
			}
		}

		private static void AtomicWrite<T>(string path, T value)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			string text = path + ".tmp";
			File.WriteAllText(text, JsonSerializer.Serialize<T>(value, JsonOptions));
			File.Move(text, path, true);
		}

		private void Stop(string reason, System.Exception error)
		{
			disabled = true;
			((MelonBase)this).LoggerInstance.Error(reason + ". Restart after fixing the error. " + (object)error);
		}
	}
	public static class DealerTexts
	{
		private sealed record Voice(string Greeting, string Signoff, string[] Wins, string[] Losses)
		{
			[CompilerGenerated]
			private System.Type EqualityContract
			{
				[CompilerGenerated]
				get
				{
					return typeof(Voice);
				}
			}

			[CompilerGenerated]
			public override string ToString()
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Expected O, but got Unknown
				StringBuilder val = new StringBuilder();
				val.Append("Voice");
				val.Append(" { ");
				if (PrintMembers(val))
				{
					val.Append(' ');
				}
				val.Append('}');
				return ((object)val).ToString();
			}

			[CompilerGenerated]
			private bool PrintMembers(StringBuilder builder)
			{
				RuntimeHelpers.EnsureSufficientExecutionStack();
				builder.Append("Greeting = ");
				builder.Append((object)Greeting);
				builder.Append(", Signoff = ");
				builder.Append((object)Signoff);
				builder.Append(", Wins = ");
				builder.Append((object)Wins);
				builder.Append(", Losses = ");
				builder.Append((object)Losses);
				return true;
			}

			[CompilerGenerated]
			public bool Equals(Voice? other)
			{
				if ((object)this != other)
				{
					if ((object)other != null && EqualityContract == other.EqualityContract && EqualityComparer<string>.Default.Equals(Greeting, other.Greeting) && EqualityComparer<string>.Default.Equals(Signoff, other.Signoff) && EqualityComparer<string[]>.Default.Equals(Wins, other.Wins))
					{
						return EqualityComparer<string[]>.Default.Equals(Losses, other.Losses);
					}
					return false;
				}
				return true;
			}
		}

		private static readonly Voice Generic = new Voice("Hey boss,", "I've left your cut at ", new string[4] { "Jumped a Benzies dealer on their own. They fought back hard, but I got the bag and made it out.", "One of Benzie's dealers tried robbing me. Turned it round after a nasty scrap. I've got something for us.", "Hit a Benzies handoff. Shots went off and everyone scattered. Got clear with a bag before their backup arrived.", "Found a Benzies stash with nobody watching it. Took the opportunity. A quiet result for once." }, new string[4] { "Followed a Benzies dealer, but their mates turned up. Wasn't worth the trouble.", "Tried to catch one of the Benzies alone. They spotted me and ran. Nothing this time.", "Found a Benzies stash, but someone had cleared it out already.", "Had a chance to hit the Benzies, but there were cops everywhere. Had to leave it." });

		private static readonly Dictionary<string, Voice> Voices = new Dictionary<string, Voice>((IEqualityComparer<string>)(object)StringComparer.Ordinal)
		{
			["benji_coleman"] = new Voice("Hey boss,", "Your cut's waiting at ", new string[12]
			{
				"Caught a Benzies dealer behind the motel. Thought I had the jump on him, then he pulled a knife. Got the bag, but my lip's going to look stupid for a bit.", "One of the Benzies tried robbing me. Again. This time I was ready. He's still looking for his gun in the gutter and I've got his stuff.", "Boss, nearly shit myself. Jumped a Benzies dealer and his mate started shooting. Got out with the bag. There's a hole in my shirt that wasn't there earlier.", "Saw one of Benzie's dealers hassling someone near the motel. Stepped in, caught an elbow, gave him worse. Picked his pockets while he was seeing stars.", "Followed a Benzies dealer and he spotted me. Had to chase him halfway round the block. He tripped before I ran out of breath. Just.", "Got a tip about a Benzies stash. The tip forgot to mention the bloke guarding it. Had a nasty little scrap, but I came away with something.", "One of the Benzies laughed when I told him to empty his pockets. Wasn't laughing after I put him into the bins. Bit of a smell on the haul. Sorry.", "Benzies dealer tried to draw on me outside the motel. I hit him before he got it clear. Hands are still shaking, but I got what he was carrying.", "Heard shots, then saw a Benzies dealer legging it with a bag. Thought he'd got away from everyone. He hadn't got away from me.", "Found a Benzies bag being watched by a dog with a better attitude than its owner. Owner came back, we had a fight, dog stayed out of it. Sensible dog.",
				"Cornered one of Benzie's lot and told him to hand it over. He swung first. I swung last. Think that makes us even for the motel bullshit.", "Got into a Benzies stash while their lookout was asleep. He woke up as I was leaving. Never run that fast in my life, but I kept the bag."
			}, new string[8] { "Tried jumping a Benzies dealer. He cracked me across the mouth before I got close. Had to get out of there. Nothing for us, boss.", "Thought it was one Benzies dealer. Turned out there was another with a gun. Spent the next minute behind a bin reconsidering things. Came back empty.", "Caught a Benzies dealer, lost the fight, lost my dignity. Didn't lose any of our stock, at least. I'm not going back round there just yet.", "Their lookout fired at me before I reached the stash. Took a graze and kept running. I'm alright. There's no haul, though.", "Chased one of Benzie's dealers right into his mates. That was a fucking stupid idea. Got away before they boxed me in.", "Had a Benzies dealer down, then the cops came round the corner. Had to leave before I could take anything. Timing, boss. Terrible timing.", "Got a tip about a stash. Empty room, two angry Benzies outside. Reckon somebody set me up. Slipped out the back with nothing.", "One of Benzie's lot dropped his bag and ran. Went to grab it, then he started shooting back at me. Left the bag. Like my head where it is." }),
			["molly_presley"] = new Voice("Boss,", "I've put your share at ", new string[12]
			{
				"A Benzies dealer grabbed me outside the apartments. Slammed his hand in the gate and relieved him of his bag. He can complain to someone else.", "Caught one of Benzie's lot on the stairs. He went for a knife. I introduced him to the railing. Took what he had while he was picking himself up.", "One of the Benzies put a gun in my face. Knocked it aside before he fired. My ears are ringing, his nose is fucked, and we got the haul.", "Their dealer thought bringing a mate made him untouchable. Took a punch from one. The other caught a round when he drew on me. They left the bag when they backed off.", "Heard a Benzies dealer bragging about robbing our people. Caught him without the crowd and bounced his head off a door. Took a little compensation.", "Hit a Benzies handoff. Somebody started shooting, so the whole thing went to shit. Dragged a bag out before their backup arrived.", "A Benzies lookout tried stopping me in the stairwell. Came out with scraped knuckles and their stash. He came out with a limp.", "One of Benzie's dealers ran when he recognised me. Caught him at a locked gate. Funny how fast all that swagger goes when there's nowhere left to go.", "Went after a Benzies stash. The dealer got back early and caught me inside. Broke a chair sorting that out. Still got the haul.", "The Benzies tried to lure me into an alley. Saw the second one coming. They weren't expecting me to hit first. Left them there and took the bag.",
				"Benzies dealer clipped my shoulder in a scrap. Didn't stop me putting him on the floor. Hurts now, but I got something out of it.", "Their lookout was busy watching the street. Took the bag from behind him. He chased me for half a block, then decided he liked his teeth."
			}, new string[8] { "Benzies had someone waiting on the stairs. Caught a boot in the ribs before I could reach the bag. Got clear. Nothing coming in.", "Tried hitting a Benzies handoff. More guns than I'd been told about. I'm not dying for somebody else's bag. Pulled out.", "Their dealer swung a knife and caught my sleeve. Could've been worse. Couldn't get the haul without getting cornered, so I left it.", "Had one of the Benzies against a wall when his backup arrived. Got a nasty knock getting out. They kept their stuff this time.", "The stash tip was bullshit. Walked into an ambush. Made it out before they closed the alley, but came away with nothing.", "Benzies dealer went down, then somebody started firing from a window. Left him and the bag where they were. Not hanging round for that.", "Nearly had their courier. He shoved a bystander into me and disappeared. Next time I'm not leaving him that opening.", "Police rolled in halfway through the fight. Had to break off before I got anything. Irritating, but I'm clear." }),
			["brad_crosby"] = new Voice("Hey boss,", "Stashed your cut at ", new string[12]
			{
				"Benzies dealer tried pulling a knife on me. Must've looked slower than I am. Put him down and picked up his bag. Fair bit of effort for a walk.", "Had a disagreement with one of Benzie's lot. He used his fists, I used mine. Mine made the better argument. Got something for the trouble.", "Caught a Benzies courier on his own. He threw the bag at me and went for his gun. Took cover with the bag. Let him keep the temper.", "Was minding my business till a Benzies dealer decided to make it his. Few bruises later, his pockets were my business instead.", "Found a Benzies stash in a spot I know. Guard came back while I was looking. Bit of wrestling, bit of swearing. Hauled the bag out in the end.", "One of the Benzies took a swing and missed. Put him on his arse before he got another go. Took his stuff. Could've just kept walking, couldn't he?", "Shots went off near a Benzies handoff. Everyone scattered except one dealer nursing his ankle. Got the bag off him before the others came back.", "Waited out a Benzies lookout. Got bored myself and walked right into him. Less subtle than planned. Still got the stash, just with a sore jaw.", "Benzies dealer chased me away from a bag. Led him round the block, doubled back and grabbed it. He can finish the lap without me.", "Caught one of their dealers kicking someone while they were down. Gave him a turn on the floor. Took his bag when he stopped arguing.",
				"One of the Benzies drew on me and put a round into the wall. We had a pretty ugly scrap after that. Took the haul, left him making a lot of noise.", "Got past a Benzies lookout and lifted the stash. Nearly did it quietly. Knocked something over on the way out and had to run. Can't have everything."
			}, new string[8] { "Benzies dealer was quicker than he looked. Caught me a good one behind the ear. Got my feet under me and left. No bonus this time.", "Thought I could get round their lookout. Turns out he had a shotgun. Changed my plans pretty sharpish. Nothing to collect.", "Had a scrap with one of Benzie's lot. Nobody won much except a few bruises. His mate took the bag while we were busy. Ah well.", "One of the Benzies fired at me from across the street. Spent a while getting out of sight. Didn't fancy crossing back for the stash.", "Chased a Benzies courier till my legs voted against it. He kept the bag. Need to pick a slower one next time.", "Someone gave me a bad stash tip. Three Benzies waiting instead. Took a knock on the way out, but didn't leave them anything of ours.", "Got the dealer down, then heard sirens right on top of us. Left before I could search him. Whole lot of work for nothing.", "Their stash guard brought a mate. Mate brought a gun. Didn't stick around to see what else they'd brought." }),
			["jane_lucero"] = new Voice("Boss, quick update.", "Your share is tucked away at ", new string[12]
			{
				"Their courier checked the road but missed the turn I came through. He drew a knife when I stopped him. Disarmed him, took the bag, left before his next check-in.", "Benzies tried boxing me into a narrow passage. Put the nearest one down and went through the gap. Took his bag on the way out.", "Hit a Benzies handoff as they were splitting up. One fired at me. Got behind cover with their bag and used another way out. Nobody followed.", "Followed a dealer to a stash, but the lookout caught me leaving. Took a cut across the arm getting past. Haul's safe. I'll deal with the cut.", "Found a Benzies dealer leaning on one of our customers. Stepped in before it got worse. Had to put him down hard. Took what he was carrying.", "Their lookout kept calling in the wrong direction. Took the opening, caught the courier and got the bag after a short fight. Cleared the route before backup came.", "A Benzies dealer chased me away from their stash. Lost him through a side route, doubled back and took it. Let him keep looking.", "Caught their dealer alone at the end of a run. He went for his waistband. Knocked him off balance before he got the gun out. Left with the bag.", "The Benzies were moving a bag between two lookouts. Caught the carrier between them. He fought harder than I expected, but neither lookout got there in time.", "Got jumped by one of Benzie's dealers. Took a hit to the cheek, then got him down. Turned his robbery into ours.",
				"Spotted a Benzies bag being passed through a gate. Stopped the receiver before he could get away. Had to fight for it, but I've checked the way back. We're clear.", "Waited until their armed lookout moved round the corner. Took the stash. They noticed quickly, but I'd already changed routes. Kept the haul."
			}, new string[8] { "The courier was bait. Another Benzies dealer came up behind me with a gun. Got clear before they could close both exits. No haul.", "Reached the stash, then took a hit from the guard I hadn't seen. Had to leave the bag and get out. I'm clear of them now.", "A shot clipped my arm near their handoff. Couldn't keep after the courier without leaving cover. Broke off. Nothing coming in.", "Had their dealer down, but a second one came through the gate. Could fight or get out, not both with the bag. Chose the way out.", "Chased the courier through a route I thought I knew. They'd blocked the exit. Turned back before the Benzies behind me caught up.", "Police arrived while I was getting the dealer off me. Left before either of us could be stopped. Didn't get to his bag.", "Their lookout changed position while I moved in. Nearly walked into his sights. Backed out without giving him a clean shot. No result.", "Caught the dealer but the handoff was already done. Took an elbow for an empty bag. Lost the next courier while I was dealing with him." }),
			["wei_long"] = new Voice("Hello boss,", "Your share is waiting at ", new string[12]
			{
				"One of Benzie's dealers reached for a knife when I stopped him. Broke his grip and put him down. The bag was less troublesome than its owner.", "A Benzies dealer followed me looking for an easy robbery. I let him catch up where his friends could not help. We came to a different arrangement after the fight.", "Their lookout opened fire sooner than I expected. Had to leave the first route, but kept hold of the bag. A damaged jacket is manageable.", "Caught a Benzies courier after the others left. He was armed and unwilling to give anything up. Settled it at close quarters. I have the haul.", "The Benzies had a guard inside the stash room. We surprised one another. It was a rough few moments, but I left with something for us.", "Watched one of their dealers rob a customer. Stepped in as he was putting his weapon away. He is unlikely to enjoy explaining where the proceeds went.", "Intercepted a Benzies handoff. One man tried to hold me while the other drew. Got clear of the first and put the second down. Took their bag before they recovered.", "A dealer from Benzie's crew tried to make an example of me. I have a bruised cheek. He has rather more to think about. Collected what he was carrying.", "Their courier ran instead of drawing. Caught him at the next turn and had to fight for the bag. Better there than among his friends.", "Found a Benzies dealer watching the same stash for too long. He spotted me moving in and fired. Reached him before he could fire again. Took the stash afterward.",
				"Their backup arrived just as I got the bag. A round grazed my shoulder on the way out. I am safe, and the haul is accounted for.", "The Benzies left a supply bag with an exhausted lookout. Took it without waking him. I prefer that result to another fight when it is available."
			}, new string[8] { "The dealer was not alone. His partner had a clear shot and I had no clean way to reach them. Withdrew without the bag.", "A Benzies guard struck me from the side as I reached the stash. Got out with a sore head and nothing else. The information was incomplete.", "Their courier fought long enough for his friends to arrive. Had to leave him before I could take anything. No gain this time.", "Caught a graze while approaching the handoff. Could have pressed on, but they had the better position. I am clear now. The bag remains theirs.", "The Benzies used an empty bag to draw me into an alley. Saw the gunman before he saw me. Left by another route. Nothing to collect.", "The dealer was down, but his weapon had drawn police attention. Left without searching him. An arrest would have cost us more.", "Their courier escaped after a brief fight. Took a blow to the ribs slowing me down. I will not make the same estimate of him twice.", "Reached the stash, but their backup cut off the way out. Abandoned the haul to get clear. An unpleasant result, but a necessary one." }),
			["leo_rivers"] = new Voice("Boss. Got something for you.", "Your cut is at ", new string[12]
			{
				"Caught one of Benzie's dealers alone. He reached for his gun, so I put him down before he cleared his jacket. Took what he was carrying. No speech needed.", "Benzies tried robbing me on my own route. Wrong dealer. Took a punch, put the first one out, and the second ran. Picked up their bag afterward.", "Hit their handoff. One of them fired first. I fired back and he went down. Took the bag before his mate got a clear shot. Not a quiet job.", "Their courier bolted when he saw me. Caught him at a fence and dragged him back down. He fought for the bag. Lost it anyway.", "Walked into a Benzies stash and found the guard still there. He came at me with a blade. Got the haul and a split knuckle. Could've been worse.", "One of Benzie's dealers was threatening our people. Found him away from his crew and put him on the floor. Took something to remember him by.", "Their lookout recognised me and went straight for his weapon. Had to hit him hard. Took the bag before the rest of them came looking.", "Caught a Benzies dealer loading up for another run. He slammed me into the wall, so I slammed him back. Left him there. The bag came with me.", "Benzies set up an ambush and got too eager. First one came round the corner alone. Dropped him, grabbed his bag and got out before the others could line me up.", "A round clipped my side getting out of their handoff. I'm on my feet. Got the bag, too. They don't get to call that one a win.",
				"One of their dealers tried buying time with a lot of talking. Saw his mate coming behind me. Broke through before they could pin me down and took the haul.", "Got to a Benzies stash between shifts. No fight, for once. They left enough behind to make the trip worthwhile. Took it and kept moving."
			}, new string[8] { "Their courier had someone covering him from above. First shot took a chunk out of the wall. Left the bag. Not giving them a second shot at me.", "Got one Benzies dealer down and caught a blow from the second. Had to fight my way out. Nothing to show for it except a bad fucking headache.", "The handoff was an ambush. Too many guns and no room to move with the haul. Got clear. They kept their stuff.", "Caught a round across the arm before I reached their dealer. Still moving, but not chasing him through gunfire for an unknown payout. Nothing this time.", "Their dealer held on long enough for backup. Had to leave before they surrounded me. I'll remember his face.", "Chased their courier into traffic and lost him. Got clipped by a wing mirror for the effort. Bag got away with him.", "The stash was empty. Lookout wasn't. Had to put him down just to get out. No haul, and whoever gave me that tip needs a word.", "Police turned up while I had the dealer pinned. Left him there before they could get a look at me. Didn't get to his pockets." })
		};

		private static Voice Get(string id)
		{
			Voice result = default(Voice);
			if (!Voices.TryGetValue(id, ref result))
			{
				return Generic;
			}
			return result;
		}

		public static int Count(string id, bool success)
		{
			if (!success)
			{
				return Get(id).Losses.Length;
			}
			return Get(id).Wins.Length;
		}

		public static int ChooseVariant(SaveState state, string id, bool success, string seed, int day)
		{
			if (state.MessageHistory == null)
			{
				Dictionary<string, List<int>> val = (state.MessageHistory = new Dictionary<string, List<int>>());
			}
			string text = id + (success ? ":success" : ":failure");
			List<int> used = default(List<int>);
			if (!state.MessageHistory.TryGetValue(text, ref used) || used == null)
			{
				state.MessageHistory[text] = (used = new List<int>());
			}
			int count = Count(id, success);
			used.RemoveAll((Predicate<int>)((int i) => i < 0 || i >= count));
			if (Enumerable.Count<int>(Enumerable.Distinct<int>((System.Collections.Generic.IEnumerable<int>)used)) >= count)
			{
				int num = used[used.Count - 1];
				used.Clear();
				used.Add(num);
			}
			int[] array = Enumerable.ToArray<int>(Enumerable.Where<int>(Enumerable.Range(0, count), (Func<int, bool>)((int i) => !used.Contains(i))));
			int num2 = array[EventRules.Pick(seed, day, "message", array.Length)];
			used.Add(num2);
			return num2;
		}

		public static string Success(string id, int variant, string location)
		{
			Voice voice = Get(id);
			return string.Concat(new string[7]
			{
				voice.Greeting,
				"\n\n",
				voice.Wins[(uint)variant % (uint)voice.Wins.Length],
				"\n\n",
				voice.Signoff,
				location,
				"."
			});
		}

		public static string Failure(string id, int variant)
		{
			Voice voice = Get(id);
			return voice.Greeting + "\n\n" + voice.Losses[(uint)variant % (uint)voice.Losses.Length];
		}

		public static string Success(int variant, string location)
		{
			return Success("", variant, location);
		}

		public static string Failure(int variant)
		{
			return Failure("", variant);
		}
	}
	public sealed class Settings
	{
		[field: CompilerGenerated]
		public bool Enabled
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = true;

		[field: CompilerGenerated]
		public bool DevMode
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}

		[field: CompilerGenerated]
		public bool EnableTestCleanup
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}

		[field: CompilerGenerated]
		public bool RequireHostileCartel
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = true;

		[field: CompilerGenerated]
		public int AutomaticEventIntervalMinutes
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}

		[field: CompilerGenerated]
		public double DailyChance
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 0.4;

		[field: CompilerGenerated]
		public int CooldownDays
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 2;

		[field: CompilerGenerated]
		public double SuccessChance
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 0.8;

		[field: CompilerGenerated]
		public double JewelleryChance
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 0.1;

		[field: CompilerGenerated]
		public double GoldBarChance
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 0.005;

		[field: CompilerGenerated]
		public double BrickChance
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 0.05;

		[field: CompilerGenerated]
		public double TwoBrickChance
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 0.1;

		[field: CompilerGenerated]
		public int BrickValueBudget
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 6000;

		[field: CompilerGenerated]
		public int MinCash
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 500;

		[field: CompilerGenerated]
		public int MaxCash
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 1500;

		[field: CompilerGenerated]
		public int MinProductUnits
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 5;

		[field: CompilerGenerated]
		public int MaxProductUnits
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 12;

		[field: CompilerGenerated]
		public int ProductValueBudget
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 1000;

		public Settings ApplyBuildPolicy()
		{
			return new Settings();
		}

		public void Validate()
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			Probability("DailyChance", DailyChance);
			Probability("SuccessChance", SuccessChance);
			Probability("JewelleryChance", JewelleryChance);
			Probability("GoldBarChance", GoldBarChance);
			Probability("BrickChance", BrickChance);
			Probability("TwoBrickChance", TwoBrickChance);
			if (BrickValueBudget < 1)
			{
				throw new FormatException($"BrickValueBudget is {BrickValueBudget}; expected at least 1.");
			}
			if (CooldownDays < 0 || CooldownDays > 30)
			{
				throw new FormatException($"CooldownDays is {CooldownDays}; expected 0 to 30.");
			}
			if (AutomaticEventIntervalMinutes < 0 || AutomaticEventIntervalMinutes > 1440)
			{
				throw new FormatException($"AutomaticEventIntervalMinutes is {AutomaticEventIntervalMinutes}; expected 0 (daily scheduler) or 1 to 1440 in-game minutes.");
			}
			if (MinCash < 1)
			{
				throw new FormatException($"MinCash is {MinCash}; expected at least 1.");
			}
			if (MaxCash < MinCash)
			{
				throw new FormatException($"MaxCash is {MaxCash}; it must be at least MinCash ({MinCash}).");
			}
			if (MaxCash > 1000000000)
			{
				throw new FormatException($"MaxCash is {MaxCash}; the game supports at most 1000000000 per cash item.");
			}
			if (MaxProductUnits < 1 || MaxProductUnits > 20)
			{
				throw new FormatException($"MaxProductUnits is {MaxProductUnits}; expected 1 to 20 for this build's single product stack.");
			}
			if (MinProductUnits < 1 || MinProductUnits > 20)
			{
				throw new FormatException($"MinProductUnits is {MinProductUnits}; expected 1 to 20; budget and maximum take priority.");
			}
			if (ProductValueBudget < 1)
			{
				throw new FormatException($"ProductValueBudget is {ProductValueBudget}; expected at least 1.");
			}
		}

		private static void Probability(string name, double value)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (double.IsNaN(value) || value < 0.0 || value > 1.0)
			{
				throw new FormatException($"{name} is {value}; expected a chance between 0 and 1.");
			}
		}
	}
	public sealed class SaveState
	{
		[field: CompilerGenerated]
		public int Version
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 1;

		[field: CompilerGenerated]
		public string Seed
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = "";

		[field: CompilerGenerated]
		public int LastAttemptDay
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = -1;

		[field: CompilerGenerated]
		public int NextEligibleDay
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}

		[field: CompilerGenerated]
		public int EventCount
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}

		[field: CompilerGenerated]
		public int ConsecutiveDailyMisses
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}

		[field: CompilerGenerated]
		public PendingReward? Pending
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}

		[field: CompilerGenerated]
		public List<string> LootDropGuids
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = new List<string>();

		[field: CompilerGenerated]
		public List<LootDelivery> LootDeliveries
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = new List<LootDelivery>();

		[field: CompilerGenerated]
		public IntervalSchedule AutomaticSchedule
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = new IntervalSchedule();

		[field: CompilerGenerated]
		public Dictionary<string, List<int>> MessageHistory
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = new Dictionary<string, List<int>>();
	}
	public sealed class IntervalSchedule
	{
		[field: CompilerGenerated]
		public int IntervalMinutes
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}

		[field: CompilerGenerated]
		public int LastClockMinute
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = -1;

		[field: CompilerGenerated]
		public int NextAttemptMinute
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = -1;

		[field: CompilerGenerated]
		public int Sequence
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}
	}
	public static class AutomaticTiming
	{
		public static bool Due(IntervalSchedule schedule, int interval, int now)
		{
			if (interval <= 0)
			{
				return false;
			}
			if (schedule.IntervalMinutes != interval || schedule.NextAttemptMinute < 0 || now < schedule.LastClockMinute)
			{
				Delay(schedule, interval, now);
				return false;
			}
			schedule.LastClockMinute = now;
			return now >= schedule.NextAttemptMinute;
		}

		public static void Consume(IntervalSchedule schedule, int interval, int now)
		{
			schedule.Sequence++;
			Delay(schedule, interval, now);
		}

		public static void Delay(IntervalSchedule schedule, int interval, int now)
		{
			schedule.IntervalMinutes = interval;
			schedule.LastClockMinute = now;
			schedule.NextAttemptMinute = now + interval;
		}
	}
	public sealed class LootDelivery
	{
		[field: CompilerGenerated]
		public string DropGuid
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = "";

		[field: CompilerGenerated]
		public string QuestGuid
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = "";

		[field: CompilerGenerated]
		public List<string> PreviousQuestGuids
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = new List<string>();

		[field: CompilerGenerated]
		public string DealerId
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = "";

		[field: CompilerGenerated]
		public string DealerName
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = "Your dealer";

		[field: CompilerGenerated]
		public string Location
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = "the dead drop";

		[JsonIgnore]
		public string ObjectiveTitle => "Collect your cut";

		[JsonIgnore]
		public string ObjectiveDescription => DealerName + " left a drop at " + Location + ".";
	}
	public sealed class PendingReward
	{
		[field: CompilerGenerated]
		public string DealerId
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = "";

		[field: CompilerGenerated]
		public string ProductId
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = "";

		[field: CompilerGenerated]
		public int ProductQuality
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = 2;

		[field: CompilerGenerated]
		public string PackagingId
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = "";

		[field: CompilerGenerated]
		public int ProductUnits
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}

		[field: CompilerGenerated]
		public int Cash
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}

		[field: CompilerGenerated]
		public string ValuableId
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		} = "";

		[field: CompilerGenerated]
		public bool Success
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}

		[field: CompilerGenerated]
		public int MessageVariant
		{
			[CompilerGenerated]
			get;
			[CompilerGenerated]
			set;
		}
	}
	public static class EventRules
	{
		public const int DrySpellMissLimit = 4;

		public static bool DrySpellDue(SaveState state, Settings settings)
		{
			if (settings.DailyChance > 0.0)
			{
				return state.ConsecutiveDailyMisses >= 4;
			}
			return false;
		}

		public static uint Hash(string text)
		{
			uint num = 2166136261u;
			foreach (char c in text)
			{
				num ^= c;
				num *= 16777619;
			}
			return num;
		}

		public static double Roll(string seed, int day, string stream)
		{
			return (double)Hash(string.Concat(new string[5]
			{
				seed,
				":",
				day.ToString((IFormatProvider)(object)CultureInfo.InvariantCulture),
				":",
				stream
			})) / 4294967296.0;
		}

		public static int Pick(string seed, int day, string stream, int count)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (count > 0)
			{
				return (int)(Roll(seed, day, stream) * (double)count);
			}
			throw new ArgumentOutOfRangeException("count");
		}

		public static int ScheduledMinute(string seed, int day)
		{
			return 600 + Pick(seed, day, "time", 720);
		}

		public static bool Due(SaveState state, int day, int minutes)
		{
			if (state.Pending == null && day >= state.NextEligibleDay && day > state.LastAttemptDay)
			{
				return minutes >= ScheduledMinute(state.Seed, day);
			}
			return false;
		}

		public static bool Attempt(SaveState state, Settings settings, int day)
		{
			if (state.Pending != null || day < state.NextEligibleDay || day <= state.LastAttemptDay)
			{
				return false;
			}
			state.LastAttemptDay = day;
			if (!DrySpellDue(state, settings) && Roll(state.Seed, day, "event") >= settings.DailyChance)
			{
				if (settings.DailyChance > 0.0)
				{
					state.ConsecutiveDailyMisses = Math.Min(4, Math.Max(0, state.ConsecutiveDailyMisses) + 1);
				}
				return false;
			}
			state.ConsecutiveDailyMisses = 0;
			state.NextEligibleDay = day + settings.CooldownDays;
			state.EventCount++;
			return true;
		}
	}
	internal sealed class LootCompassMarkers
	{
		internal static readonly Color BrandGreen = new Color(0.224f, 1f, 0.078f, 1f);

		private readonly Dictionary<string, Element> elements = new Dictionary<string, Element>();

		private RectTransform? iconTemplate;

		private CompassManager? owner;

		public void Sync(SaveState state)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			if (!Singleton<CompassManager>.InstanceExists)
			{
				return;
			}
			CompassManager instance = Singleton<CompassManager>.Instance;
			if ((Object)(object)owner != (Object)null && (Object)(object)owner != (Object)(object)instance)
			{
				Clear();
			}
			owner = instance;
			Dictionary<string, DeadDrop> drops = new Dictionary<string, DeadDrop>();
			Enumerator<DeadDrop> enumerator = DeadDrop.DeadDrops.GetEnumerator();
			while (enumerator.MoveNext())
			{
				DeadDrop current = enumerator.Current;
				if ((Object)(object)current != (Object)null && (Object)(object)current.Storage != (Object)null)
				{
					drops[((object)current.GUID/*cast due to .constrained prefix*/).ToString()] = current;
				}
			}
			DeadDrop val3 = default(DeadDrop);
			state.LootDropGuids.RemoveAll((Predicate<string>)((string id) => drops.TryGetValue(id, ref val3) && ((StorageEntity)val3.Storage).ItemCount == 0));
			string[] array = Enumerable.ToArray<string>((System.Collections.Generic.IEnumerable<string>)elements.Keys);
			foreach (string text in array)
			{
				if (!state.LootDropGuids.Contains(text))
				{
					instance.RemoveElement(elements[text], true);
					elements.Remove(text);
				}
			}
			System.Collections.Generic.IEnumerator<string> enumerator2 = Enumerable.Distinct<string>((System.Collections.Generic.IEnumerable<string>)state.LootDropGuids).GetEnumerator();
			try
			{
				DeadDrop val = default(DeadDrop);
				while (((System.Collections.IEnumerator)enumerator2).MoveNext())
				{
					string current2 = enumerator2.Current;
					if (!elements.ContainsKey(current2) && drops.TryGetValue(current2, ref val))
					{
						if (iconTemplate == null)
						{
							iconTemplate = CreateIcon();
						}
						Element val2 = instance.AddElement(((Component)val).transform, iconTemplate, true);
						if (val2 == null)
						{
							throw new InvalidOperationException("Compass did not create a loot marker.");
						}
						elements.Add(current2, val2);
					}
				}
			}
			finally
			{
				((System.IDisposable)enumerator2)?.Dispose();
			}
		}

		internal static RectTransform CreateIcon()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_0080: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: 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_00f7: 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)
			RectTransform obj = new GameObject("DealersRetaliate_LootIcon").AddComponent<RectTransform>();
			obj.sizeDelta = new Vector2(28f, 28f);
			((Transform)Block(obj, "NeonGreenDiamond", new Vector2(20f, 20f), Vector2.zero, BrandGreen)).localRotation = Quaternion.Euler(0f, 0f, 45f);
			Block(obj, "DropBox", new Vector2(12f, 10f), new Vector2(0f, -1f), new Color(0.12f, 0.14f, 0.17f, 1f));
			Block(obj, "BoxLid", new Vector2(14f, 2f), new Vector2(0f, 5f), new Color(0.12f, 0.14f, 0.17f, 1f));
			Block(obj, "BoxSlot", new Vector2(6f, 2f), new Vector2(0f, 1f), BrandGreen);
			return obj;
		}

		private static RectTransform Block(RectTransform parent, string name, Vector2 size, Vector2 position, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0029: 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_0030: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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)
			GameObject val = new GameObject(name);
			RectTransform obj = val.AddComponent<RectTransform>();
			((Transform)obj).SetParent((Transform)(object)parent, false);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.5f);
			obj.pivot = val2;
			Vector2 anchorMin = (obj.anchorMax = val2);
			obj.anchorMin = anchorMin;
			obj.sizeDelta = size;
			obj.anchoredPosition = position;
			Image obj2 = val.AddComponent<Image>();
			((Graphic)obj2).color = color;
			((Graphic)obj2).raycastTarget = false;
			return obj;
		}

		public void Clear()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)owner != (Object)null))
				{
					return;
				}
				Enumerator<string, Element> enumerator = elements.Values.GetEnumerator();
				try
				{
					while (enumerator.MoveNext())
					{
						Element current = enumerator.Current;
						owner.RemoveElement(current, true);
					}
				}
				finally
				{
					((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
				}
			}
			finally
			{
				elements.Clear();
				owner = null;
				if ((Object)(object)iconTemplate != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)iconTemplate).gameObject);
				}
				iconTemplate = null;
			}
		}
	}
	internal sealed class LootObjectives
	{
		private readonly Dictionary<string, DeaddropQuest> configured = new Dictionary<string, DeaddropQuest>();

		private readonly List<RectTransform> iconTemplates = new List<RectTransform>();

		private readonly ObjectiveResolver<DeaddropQuest> resolver = new ObjectiveResolver<DeaddropQuest>();

		private readonly Dictionary<string, float> retryAt = new Dictionary<string, float>();

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

		public void Sync(SaveState state, bool ignoreRetry = false)
		{
			//IL_003b: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_039d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Invalid comparison between Unknown and I4
			if (!NetworkSingleton<QuestManager>.InstanceExists)
			{
				return;
			}
			Dictionary<string, DeadDrop> val = new Dictionary<string, DeadDrop>();
			Enumerator<DeadDrop> enumerator = DeadDrop.DeadDrops.GetEnumerator();
			while (enumerator.MoveNext())
			{
				DeadDrop current = enumerator.Current;
				if ((Object)(object)current != (Object)null && (Object)(object)current.Storage != (Object)null)
				{
					val[((object)current.GUID/*cast due to .constrained prefix*/).ToString()] = current;
				}
			}
			string[] array = state.LootDropGuids.ToArray();
			DeadDrop val2 = default(DeadDrop);
			foreach (string id in array)
			{
				if (!Enumerable.Any<LootDelivery>((System.Collections.Generic.IEnumerable<LootDelivery>)state.LootDeliveries, (Func<LootDelivery, bool>)((LootDelivery d) => d.DropGuid == id)) && val.TryGetValue(id, ref val2) && ((StorageEntity)val2.Storage).ItemCount != 0)
				{
					state.LootDeliveries.Add(new LootDelivery
					{
						DropGuid = id,
						QuestGuid = ((object)Guid.NewGuid()/*cast due to .constrained prefix*/).ToString(),
						Location = DropName(val2)
					});
				}
			}
			LootDelivery[] array2 = state.LootDeliveries.ToArray();
			DeadDrop val3 = default(DeadDrop);
			float num = default(float);
			DeaddropQuest val6 = default(DeaddropQuest);
			foreach (LootDelivery delivery in array2)
			{
				if (!val.TryGetValue(delivery.DropGuid, ref val3) || (!ignoreRetry && retryAt.TryGetValue(delivery.DropGuid, ref num) && Time.realtimeSinceStartup < num))
				{
					continue;
				}
				try
				{
					if (((StorageEntity)val3.Storage).ItemCount == 0)
					{
						DeaddropQuest val4 = resolver.Resolve(delivery.DropGuid, (DeaddropQuest q) => (Object)(object)q != (Object)null, (string _) => FindDeliveryQuest(delivery), () => (DeaddropQuest?)null);
						if ((Object)(object)val4 != (Object)null)
						{
							int instanceID = ((Object)val4).GetInstanceID();
							((Quest)val4).autoInitialize = false;
							ObjectiveIdentity.AdoptNativeGuid(delivery, ((object)((Quest)val4).GUID/*cast due to .constrained prefix*/).ToString());
							if ((int)((Quest)val4).State == 1)
							{
								((Quest)val4).Complete(false);
							}
							ObjectiveHudGuard.Forget(instanceID);
						}
						RetireOwnedCopies(delivery, val4);
						state.LootDropGuids.Remove(delivery.DropGuid);
						state.LootDeliveries.Remove(delivery);
						configured.Remove(delivery.DropGuid);
						resolver.Remove(delivery.DropGuid);
						retryAt.Remove(delivery.DropGuid);
						lastError.Remove(delivery.DropGuid);
						continue;
					}
					if (string.IsNullOrWhiteSpace(delivery.QuestGuid))
					{
						delivery.QuestGuid = ((object)Guid.NewGuid()/*cast due to .constrained prefix*/).ToString();
					}
					DeaddropQuest val5 = resolver.Resolve(delivery.DropGuid, (DeaddropQuest q) => (Object)(object)q != (Object)null, (string _) => FindDeliveryQuest(delivery), delegate
					{
						DeaddropQuest val7 = NetworkSingleton<QuestManager>.Instance.CreateDeaddropCollectionQuest(delivery.DropGuid, delivery.QuestGuid);
						if ((Object)(object)val7 != (Object)null)
						{
							((Quest)val7).autoInitialize = false;
						}
						return val7;
					});
					if ((Object)(object)val5 == (Object)null)
					{
						Defer(delivery, "Native factory returned no quest; checking existing quest registries again on retry.");
						continue;
					}
					((Quest)val5).autoInitialize = false;
					ObjectiveIdentity.AdoptNativeGuid(delivery, ((object)((Quest)val5).GUID/*cast due to .constrained prefix*/).ToString());
					ObjectiveHudGuard.Track((Quest)(object)val5);
					RetireOwnedCopies(delivery, val5);
					if (!configured.TryGetValue(delivery.DropGuid, ref val6) || (Object)(object)val6 != (Object)(object)val5)
					{
						Configure(val5, delivery);
						configured[delivery.DropGuid] = val5;
					}
					retryAt.Remove(delivery.DropGuid);
					lastError.Remove(delivery.DropGuid);
				}
				catch (System.Exception ex)
				{
					Defer(delivery, ((object)ex).ToString());
				}
			}
		}

		private void Defer(LootDelivery delivery, string error)
		{
			retryAt[delivery.DropGuid] = Time.realtimeSinceStartup + 5f;
			string text = default(string);
			if (!lastError.TryGetValue(delivery.DropGuid, ref text) || text != error)
			{
				MelonLogger.Warning(string.Concat(new string[6] { "Dealers Retaliate: objective at ", delivery.Location, " will retry in 5 seconds. Other deliveries remain active. Quest=", delivery.QuestGuid, ". ", error }));
			}
			lastError[delivery.DropGuid] = error;
		}

		private static List<Quest> QuestSnapshot()
		{
			Dictionary<int, Quest> val = new Dictionary<int, Quest>();
			Enumerator<Quest> enumerator = Quest.Quests.GetEnumerator();
			while (enumerator.MoveNext())
			{
				Quest current = enumerator.Current;
				if ((Object)(object)current != (Object)null)
				{
					val[((Object)current).GetInstanceID()] = current;
				}
			}
			enumerator = Quest.ActiveQuests.GetEnumerator();
			while (enumerator.MoveNext())
			{
				Quest current2 = enumerator.Current;
				if ((Object)(object)current2 != (Object)null)
				{
					val[((Object)current2).GetInstanceID()] = current2;
				}
			}
			Enumerator<DeaddropQuest> enumerator2 = DeaddropQuest.DeaddropQuests.GetEnumerator();
			while (enumerator2.MoveNext())
			{
				DeaddropQuest current3 = enumerator2.Current;
				if ((Object)(object)current3 != (Object)null)
				{
					val[((Object)current3).GetInstanceID()] = (Quest)(object)current3;
				}
			}
			return Enumerable.ToList<Quest>((System.Collections.Generic.IEnumerable<Quest>)val.Values);
		}

		private static DeaddropQuest? FindDeliveryQuest(LootDelivery delivery)
		{
			//IL_0018: 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_004a: 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)
			Dictionary<int, DeaddropQuest> val = new Dictionary<int, DeaddropQuest>();
			Enumerator<Quest> enumerator = QuestSnapshot().GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					DeaddropQuest component = ((Component)enumerator.Current).GetComponent<DeaddropQuest>();
					if ((Object)(object)component != (Object)null && (Object)(object)component.Drop != (Object)null && ((object)component.Drop.GUID/*cast due to .constrained prefix*/).ToString() == delivery.DropGuid)
					{
						val[((Object)component).GetInstanceID()] = component;
					}
				}
			}
			finally
			{
				((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
			}
			return Enumerable.FirstOrDefault<DeaddropQuest>((System.Collections.Generic.IEnumerable<DeaddropQuest>)val.Values, (Func<DeaddropQuest, bool>)((DeaddropQuest q) => string.Equals(((object)((Quest)q).GUID/*cast due to .constrained prefix*/).ToString(), delivery.QuestGuid, (StringComparison)5))) ?? Enumerable.FirstOrDefault<DeaddropQuest>((System.Collections.Generic.IEnumerable<DeaddropQuest>)val.Values, (Func<DeaddropQuest, bool>)((DeaddropQuest q) => ObjectiveIdentity.Owns(delivery, ((object)((Quest)q).GUID/*cast due to .constrained prefix*/).ToString(), ((Quest)q).Title, ((Quest)q).Description))) ?? Enumerable.FirstOrDefault<DeaddropQuest>((System.Collections.Generic.IEnumerable<DeaddropQuest>)val.Values, (Func<DeaddropQuest, bool>)((DeaddropQuest q) => (int)((Quest)q).State == 1)) ?? Enumerable.FirstOrDefault<DeaddropQuest>((System.Collections.Generic.IEnumerable<DeaddropQuest>)val.Values);
		}

		private static void RetireOwnedCopies(LootDelivery delivery, DeaddropQuest? canonical)
		{
			//IL_0007: 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_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Invalid comparison between Unknown and I4
			List<Quest> val = QuestSnapshot();
			Enumerator<Quest> enumerator = val.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					Quest current = enumerator.Current;
					DeaddropQuest component = ((Component)current).GetComponent<DeaddropQuest>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component.Drop == (Object)null) && !(((object)component.Drop.GUID/*cast due to .constrained prefix*/).ToString() != delivery.DropGuid))
					{
						string id = ((object)((Quest)component).GUID/*cast due to .constrained prefix*/).ToString();
						if (!string.Equals(id, delivery.QuestGuid, (StringComparison)5) && !Enumerable.Any<string>((System.Collections.Generic.IEnumerable<string>)delivery.PreviousQuestGuids, (Func<string, bool>)((string previous) => string.Equals(previous, id, (StringComparison)5))))
						{
							delivery.PreviousQuestGuids.Add(id);
						}
					}
				}
			}
			finally
			{
				((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
			}
			enumerator = val.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					Quest current2 = enumerator.Current;
					if ((Object)(object)current2 == (Object)null)
					{
						continue;
					}
					if ((Object)(object)canonical != (Object)null && ((Object)current2).GetInstanceID() == ((Object)canonical).GetInstanceID())
					{
						while (Quest.Quests.Remove(current2))
						{
						}
					}
					else
					{
						if (!ObjectiveIdentity.Owns(delivery, ((object)current2.GUID/*cast due to .constrained prefix*/).ToString(), current2.Title, current2.Description))
						{
							continue;
						}
						DeaddropQuest component2 = ((Component)current2).GetComponent<DeaddropQuest>();
						if ((Object)(object)component2 != (Object)null && (Object)(object)component2.Drop != (Object)null && ((object)component2.Drop.GUID/*cast due to .constrained prefix*/).ToString() != delivery.DropGuid)
						{
							continue;
						}
						DestroyObjectiveHud(current2);
						current2.DestroyJournalEntry();
						while (Quest.ActiveQuests.Remove(current2))
						{
						}
						while (Quest.Quests.Remove(current2))
						{
						}
						if ((Object)(object)component2 != (Object)null)
						{
							while (DeaddropQuest.DeaddropQuests.Remove(component2))
							{
							}
						}
						ObjectiveHudGuard.Forget(((Object)current2).GetInstanceID());
						if ((int)current2.State == 1)
						{
							current2.Cancel(false);
						}
					}
				}
			}
			finally
			{
				((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private void Configure(DeaddropQuest quest, LootDelivery delivery)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Invalid comparison between Unknown and I4
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Invalid comparison between Unknown and I4
			((Quest)quest).title = delivery.ObjectiveTitle;
			((Quest)quest).Description = delivery.ObjectiveDescription;
			((Quest)quest).SetSubtitle("");
			((Quest)quest).ConfigureExpiry(false, default(GameDateTime));
			((Quest)quest).ShouldSendExpiryReminder = false;
			((Quest)quest).ShouldSendExpiredNotification = false;
			((Quest)quest).CompletionXP = 0;
			((Quest)quest).PlayQuestCompleteSound = false;
			((Quest)quest).TrackOnBegin = true;
			Enumerator<QuestEntry> enumerator = ((Quest)quest).Entries.GetEnumerator();
			while (enumerator.MoveNext())
			{
				QuestEntry current = enumerator.Current;
				current.AutoCreatePoI = false;
				current.AutoUpdatePoILocation = false;
				current.PoILocation = null;
				current.DestroyPoI();
				if (current.compassElement != null && Singleton<CompassManager>.InstanceExists)
				{
					Singleton<CompassManager>.Instance.RemoveElement(current.compassElement, true);
				}
				current.compassElement = null;
			}
			if (((Quest)quest).Entries.Count > 0)
			{
				((Quest)quest).Entries[0].SetEntryTitle(delivery.ObjectiveDescription);
			}
			RectTransform val = CreatePortraitIcon(string.IsNullOrWhiteSpace(delivery.DealerId) ? null : NPCManager.GetNPC(delivery.DealerId));
			iconTemplates.Add(val);
			((Quest)quest).IconPrefab = val;
			DestroyObjectiveHud((Quest)(object)quest);
			((Quest)quest).DestroyJournalEntry();
			if ((int)((Quest)quest).State != 1)
			{
				((Quest)quest).SetQuestState((EQuestState)1, false);
			}
			((Quest)quest).SetIsTracked(true);
			((Quest)quest).SetupJournalEntry();
			if (!((Quest)quest).hudUIExists)
			{
				((Quest)quest).SetupHUDUI();
			}
			enumerator = ((Quest)quest).Entries.GetEnumerator();
			while (enumerator.MoveNext())
			{
				QuestEntry current2 = enumerator.Current;
				if ((int)current2.State == 1)
				{
					if ((Object)(object)current2.entryUI == (Object)null)
					{
						current2.CreateEntryUI();
					}
					current2.UpdateEntryUI();
				}
			}
			((Quest)quest).UpdateHUDUI();
			if ((Object)(object)((Quest)quest).hudUI != (Object)null)
			{
				LayoutRebuilder.ForceRebuildLayoutImmediate(((Component)((Quest)quest).hudUI).GetComponent<RectTransform>());
			}
			((Quest)quest).HasChanged = true;
		}

		private static void DestroyObjectiveHud(Quest quest)
		{
			QuestHUDUI hudUI = quest.hudUI;
			if ((Object)(object)hudUI != (Object)null)
			{
				((Component)hudUI).gameObject.SetActive(false);
				hudUI.Destroy();
			}
			quest.DestroyHUDUI();
			Enumerator<QuestEntry> enumerator = quest.Entries.GetEnumerator();
			while (enumerator.MoveNext())
			{
				enumerator.Current.entryUI = null;
			}
		}

		private static RectTransform CreatePortraitIcon(NPC? dealer)
		{
			//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_0039: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)dealer == (Object)null || (Object)(object)dealer.MugshotSprite == (Object)null)
			{
				return LootCompassMarkers.CreateIcon();
			}
			GameObject val = new GameObject("DealersRetaliate_DealerObjectiveIcon");
			RectTransform val2 = val.AddComponent<RectTransform>();
			val2.sizeDelta = new Vector2(32f, 32f);
			Image obj = val.AddComponent<Image>();
			obj.sprite = dealer.MugshotSprite;
			obj.preserveAspect = true;
			((Graphic)obj).raycastTarget = false;
			Outline obj2 = val.AddComponent<Outline>();
			((Shadow)obj2).effectColor = LootCompassMarkers.BrandGreen;
			((Shadow)obj2).effectDistance = new Vector2(1f, -1f);
			RectTransform obj3 = LootCompassMarkers.CreateIcon();
			((Object)obj3).name = "RetaliationDropBadge";
			((Transform)obj3).SetParent((Transform)(object)val2, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(1f, 0f);
			obj3.anchorMax = val3;
			obj3.anchorMin = val3;
			obj3.anchoredPosition = new Vector2(-3f, 3f);
			((Transform)obj3).localScale = new Vector3(0.45f, 0.45f, 1f);
			return val2;
		}

		internal static string DropName(DeadDrop drop)
		{
			if (!string.IsNullOrWhiteSpace(drop.DeadDropName))
			{
				return drop.DeadDropName;
			}
			return drop.DeadDropDescription;
		}

		public void Clear()
		{
			//IL_0037: 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)
			configured.Clear();
			ObjectiveHudGuard.Clear();
			resolver.Clear();
			retryAt.Clear();
			lastError.Clear();
			Enumerator<RectTransform> enumerator = iconTemplates.GetEnumerator();
			try
			{
				while (enumerator.MoveNext())
				{
					RectTransform current = enumerator.Current;
					if ((Object)(object)current != (Object)null)
					{
						Object.Destroy((Object)(object)((Component)current).gameObject);
					}
				}
			}
			finally
			{
				((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
			}
			iconTemplates.Clear();
		}
	}
	public static class LootPackagingRules
	{
		public static int Capacity(string packaging)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if ((packaging == null || packaging.Length != 0) && !(packaging == "baggie"))
			{
				if (packaging == "brick")
				{
					return 20;
				}
				throw new ArgumentOutOfRangeException("packaging");
			}
			return 1;
		}

		public static int MaxPackages(double packageValue, int budget, int maximum, int stackLimit)
		{
			if (double.IsNaN(packageValue) || double.IsInfinity(packageValue) || packageValue <= 0.0 || budget <= 0)
			{
				return 0;
			}
			return (int)Math.Max(0.0, Math.Min((double)Math.Min(maximum, stackLimit), Math.Floor((double)budget / packageValue)));
		}

		public static int BrickCount(int cap, double roll, double twoBrickChance)
		{
			if (cap >= 1)
			{
				if (cap < 2 || !(roll < twoBrickChance))
				{
					return 1;
				}
				return 2;
			}
			return 0;
		}

		public static int BaggieCount(int cap, int minimum, double roll)
		{
			if (cap <= 0)
			{
				return 0;
			}
			int num = Math.Min(cap, Math.Max(1, minimum));
			return num + (int)(roll * (double)(cap - num + 1));
		}

		public static bool IsBrickHaul(PendingReward reward)
		{
			if (reward.PackagingId == "brick")
			{
				return reward.ProductUnits > 0;
			}
			return false;
		}

		public static void EnforceExclusiveBricks(PendingReward reward)
		{
			if (IsBrickHaul(reward))
			{
				reward.Cash = 0;
				reward.ValuableId = "";
			}
		}
	}
	internal static class LootProductIds
	{
		public static string Canonical(string id)
		{
			string text = id.ToLowerInvariant();
			if (!(text == "defaultweed"))
			{
				if (!(text == "defaultmeth"))
				{
					if (!(text == "defaultcocaine"))
					{
						if (text == "defaultshroom")
						{
							return "shroom";
						}
						return id.ToLowerInvariant();
					}
					return "cocaine";
				}
				return "meth";
			}
			return "ogkush";
		}

		public static bool IsBaseProduct(string id)
		{
			string text = Canonical(id);
			if (text != null)
			{
				switch (text.Length)
				{
				case 6:
				{
					char c = text[0];
					if (c != 'o')
					{
						if (c != 's' || !(text == "shroom"))
						{
							break;
						}
					}
					else if (!(text == "ogkush"))
					{
						break;
					}
					goto IL_00d1;
				}
				case 10:
				{
					char c = text[0];
					if (c != 'g')
					{
						if (c != 's' || !(text == "sourdiesel"))
						{
							break;
						}
					}
					else if (!(text == "greencrack"))
					{
						break;
					}
					goto IL_00d1;
				}
				case 16:
					if (!(text == "granddaddypurple"))
					{
						break;
					}
					goto IL_00d1;
				case 4:
					if (!(text == "meth"))
					{
						break;
					}
					goto IL_00d1;
				case 7:
					{
						if (!(text == "cocaine"))
						{
							break;
						}
						goto IL_00d1;
					}
					IL_00d1:
					return true;
				}
			}
			return false;
		}
	}
	internal static class LootProducts
	{
		private static readonly HashSet<string> reported = new HashSet<string>();

		public static ProductDefinition? Resolve(string id)
		{
			string text = LootProductIds.Canonical(id);
			if (!LootProductIds.IsBaseProduct(text))
			{
				return null;
			}
			try
			{
				if (!Registry.ItemExists(text))
				{
					return null;
				}
				ItemDefinition item = Registry.GetItem(text);
				ProductDefinition val = ((item != null) ? ((Il2CppObjectBase)item).TryCast<ProductDefinition>() : null);
				if (!HasDrugType(val))
				{
					return null;
				}
				ItemInstance defaultInstance = ((ItemDefinition)val).GetDefaultInstance(1);
				ProductItemInstance val2 = ((defaultInstance != null) ? ((Il2CppObjectBase)defaultInstance).TryCast<ProductItemInstance>() : null);
				return (val2 != null && (Object)(object)((BaseItemInstance)val2).Icon != (Object)null) ? val : null;
			}
			catch (System.Exception ex)
			{
				if (reported.Add(text))
				{
					MelonLogger.Warning("Dealers Retaliate: rejected incomplete loot product " + text + ". " + ex.Message);
				}
				return null;
			}
		}

		private static bool HasDrugType(ProductDefinition? product)
		{
			if ((O