Decompiled source of DealersSendTexts v2.2.0

Main-DealersSendTexts-2.2.0.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using DealersSendTexts;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppScheduleOne;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Economy;
using Il2CppScheduleOne.GameTime;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.Map;
using Il2CppScheduleOne.Messaging;
using Il2CppScheduleOne.Money;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.Persistence;
using Il2CppScheduleOne.Persistence.Datas;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.Product;
using Il2CppScheduleOne.Quests;
using Il2CppSystem.Collections.Generic;
using MelonLoader;
using MelonLoader.Preferences;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(DealerText), "Dealers Send Texts", "2.1.0", "GuysWeForgotDre", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: AssemblyTitle("Dealers Send Texts")]
[assembly: AssemblyDescription("Dealers text updates on deals and daily summary in Schedule One")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Dealers Send Texts")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("51af033c-2fba-4fd9-950e-a5fb45e211c4")]
[assembly: AssemblyFileVersion("2.1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("2.1.0.0")]
namespace DealersSendTexts;

public static class AlertManager
{
	public static void DailySummary(DealerManager stats)
	{
		if (!((NPC)stats.Dealer).RelationData.Unlocked)
		{
			return;
		}
		DealerPrefs dealerPrefs = DealerPrefs.Prefs(((NPC)stats.Dealer).FirstName);
		DealerState state = stats.State;
		Dictionary<string, int> dictionary = new Dictionary<string, int>();
		HashSet<string> hashSet = new HashSet<string>();
		float num = 0f;
		foreach (SaleData dailySale in state.DailySales)
		{
			num += dailySale.Payment;
			if (!dictionary.ContainsKey(dailySale.Product))
			{
				dictionary.Add(dailySale.Product, dailySale.Quantity);
			}
			else
			{
				dictionary[dailySale.Product] += dailySale.Quantity;
			}
			hashSet.Add(dailySale.Location);
		}
		if (dealerPrefs.GetShowSummary())
		{
			string text = MoneyManager.FormatAmount(num, false, true);
			string text2 = "";
			foreach (string key in dictionary.Keys)
			{
				text2 += $"\n{Util.GetName(key)} ({dictionary[key]})";
			}
			string text3 = $"DAILY SUMMARY\nDid {state.DailySales.Count} deals in {hashSet.Count} locations; {state.Failures.Count} failed.\nMade {text} from:{text2}";
			if (stats.IsDealerHurt(out var status))
			{
				text3 = text3 + "\nDealer " + status;
			}
			MessageManager.Send(stats.Dealer, EIcon.Summary, text3, notify: false);
		}
		if (dealerPrefs.GetSendCustomers())
		{
			string text4 = $"CUSTOMER LOG ({state.TodaysSales.Count})";
			foreach (string key2 in state.TodaysSales.Keys)
			{
				stats.IsCustomerHurt(key2, out var status2);
				text4 = text4 + "\n" + key2 + ": " + state.TodaysSales[key2] + " " + status2;
			}
			MessageManager.Send(stats.Dealer, EIcon.Customer, text4, notify: false);
		}
		if (dealerPrefs.GetSendLocations())
		{
			string text5 = $"LOCATIONS ({hashSet.Count})";
			foreach (string item in hashSet)
			{
				text5 = text5 + "\n" + item;
			}
			MessageManager.Send(stats.Dealer, EIcon.Location, text5, notify: false);
		}
		if (dealerPrefs.GetSendFailures() && state.Failures.Count > 0)
		{
			string text6 = $"FAILURES ({state.Failures.Count})";
			foreach (FailureKey failure in state.Failures)
			{
				text6 = text6 + "\n" + failure.Customer + " " + Util.Prefix(failure.Location);
			}
			MessageManager.Send(stats.Dealer, EIcon.Failure, text6, notify: false);
		}
		state.ClearAll(daily: true);
	}

	public static void NPCInjuryAlert(NPC npc, EIcon type)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		string text = LocationManager.Describe(((Component)npc).transform.position, "at");
		string text2 = Vector3.Distance(Player.Local.PlayerBasePosition, ((Component)npc).transform.position).ToString("#.#");
		string text3 = type switch
		{
			EIcon.HasDied => "died", 
			EIcon.KnockOut => "been knocked out", 
			_ => "an unknown alert", 
		};
		Dealer val = (Dealer)(object)((npc is Dealer) ? npc : null);
		if (val != null)
		{
			EMsg dealerInjury = DealerPrefs.Prefs(((NPC)val).FirstName).GetDealerInjury();
			string message = $"I have {text3} in {npc.Region} {text}, {text2} meters from you.";
			if (dealerInjury != EMsg.Disable)
			{
				MessageManager.Send(val, type, message, dealerInjury == EMsg.Notify);
			}
			return;
		}
		foreach (DealerManager value2 in DealerManager.Dealers.Values)
		{
			foreach (string key in value2.Customers.Keys)
			{
				if (key == npc.fullName)
				{
					EMsg customerInjury = DealerPrefs.Prefs(((NPC)value2.Dealer).FirstName).GetCustomerInjury();
					if (!value2.State.RecentSale.TryGetValue(key, out var value))
					{
						value = "Never";
					}
					string message2 = $"{npc.fullName} has {text3} in {npc.Region} {text}, {text2} meters from you. Last sale was {value}.";
					if (customerInjury != EMsg.Disable)
					{
						MessageManager.Send(value2.Dealer, type, message2, customerInjury == EMsg.Notify);
					}
				}
			}
		}
	}

	public static void CheckProductAlert(Dealer dealer)
	{
		//IL_002c: 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_0056: Unknown result type (might be due to invalid IL or missing references)
		DealerPrefs dealerPrefs = DealerPrefs.Prefs(((NPC)dealer).FirstName);
		DealerManager stats = DealerManager.GetStats(dealer);
		stats.RefreshInventory();
		string text = MoneyManager.FormatAmount(dealer.Cash, false, false);
		string text2 = Vector3.Distance(Player.Local.PlayerBasePosition, ((Component)dealer).transform.position).ToString("#.#");
		string text3 = LocationManager.Describe(((Component)dealer).transform.position, "at");
		float num = 0f;
		foreach (string key in stats.State.Products.Keys)
		{
			num += (float)stats.State.Products[key];
		}
		if (dealerPrefs.GetCheckCash() != EMsg.Disable && dealer.Cash >= dealerPrefs.GetCashAlert())
		{
			MessageManager.Send(dealer, EIcon.ProdAlert, $"Cash threshold reached: {text}.\n{num} products left; I'm {text2} meters from you, {text3}.", dealerPrefs.GetCheckCash() == EMsg.Notify);
		}
		if (dealerPrefs.GetCheckProduct() != EMsg.Disable && num <= dealerPrefs.GetProductAlert())
		{
			MessageManager.Send(dealer, EIcon.ProdAlert, $"Product threshold reached: {num}.\n{text} cash; I'm {text2} meters from you, {text3}.", dealerPrefs.GetCheckProduct() == EMsg.Notify);
		}
	}
}
public class DealerPrefs
{
	public static DealerPrefs Master;

	private static bool MasterOnly;

	private static bool Initialized = false;

	private const string MasterName = "_Master";

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

	private MelonPreferences_Category DealerGroup;

	private MelonPreferences_Entry<bool> OverrideMaster;

	private MelonPreferences_Entry<bool> CreateDealers;

	private MelonPreferences_Entry<EMsg> ShowStarted;

	private MelonPreferences_Entry<EMsg> ShowSuccess;

	private MelonPreferences_Entry<EMsg> ShowFailure;

	private MelonPreferences_Entry<bool> ShowSummary;

	private MelonPreferences_Entry<bool> SendCustomers;

	private MelonPreferences_Entry<bool> SendLocations;

	private MelonPreferences_Entry<bool> SendFailures;

	private MelonPreferences_Entry<EMsg> CheckProduct;

	private MelonPreferences_Entry<float> ProductAlert;

	private MelonPreferences_Entry<EMsg> CheckCash;

	private MelonPreferences_Entry<float> CashAlert;

	private MelonPreferences_Entry<EMsg> CustomerInjury;

	private MelonPreferences_Entry<EMsg> DealerInjury;

	private MelonPreferences_Entry<EMsg> IsStuckAlert;

	private MelonPreferences_Entry<int> IsStuckCount;

	private MelonPreferences_Entry<int> IsStuckRadius;

	private MelonPreferences_Entry<EMsg> Navigation;

	private MelonPreferences_Entry<int> NavDeltaTime;

	public static void Initialize()
	{
		if (!Initialized)
		{
			Master = AddDealer("_Master");
			MasterOnly = !Master.CreateDealers.Value;
			Initialized = true;
		}
	}

	public EMsg GetShowStarted()
	{
		return GetValue<EMsg>(ShowStarted, Master.ShowStarted);
	}

	public EMsg GetShowSuccess()
	{
		return GetValue<EMsg>(ShowSuccess, Master.ShowSuccess);
	}

	public EMsg GetShowFailure()
	{
		return GetValue<EMsg>(ShowFailure, Master.ShowFailure);
	}

	public bool GetShowSummary()
	{
		return GetValue<bool>(ShowSummary, Master.ShowSummary);
	}

	public bool GetSendCustomers()
	{
		return GetValue<bool>(SendCustomers, Master.SendCustomers);
	}

	public bool GetSendLocations()
	{
		return GetValue<bool>(SendLocations, Master.SendLocations);
	}

	public bool GetSendFailures()
	{
		return GetValue<bool>(SendFailures, Master.SendFailures);
	}

	public EMsg GetCheckProduct()
	{
		return GetValue<EMsg>(CheckProduct, Master.CheckProduct);
	}

	public float GetProductAlert()
	{
		return GetValue<float>(ProductAlert, Master.ProductAlert);
	}

	public EMsg GetCheckCash()
	{
		return GetValue<EMsg>(CheckCash, Master.CheckCash);
	}

	public float GetCashAlert()
	{
		return GetValue<float>(CashAlert, Master.CashAlert);
	}

	public EMsg GetCustomerInjury()
	{
		return GetValue<EMsg>(CustomerInjury, Master.CustomerInjury);
	}

	public EMsg GetDealerInjury()
	{
		return GetValue<EMsg>(DealerInjury, Master.DealerInjury);
	}

	public EMsg GetIsStuckAlert()
	{
		return GetValue<EMsg>(IsStuckAlert, Master.IsStuckAlert);
	}

	public int GetIsStuckCount()
	{
		return GetValue<int>(IsStuckCount, Master.IsStuckCount);
	}

	public int GetIsStuckRadius()
	{
		return GetValue<int>(IsStuckRadius, Master.IsStuckRadius);
	}

	public EMsg GetNavigation()
	{
		return GetValue<EMsg>(Navigation, Master.Navigation);
	}

	public int GetINavDeltaTime()
	{
		return GetValue<int>(NavDeltaTime, Master.NavDeltaTime);
	}

	public static DealerPrefs Prefs(string firstName)
	{
		if (!MasterOnly)
		{
			if (!_dealerSettings.TryGetValue(firstName, out var value))
			{
				return AddDealer(firstName);
			}
			return value;
		}
		return Master;
	}

	private static DealerPrefs AddDealer(string name)
	{
		DealerPrefs dealerPrefs = new DealerPrefs
		{
			DealerGroup = MelonPreferences.CreateCategory("DealersSendTexts_" + name, name + " Preferences")
		};
		if (name == "_Master")
		{
			dealerPrefs.CreateDealers = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "00_CreateDealers", false, "Create settings per dealer [requires restart]", (string)null, false, false, (ValueValidator)null, (string)null);
			((MelonPreferences_Entry)dealerPrefs.CreateDealers).Comment = "Create separate settings panel for each dealer. Requires restart. (\"default\" false but will not auto delete if reset)";
		}
		else
		{
			dealerPrefs.OverrideMaster = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "00_OverrideMaster", false, "[Override Master]: Use these settings instead [false]", (string)null, false, false, (ValueValidator)null, (string)null);
			((MelonPreferences_Entry)dealerPrefs.OverrideMaster).Comment = "Use individual dealer profile settings instead of generic master settings (default false)";
		}
		dealerPrefs.ShowStarted = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "01_ShowStarted", EMsg.Silent, "Text: When a new deal is started [default silent]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.ShowSuccess = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "02_ShowSuccess", EMsg.Silent, "Text: When deal successfully completed [silent]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.ShowFailure = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "03_ShowFailure", EMsg.Notify, "Text: When deal failed / timed out [notify]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.ShowSummary = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "04_ShowSummary", true, "Daily: Summary of deals completed that day [true]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.SendCustomers = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "05_SendCustomers", false, "Daily: Time of today's deal, per customer [false]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.SendLocations = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "06_SendLocations", false, "Daily: List of locations visited today [false]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.SendFailures = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "07_SendFailures", true, "Daily: List of failed customers / locations [true]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.CheckProduct = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "08_CheckProduct", EMsg.Silent, "Alert: When product count below threshold [silent]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.ProductAlert = dealerPrefs.DealerGroup.CreateEntry<float>(name + "09_ProductAlert", 20f, "--Product count (total) to trigger alert [20]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.CheckCash = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "10_CheckCashLevel", EMsg.Disable, "Alert: When cash is above threshold [disable]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.CashAlert = dealerPrefs.DealerGroup.CreateEntry<float>(name + "11_CashAlert", 50000f, "--Minimum cash amount to trigger alert [50,000]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.CustomerInjury = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "12_CustomerInjury", EMsg.Silent, "Alert: When customer knocked out or dies [silent]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.DealerInjury = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "13_DealerInjury", EMsg.Notify, "Alert: When dealer is knocked out or dies [notify]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.IsStuckAlert = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "14_IsStuckAlert", EMsg.Notify, "Alert: When dealer hasn't moved in too long [notify]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.IsStuckCount = dealerPrefs.DealerGroup.CreateEntry<int>(name + "15_IsStuckCount", 4, "Failed move checks before sending stuck alert [4]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.IsStuckRadius = dealerPrefs.DealerGroup.CreateEntry<int>(name + "16_IsStuckRadius", 5, "Min. move distance to not be considered stuck [5]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.Navigation = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "17_Navigation", EMsg.Notify, "Text: When new navigation target set [notify]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.NavDeltaTime = dealerPrefs.DealerGroup.CreateEntry<int>(name + "18_NavDeltaTime", 5, "Min. time between navigation messages (minutes) [5]", (string)null, false, false, (ValueValidator)null, (string)null);
		((MelonPreferences_Entry)dealerPrefs.ShowStarted).Comment = "Send a text message when a new deal is started (default silent)";
		((MelonPreferences_Entry)dealerPrefs.ShowSuccess).Comment = "Send a text message when a deal is successfully completed (default silent)";
		((MelonPreferences_Entry)dealerPrefs.ShowFailure).Comment = "Send a text message when a deal is failed or timed out (default notify)";
		((MelonPreferences_Entry)dealerPrefs.ShowSummary).Comment = "Send a summary each night of deals completed / failed and money made (default true)";
		((MelonPreferences_Entry)dealerPrefs.SendCustomers).Comment = "Send a summary each night time of deal with each customer (default false)";
		((MelonPreferences_Entry)dealerPrefs.SendLocations).Comment = "Send a summary each night of locations visited that day for successful deals (default false)";
		((MelonPreferences_Entry)dealerPrefs.SendFailures).Comment = "Send a summary each night of failed customers / locations (default true)";
		((MelonPreferences_Entry)dealerPrefs.CheckProduct).Comment = "Send a text message when product count (any type) dips below a threshold (default silent)";
		((MelonPreferences_Entry)dealerPrefs.ProductAlert).Comment = "Threshold to send low product alert (total count of product, e.g. brick = 20) (default 20)";
		((MelonPreferences_Entry)dealerPrefs.CheckCash).Comment = "Send a text message when cash on hand is above a threshold (default disable)";
		((MelonPreferences_Entry)dealerPrefs.CashAlert).Comment = "Amount of cash on hand to send cash alert (default 50,000)";
		((MelonPreferences_Entry)dealerPrefs.CustomerInjury).Comment = "Send a text message when a customer is knocked out or is killed (default silent)";
		((MelonPreferences_Entry)dealerPrefs.DealerInjury).Comment = "Send a text message when a dealer is knocked out or is killed (default notify)";
		((MelonPreferences_Entry)dealerPrefs.IsStuckAlert).Comment = "Send a text message when dealer has not moved from one spot for too long (default notify)";
		((MelonPreferences_Entry)dealerPrefs.IsStuckCount).Comment = "Number of failed checks to send stuck alert (every 20 min) (default 4)";
		((MelonPreferences_Entry)dealerPrefs.IsStuckRadius).Comment = "Minimum distance dealer must move to not be considered stuck (default 5)";
		((MelonPreferences_Entry)dealerPrefs.Navigation).Comment = "Send a message when setting a new movement destination (default notify)";
		((MelonPreferences_Entry)dealerPrefs.NavDeltaTime).Comment = "Minimum cooldown minutes between navigation messages in minutes (default 5)";
		dealerPrefs.DealerGroup.SetFilePath(ModSaveData.PrefsPath);
		_dealerSettings[name] = dealerPrefs;
		return dealerPrefs;
	}

	public static void ClearAll()
	{
		foreach (DealerPrefs value in _dealerSettings.Values)
		{
			value.DealerGroup.Entries.Clear();
			value.DealerGroup = null;
		}
		_dealerSettings.Clear();
		Initialized = false;
	}

	private T GetValue<T>(MelonPreferences_Entry<T> local, MelonPreferences_Entry<T> master)
	{
		if (!MasterOnly && OverrideMaster.Value)
		{
			return local.Value;
		}
		return master.Value;
	}
}
[Serializable]
public class SaleData
{
	public string Product;

	public string Description;

	public string Customer;

	public string Location;

	public string Window;

	public string Started;

	public string Cost;

	public string Status;

	public string Date;

	public float Payment;

	public int Quantity;

	public int StartTime;

	[JsonIgnore]
	public DealerManager Stats;

	public SaleData()
	{
	}

	public SaleData(Contract contract, DealerManager stats)
	{
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		Stats = stats;
		Product = contract.ProductList.entries[0].ProductID;
		Description = contract.ProductList.GetCommaSeperatedString();
		Customer component = ((Component)contract.Customer).GetComponent<Customer>();
		object obj;
		if (component == null)
		{
			obj = null;
		}
		else
		{
			NPC nPC = component.NPC;
			obj = ((nPC != null) ? nPC.fullName : null);
		}
		if (obj == null)
		{
			obj = "Unknown";
		}
		Customer = (string)obj;
		Location = contract.DeliveryLocation.LocationName;
		EDealWindow window = DealWindowInfo.GetWindow(contract.DeliveryWindow.WindowStartTime);
		Window = ((object)(EDealWindow)(ref window)).ToString();
		Started = TimeManager.Get12HourTime((float)contract.AcceptTime.time, true);
		Cost = MoneyManager.FormatAmount(contract.Payment * (1f - stats.Dealer.Cut), false, false);
		Status = "Accepted";
		Date = Util.DayDate();
		Payment = contract.Payment * (1f - stats.Dealer.Cut);
		Quantity = contract.ProductList.GetTotalQuantity();
		StartTime = contract.AcceptTime.time;
	}
}
[Serializable]
public class ModSaveData : SaveData
{
	public const string PATHBASE = "UserData\\DealersSendTexts";

	public const string DATAFILE = "Data.json";

	public const string PREFFILE = "Config.cfg";

	private static string _activeSaveDir;

	private static string _activeSavePath;

	public Dictionary<string, DealerState> DealerStates = new Dictionary<string, DealerState>();

	public HashSet<string> Completed = new HashSet<string>();

	public List<Location> Locations = new List<Location>();

	public static string DataPath => Path.Combine(_activeSavePath ?? "UserData\\DealersSendTexts", "Data.json");

	public static string PrefsPath => Path.Combine(_activeSavePath ?? "UserData\\DealersSendTexts", "Config.cfg");

	public static void SaveData(string pathFromSaver)
	{
		SetActiveSave(pathFromSaver);
		MelonLogger.Msg("Attempting to save " + DataPath);
		if (!Directory.Exists(Path.GetDirectoryName(DataPath)))
		{
			Directory.CreateDirectory(DataPath);
		}
		ModSaveData modSaveData = new ModSaveData
		{
			DealerStates = DealerManager.Dealers.ToDictionary((KeyValuePair<string, DealerManager> kvp) => kvp.Key, (KeyValuePair<string, DealerManager> kvp) => kvp.Value.State),
			Completed = ContractManager.Completed,
			Locations = LocationManager.All()
		};
		MelonLogger.Msg("Serializing:");
		try
		{
			string contents = JsonConvert.SerializeObject((object)modSaveData, (Formatting)1);
			File.WriteAllText(DataPath, contents);
		}
		catch (Exception arg)
		{
			MelonLogger.Error($"[DealersSendTexts] Failed to save to {DataPath}: {arg}");
		}
		MelonLogger.Msg("[DealersSendTexts] Successfully saved " + DataPath);
		MelonLogger.Msg(Application.version);
	}

	public static void LoadData(string pathFromLoader)
	{
		SetActiveSave(pathFromLoader);
		if (!File.Exists(DataPath))
		{
			DealerText.ClearAll();
			return;
		}
		try
		{
			MelonLogger.Msg("Loading " + DataPath + ":");
			ModSaveData modSaveData = JsonConvert.DeserializeObject<ModSaveData>(File.ReadAllText(DataPath));
			ContractManager.Completed = modSaveData.Completed;
			foreach (Location location in modSaveData.Locations)
			{
				LocationManager.Add(location);
			}
			foreach (KeyValuePair<string, DealerState> dealerState in modSaveData.DealerStates)
			{
				if (DealerManager.Dealers.TryGetValue(dealerState.Key, out var value))
				{
					value.State = dealerState.Value;
				}
				else
				{
					DealerManager.StateCache.Add(dealerState.Key, dealerState.Value);
				}
			}
		}
		catch (Exception arg)
		{
			MelonLogger.Error($"[DealersSendTexts] Failed to load from {DataPath}: {arg}");
		}
		MelonLogger.Msg("[DealersSendTexts] Successfully loaded " + DataPath);
	}

	private static void SetActiveSave(string gamePath)
	{
		string text = gamePath;
		if (string.IsNullOrEmpty(text))
		{
			text = Singleton<SaveManager>.Instance.IndividualSavesContainerPath;
		}
		string text2 = (Directory.Exists(text) ? text : Path.GetDirectoryName(text));
		_activeSaveDir = (string.IsNullOrEmpty(text2) ? "Unknown Save" : new DirectoryInfo(text2).Name);
		_activeSavePath = Path.Combine("UserData\\DealersSendTexts", _activeSaveDir);
		Directory.CreateDirectory(_activeSavePath);
	}
}
public class Location
{
	public SerialVector3 Position;

	public string Name;

	public string Region;

	public int StartedCount;

	public int SuccessCount;

	public int FailureCount;

	public Dictionary<string, int> CustomerCount { get; } = new Dictionary<string, int>();


	public Dictionary<string, int> DealerCount { get; } = new Dictionary<string, int>();


	public void RecordStarted()
	{
		StartedCount++;
	}

	public void RecordSuccess()
	{
		SuccessCount++;
	}

	public void RecordFailure()
	{
		FailureCount++;
	}

	public void RecordCustomer(string name)
	{
		CustomerCount[name] = ((!CustomerCount.TryGetValue(name, out var value)) ? 1 : (value + 1));
	}

	public void RecordDealer(string name)
	{
		DealerCount[name] = ((!DealerCount.TryGetValue(name, out var value)) ? 1 : (value + 1));
	}

	public float DistanceTo(Vector3 location)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		return Vector3.Distance(location, Position.ToVector3());
	}
}
public static class LocationManager
{
	private static readonly List<Location> Locations = new List<Location>();

	public static void Register(string name, Vector3 position)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		if (!Locations.Any((Location l) => l.Name == name))
		{
			Locations.Add(new Location
			{
				Name = name,
				Position = new SerialVector3(position)
			});
		}
	}

	public static void Add(Location location)
	{
		if (!Locations.Contains(location))
		{
			Locations.Add(location);
		}
	}

	public static Location GetNearest(Vector3 position)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		return Locations.OrderBy((Location l) => l.DistanceTo(position)).FirstOrDefault();
	}

	public static Location GetNearest(Vector3 position, out float distance)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		Location nearest = GetNearest(position);
		distance = Vector3.Distance(nearest.Position.ToVector3(), position);
		return nearest;
	}

	public static string Describe(Vector3 position, string noDistPrefix = "to")
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		float distance;
		Location nearest = GetNearest(position, out distance);
		if (!(distance > 1.5f))
		{
			return noDistPrefix + " " + nearest.Name;
		}
		return $"{distance:#.#} meters from {nearest.Name}";
	}

	public static List<Location> All()
	{
		return Locations;
	}

	public static void ClearAll()
	{
		foreach (Location location in Locations)
		{
			location.CustomerCount.Clear();
			location.DealerCount.Clear();
		}
		Locations.Clear();
	}
}
public enum EIcon
{
	None = -1,
	Started,
	Success,
	Failure,
	Summary,
	Customer,
	Location,
	ProdAlert,
	HurtAlert,
	KnockOut,
	HasDied,
	Navigate
}
public static class MessageManager
{
	public static string Create(EContract state, Contract contract, SaleData sale)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_025a: Unknown result type (might be due to invalid IL or missing references)
		Vector3 position = ((Component)contract.DeliveryLocation.CustomerStandPoint).transform.position;
		Vector3 position2 = ((Component)contract.Dealer).transform.position;
		string text = Vector3.Distance(position, position2).ToString("#.#");
		int num = contract.Dealer.ActiveContracts.Count - 1;
		switch (state)
		{
		case EContract.Started:
		{
			string text3 = Util.Prefix(sale.Location);
			return sale.Customer + " at " + sale.Started + ": " + sale.Description + " for " + sale.Cost + ", " + text3 + " (" + text + " meters), " + sale.Window + ".";
		}
		case EContract.Success:
		{
			int num3 = TimeManager.AddMinutesTo24HourTime(contract.DeliveryWindow.WindowStartTime, 60);
			GameDateTime val = default(GameDateTime);
			((GameDateTime)(ref val))..ctor(contract.AcceptTime.elapsedDays, num3);
			float num4 = 0f;
			if (NetworkSingleton<TimeManager>.Instance.IsCurrentDateWithinRange(contract.AcceptTime, val))
			{
				num4 = sale.Payment * 0.1f;
				sale.Payment += num4;
				sale.Cost = MoneyManager.FormatAmount(sale.Payment, false, false);
			}
			string text4 = Util.HoursMins(Util.TimeDiff(sale.StartTime, Util.IntTime()));
			string text5 = ((num4 > 0f) ? (" (" + MoneyManager.FormatAmount(num4, false, true) + " bonus incl.)") : "");
			return $"Completed deal for {sale.Customer} in {text4}, made {sale.Cost}{text5}; {num} active deals";
		}
		case EContract.Failure:
		{
			string text2 = "Failed deal: ";
			int num2 = 5;
			text2 = (sale.Stats.IsDealerHurt(out var status) ? (text2 + "Dealer " + status) : (sale.Stats.IsCustomerHurt(sale.Customer, out status) ? (text2 + "Customer " + status) : (Pathing.IsStuck(sale.Stats.Dealer) ? (text2 + "Stuck " + LocationManager.Describe(((Component)sale.Stats.Dealer).transform.position, "at")) : ((Util.TimeDiff(contract.DeliveryWindow.WindowEndTime, Util.IntTime(), is24hour: false) < num2) ? (text2 + "Ran out of time") : ((Math.Abs(Util.IntTime() - 700) >= num2) ? (text2 + "Unknown reason") : (text2 + "Went to bed too early"))))));
			return $"{text2} for {sale.Customer} from {text} meters, lost potential {sale.Cost}; {num} active deals.";
		}
		default:
			return "";
		}
	}

	public static void Send(Dealer dealer, EIcon icon, string message, bool notify)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		if (((NPC)dealer).MSGConversation != null)
		{
			Message val = new Message(GetIcon(icon) + message, (ESenderType)1, false, -1);
			((NPC)dealer).MSGConversation.SendMessage(val, notify, false);
		}
	}

	private static string GetIcon(EIcon state)
	{
		return state switch
		{
			EIcon.Started => " <color=blue><b>▶</b></color> ", 
			EIcon.Success => " <color=green><b>✔</b></color> ", 
			EIcon.Failure => " <color=red><b>✖</b></color> ", 
			EIcon.Summary => " <color=brown><b>☰</b></color> ", 
			EIcon.Customer => " <color=darkblue><b>✪</b></color> ", 
			EIcon.Location => " <color=teal><b>✦</b></color> ", 
			EIcon.ProdAlert => " <color=purple><b>⁉</b></color> ", 
			EIcon.HurtAlert => " <color=maroon><b>‼</b></color> ", 
			EIcon.KnockOut => " <color=orange><b>⁂</b></color> ", 
			EIcon.HasDied => " <color=red><b>☠</b></color> ", 
			EIcon.Navigate => " <color=blue>∇</color> ", 
			_ => "", 
		};
	}
}
[Serializable]
public class DealerState
{
	public string MostRecent = "Never";

	public int SaleCount;

	public Dictionary<string, string> TodaysSales = new Dictionary<string, string>();

	public Dictionary<string, string> RecentSale = new Dictionary<string, string>();

	public Dictionary<string, int> Products = new Dictionary<string, int>();

	public HashSet<FailureKey> Failures = new HashSet<FailureKey>();

	public List<SaleData> DailySales = new List<SaleData>();

	public List<SaleData> TotalSales = new List<SaleData>();

	public void ClearAll(bool daily = false)
	{
		DailySales.Clear();
		TodaysSales.Clear();
		Failures.Clear();
		if (!daily)
		{
			Products.Clear();
			RecentSale.Clear();
			TotalSales.Clear();
		}
	}
}
[Serializable]
public struct SerialVector3
{
	public float x;

	public float y;

	public float z;

	public SerialVector3(float x, float y, float z)
	{
		this.x = x;
		this.y = y;
		this.z = z;
	}

	public SerialVector3(Vector3 v)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		x = v.x;
		y = v.y;
		z = v.z;
	}

	public readonly Vector3 ToVector3()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		return new Vector3(x, y, z);
	}
}
[Serializable]
public struct FailureKey
{
	public string Customer;

	public string Location;

	public FailureKey(string customer, string location)
	{
		Customer = customer;
		Location = location;
	}

	public override readonly bool Equals(object obj)
	{
		if (obj is FailureKey failureKey && Customer == failureKey.Customer)
		{
			return Location == failureKey.Location;
		}
		return false;
	}

	public override readonly int GetHashCode()
	{
		return Customer.GetHashCode() ^ Location.GetHashCode();
	}
}
[HarmonyPatch(typeof(Contract), "InitializeContract")]
public class ContractInitializeContractPatch
{
	private static void Postfix(Contract __instance)
	{
		if (__instance != null && __instance.Dealer != null && !DealerManager.IsCartel(__instance.Dealer))
		{
			ContractManager.ProcessContract(__instance, EContract.Started);
		}
	}
}
[HarmonyPatch(typeof(Contract), "End")]
public class ContractEndPatch
{
	private static void Prefix(Contract __instance)
	{
		if (__instance != null && __instance.Dealer != null && !DealerManager.IsCartel(__instance.Dealer))
		{
			ContractManager.ProcessContract(__instance, EContract.Failure);
		}
	}
}
[HarmonyPatch(typeof(Contract), "Complete")]
public class ContractCompletePatch
{
	private static void Prefix(Contract __instance)
	{
		if (__instance != null && __instance.Dealer != null && !DealerManager.IsCartel(__instance.Dealer))
		{
			HashSet<string> completed = ContractManager.Completed;
			Customer component = ((Component)__instance.Customer).GetComponent<Customer>();
			object obj;
			if (component == null)
			{
				obj = null;
			}
			else
			{
				NPC nPC = component.NPC;
				obj = ((nPC != null) ? nPC.fullName : null);
			}
			if (obj == null)
			{
				obj = "Unknown";
			}
			completed.Add((string)obj);
		}
	}
}
[HarmonyPatch(typeof(Dealer), "Start")]
public class DealerStartPatch
{
	private static void Postfix(Dealer __instance)
	{
		if (__instance != null && !DealerManager.IsCartel(__instance))
		{
			DealerPrefs.Prefs(((NPC)__instance).FirstName);
		}
	}
}
[HarmonyPatch(typeof(LoadManager), "StartGame")]
public class LoadManagerStartGamePatch
{
	private static void Postfix(SaveInfo info)
	{
		ModSaveData.LoadData(info.SavePath);
	}
}
[HarmonyPatch(typeof(NPC), "Start")]
public class NPCStartPatch
{
	private static void Postfix(NPC __instance)
	{
		if (__instance.Health != null)
		{
			__instance.Health.onDie.AddListener(UnityAction.op_Implicit((Action)delegate
			{
				AlertManager.NPCInjuryAlert(__instance, EIcon.HasDied);
			}));
			__instance.Health.onKnockedOut.AddListener(UnityAction.op_Implicit((Action)delegate
			{
				AlertManager.NPCInjuryAlert(__instance, EIcon.KnockOut);
			}));
		}
	}
}
[HarmonyPatch(typeof(NPCMovement))]
public class NPCMovementSetDestinationPatch
{
	private static MethodBase TargetMethod()
	{
		return AccessTools.GetDeclaredMethods(typeof(NPCMovement)).FirstOrDefault((MethodInfo method) => method.Name == "SetDestination" && method.GetParameters().Length == 5);
	}

	private static void Postfix(NPCMovement __instance, Vector3 pos, Action<WalkResult> callback, bool interruptExistingCallback, float successThreshold, float cacheMaxDistSqr)
	{
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		if (__instance == null || __instance.npc == null)
		{
			return;
		}
		NPC npc = __instance.npc;
		Dealer val = (Dealer)(object)((npc is Dealer) ? npc : null);
		if (val != null && !DealerManager.IsCartel(val))
		{
			EMsg navigation = DealerPrefs.Prefs(((NPC)val).FirstName).GetNavigation();
			if (navigation != EMsg.Disable && DealerManager.CheckPing(val))
			{
				DealerManager.SetPing(val, Util.AbsTime());
				float num = Vector3.Distance(((Component)val).transform.position, pos);
				string arg = LocationManager.Describe(pos);
				string message = $"Headed {arg}, {num:#.#} meters away.";
				MessageManager.Send(val, EIcon.Navigate, message, navigation == EMsg.Notify);
			}
		}
	}
}
[HarmonyPatch(typeof(Player), "SleepStart")]
public class PlayerSleepStartPatch
{
	private static void Prefix()
	{
		ContractManager.SendSummary();
	}
}
[HarmonyPatch(typeof(SaveManager), "Save", new Type[] { typeof(string) })]
public class SaveManagerSavePatch
{
	private static void Prefix(string saveFolderPath)
	{
		ModSaveData.SaveData(saveFolderPath);
	}
}
[HarmonyPatch(typeof(TimeManager), "Update")]
public class TimeManagerUpdatePatch
{
	[HarmonyPostfix]
	public static void PostFix()
	{
		int num = Util.AbsTime() % 20;
		if (num == 0 && DealerManager.CheckStuck)
		{
			DealerManager.CheckStuck = false;
			foreach (DealerManager value in DealerManager.Dealers.Values)
			{
				Pathing.CheckStuck(value);
			}
		}
		if (num == 1)
		{
			DealerManager.CheckStuck = true;
		}
	}
}
public class DealerManager
{
	public static Dictionary<string, DealerManager> Dealers = new Dictionary<string, DealerManager>();

	public static Dictionary<string, DealerState> StateCache = new Dictionary<string, DealerState>();

	public static bool CheckStuck;

	public Dictionary<string, NPC> Customers = new Dictionary<string, NPC>();

	public Dealer Dealer;

	public DealerState State;

	public Vector3 Home;

	private int LastPing = -1;

	public DealerManager(Dealer dealer)
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		Dealer = dealer;
		State = (StateCache.TryGetValue(((NPC)dealer).fullName, out var value) ? value : new DealerState());
		NPCEnterableBuilding home = dealer.Home;
		Transform transform = ((Component)dealer).transform;
		Home = ((Component)home.GetClosestDoor((transform != null) ? transform.position : Vector3.zero, true)).transform.position;
		LocationManager.Register(dealer.HomeName, Home);
		Enumerator<Customer> enumerator = dealer.AssignedCustomers.GetEnumerator();
		while (enumerator.MoveNext())
		{
			Customer current = enumerator.Current;
			string fullName = current.NPC.fullName;
			if (!State.TodaysSales.ContainsKey(fullName))
			{
				State.TodaysSales[fullName] = "Never";
			}
			if (!State.RecentSale.ContainsKey(fullName))
			{
				State.RecentSale[fullName] = "Never";
			}
			AddCustomer(fullName, current.NPC);
		}
	}

	public static DealerManager GetStats(Dealer dealer)
	{
		if (!Dealers.TryGetValue(((NPC)dealer).fullName, out var value))
		{
			value = new DealerManager(dealer);
			Dealers.Add(((NPC)dealer).fullName, value);
		}
		return value;
	}

	public void RefreshInventory()
	{
		List<ItemSlot> allSlots = Dealer.GetAllSlots();
		if (allSlots == null || allSlots.Count == 0)
		{
			return;
		}
		List<string> list = new List<string>();
		Dictionary<string, int> dictionary = new Dictionary<string, int>();
		Enumerator<ItemSlot> enumerator = allSlots.GetEnumerator();
		while (enumerator.MoveNext())
		{
			ItemSlot current = enumerator.Current;
			if (current.Quantity != 0)
			{
				int num = current.Quantity;
				ProductItemInstance val = ((Il2CppObjectBase)current.ItemInstance).TryCast<ProductItemInstance>();
				if (val != null)
				{
					num *= val.Amount;
				}
				if (list.Contains(current.ItemInstance.ID))
				{
					dictionary[current.ItemInstance.ID] += num;
					continue;
				}
				list.Add(current.ItemInstance.ID);
				dictionary.Add(current.ItemInstance.ID, num);
			}
		}
		State.Products = dictionary;
	}

	public void AddCustomer(string fullName, NPC npc)
	{
		if (!Customers.ContainsKey(fullName))
		{
			Customers.Add(fullName, npc);
		}
	}

	public bool IsCustomerHurt(string name, out string status)
	{
		status = "";
		if (!Customers.TryGetValue(name, out var value))
		{
			return false;
		}
		if (value.Health.IsDead)
		{
			status = $"<color=red>(Dead {value.Health.DaysPassedSinceDeath} days)</color>";
		}
		if (value.Health.IsKnockedOut)
		{
			status = "<color=orange>(Knocked Out)</color>";
		}
		return !string.IsNullOrEmpty(status);
	}

	public bool IsDealerHurt(out string status)
	{
		status = "";
		if (((NPC)Dealer).Health.IsDead)
		{
			status = $"<color=red>Dead ({((NPC)Dealer).Health.DaysPassedSinceDeath} days)</color>";
		}
		if (((NPC)Dealer).Health.IsKnockedOut)
		{
			status = "<color=orange>(Knocked Out)</color>";
		}
		return !string.IsNullOrEmpty(status);
	}

	public static bool IsCartel(Dealer dealer)
	{
		if (Application.version.CompareTo("0.4.0") < 0)
		{
			return false;
		}
		return Type.GetType("CartelDealer")?.IsInstanceOfType(dealer) ?? false;
	}

	public static bool CheckPing(Dealer dealer)
	{
		int iNavDeltaTime = DealerPrefs.Prefs(((NPC)dealer).FirstName).GetINavDeltaTime();
		if (iNavDeltaTime > -1)
		{
			return Util.AbsTime() - GetStats(dealer).LastPing > iNavDeltaTime;
		}
		return false;
	}

	public static void SetPing(Dealer dealer, int ping)
	{
		GetStats(dealer).LastPing = ping;
	}

	public static void ClearAll()
	{
		foreach (DealerManager value in Dealers.Values)
		{
			value.Customers.Clear();
			value.State.ClearAll();
		}
		Dealers.Clear();
		StateCache.Clear();
	}

	public Vector3 DistanceToHome()
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		return Dealer.Home.GetClosestDoor(((Component)Dealer).transform.position, true).AccessPoint.position;
	}
}
public enum EMsg
{
	Notify,
	Silent,
	Disable
}
public class DealerText : MelonMod
{
	public const string ModName = "Dealers Send Texts";

	public const string Version = "2.1.0";

	public const string ModDesc = "Dealers text updates on deals and daily summary in Schedule One";

	public override void OnSceneWasLoaded(int buildIndex, string sceneName)
	{
		if (sceneName.Equals("main", StringComparison.OrdinalIgnoreCase))
		{
			DealerPrefs.Initialize();
		}
	}

	public override void OnSceneWasUnloaded(int buildIndex, string sceneName)
	{
		if (sceneName.Equals("main", StringComparison.OrdinalIgnoreCase))
		{
			ClearAll();
		}
	}

	public static void ClearAll()
	{
		ContractManager.ClearAll();
		LocationManager.ClearAll();
		DealerManager.ClearAll();
		DealerPrefs.ClearAll();
		Pathing.ClearAll();
	}
}
public static class Pathing
{
	private const int MINTIME = 5;

	private const int MAXHISTORY = 10;

	private static readonly Dictionary<string, Queue<(Vector3, int)>> PositionHistory = new Dictionary<string, Queue<(Vector3, int)>>();

	public static void RecordPosition(Dealer dealer)
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		if (dealer != null && dealer.ActiveContracts.Count != 0)
		{
			string fullName = ((NPC)dealer).fullName;
			int num = Util.AbsTime();
			if (!PositionHistory.TryGetValue(fullName, out var value))
			{
				value = (PositionHistory[fullName] = new Queue<(Vector3, int)>());
			}
			if (value.Count == 0 || Math.Abs(value.Peek().Item2 - num) >= 5)
			{
				value.Enqueue((((Component)dealer).transform.position, num));
			}
			if (value.Count > 10)
			{
				value.Dequeue();
			}
		}
	}

	public static bool IsStuck(Dealer dealer)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		if (dealer == null || !PositionHistory.TryGetValue(((NPC)dealer).fullName, out var value))
		{
			return false;
		}
		DealerPrefs dealerPrefs = DealerPrefs.Prefs(((NPC)dealer).FirstName);
		int isStuckCount = dealerPrefs.GetIsStuckCount();
		if (value.Count < isStuckCount)
		{
			return false;
		}
		(Vector3, int)[] array = value.Reverse().Take(isStuckCount).ToArray();
		Vector3 item = array[0].Item1;
		(Vector3, int)[] array2 = array;
		for (int i = 0; i < array2.Length; i++)
		{
			if (Vector3.Distance(array2[i].Item1, item) > (float)dealerPrefs.GetIsStuckRadius())
			{
				return false;
			}
		}
		return true;
	}

	public static void CheckStuck(DealerManager stats)
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		Dealer dealer = stats.Dealer;
		RecordPosition(dealer);
		if (IsStuck(dealer) && !stats.IsDealerHurt(out var _) && dealer.ActiveContracts.Count != 0)
		{
			bool notify = DealerPrefs.Prefs(((NPC)dealer).FirstName).GetIsStuckAlert() == EMsg.Notify;
			string arg = LocationManager.Describe(((Component)dealer).transform.position, "at");
			string message = $"I may be stuck in {((NPC)dealer).Region} {arg}. Most recent deal was {stats.State.MostRecent}.";
			MessageManager.Send(dealer, EIcon.HurtAlert, message, notify);
		}
	}

	public static void ClearAll()
	{
		PositionHistory.Clear();
	}
}
public enum EContract
{
	Started,
	Success,
	Failure
}
public static class ContractManager
{
	public static HashSet<string> Completed = new HashSet<string>();

	public static void ProcessContract(Contract contract, EContract state)
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		if (contract != null && contract.Dealer != null)
		{
			DealerPrefs prefs = DealerPrefs.Prefs(((NPC)contract.Dealer).FirstName);
			DealerManager stats = DealerManager.GetStats(contract.Dealer);
			SaleData saleData = new SaleData(contract, stats);
			NPC nPC = ((Component)contract.Customer).GetComponent<Customer>().NPC;
			Vector3 position = contract.DeliveryLocation.CustomerStandPoint.position;
			stats.AddCustomer(nPC.fullName, nPC);
			LocationManager.Register(saleData.Location, position);
			Location nearest = LocationManager.GetNearest(position);
			if (state == EContract.Failure && Completed.Contains(saleData.Customer))
			{
				state = EContract.Success;
			}
			if (state == EContract.Started)
			{
				ContractStarted(contract, saleData, nearest, prefs);
			}
			if (state == EContract.Success)
			{
				ContractSuccess(contract, saleData, nearest, prefs);
			}
			if (state == EContract.Failure)
			{
				ContractFailure(contract, saleData, nearest, prefs);
			}
		}
	}

	public static void SendSummary()
	{
		Dictionary<string, DealerManager> dealers = DealerManager.Dealers;
		if (dealers == null || dealers.Count == 0)
		{
			return;
		}
		foreach (DealerManager value in dealers.Values)
		{
			AlertManager.DailySummary(value);
		}
	}

	private static void ContractStarted(Contract contract, SaleData sale, Location location, DealerPrefs prefs)
	{
		location.RecordStarted();
		location.RecordCustomer(sale.Customer);
		location.RecordDealer(((NPC)contract.Dealer).fullName);
		sale.Stats.State.TotalSales.Add(sale);
		sale.Stats.State.SaleCount++;
		string message = MessageManager.Create(EContract.Started, contract, sale);
		if (prefs.GetShowStarted() != EMsg.Disable)
		{
			MessageManager.Send(contract.Dealer, EIcon.Started, message, prefs.GetShowStarted() == EMsg.Notify);
		}
	}

	private static void ContractSuccess(Contract contract, SaleData sale, Location location, DealerPrefs prefs)
	{
		sale.Status = "Success";
		DealerState state = sale.Stats.State;
		Completed.Remove(sale.Customer);
		Dictionary<string, string> todaysSales = state.TodaysSales;
		string customer = sale.Customer;
		string value = (state.RecentSale[sale.Customer] = (state.MostRecent = Util.Time()));
		todaysSales[customer] = value;
		state.DailySales.Add(sale);
		location.RecordSuccess();
		string message = MessageManager.Create(EContract.Success, contract, sale);
		if (prefs.GetShowSuccess() != EMsg.Disable)
		{
			MessageManager.Send(contract.Dealer, EIcon.Success, message, prefs.GetShowSuccess() == EMsg.Notify);
		}
		AlertManager.CheckProductAlert(contract.Dealer);
	}

	private static void ContractFailure(Contract contract, SaleData sale, Location location, DealerPrefs prefs)
	{
		sale.Status = "Failure";
		sale.Stats.State.Failures.Add(new FailureKey(sale.Customer, sale.Location));
		location.RecordFailure();
		string message = MessageManager.Create(EContract.Failure, contract, sale);
		if (prefs.GetShowFailure() != EMsg.Disable)
		{
			MessageManager.Send(contract.Dealer, EIcon.Failure, message, prefs.GetShowFailure() == EMsg.Notify);
		}
	}

	public static void ClearAll()
	{
		Completed.Clear();
	}
}
public static class Util
{
	public static int TimeDiff(int start, int end, bool is24hour = true)
	{
		start = (is24hour ? TimeManager.GetMinSumFrom24HourTime(start) : start);
		end = (is24hour ? TimeManager.GetMinSumFrom24HourTime(end) : end);
		int num = end - start;
		if (num < 0)
		{
			num += 1440;
		}
		return num;
	}

	public static string GetName(string input)
	{
		ItemDefinition item = Registry.GetItem(input);
		string text = ((item != null) ? item.Name : null);
		if (string.IsNullOrEmpty(text))
		{
			return input;
		}
		return text;
	}

	public static string Prefix(string input)
	{
		if (input.ToLower().StartsWith("outside") || input.ToLower().StartsWith("next") || input.ToLower().StartsWith("under") || input.ToLower().StartsWith("behind") || input.ToLower().StartsWith("in "))
		{
			return input;
		}
		return "at the " + input;
	}

	public static string DayDate()
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		return $"{NetworkSingleton<TimeManager>.Instance.CurrentDay}, Day {NetworkSingleton<TimeManager>.Instance.ElapsedDays}";
	}

	public static int IntTime()
	{
		return NetworkSingleton<TimeManager>.Instance.CurrentTime;
	}

	public static int AbsTime()
	{
		return NetworkSingleton<TimeManager>.Instance.GetTotalMinSum();
	}

	public static string Time()
	{
		return TimeManager.Get12HourTime((float)IntTime(), true);
	}

	public static string HoursMins(int time)
	{
		return ((time / 60 > 0) ? (time / 60 + "hr ") : "") + time % 60 + "min";
	}
}

Alt-DealersSendTexts-2.2.0.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using DealersSendTexts;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using Newtonsoft.Json;
using ScheduleOne;
using ScheduleOne.DevUtilities;
using ScheduleOne.Economy;
using ScheduleOne.GameTime;
using ScheduleOne.ItemFramework;
using ScheduleOne.Map;
using ScheduleOne.Messaging;
using ScheduleOne.Money;
using ScheduleOne.NPCs;
using ScheduleOne.Persistence;
using ScheduleOne.Persistence.Datas;
using ScheduleOne.PlayerScripts;
using ScheduleOne.Product;
using ScheduleOne.Quests;
using UnityEngine;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(DealerText), "Dealers Send Texts", "2.1.0", "GuysWeForgotDre", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: AssemblyTitle("Dealers Send Texts")]
[assembly: AssemblyDescription("Dealers text updates on deals and daily summary in Schedule One")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Dealers Send Texts")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("51af033c-2fba-4fd9-950e-a5fb45e211c4")]
[assembly: AssemblyFileVersion("2.1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("2.1.0.0")]
namespace DealersSendTexts;

public static class AlertManager
{
	public static void DailySummary(DealerManager stats)
	{
		if (!((NPC)stats.Dealer).RelationData.Unlocked)
		{
			return;
		}
		DealerPrefs dealerPrefs = DealerPrefs.Prefs(((NPC)stats.Dealer).FirstName);
		DealerState state = stats.State;
		Dictionary<string, int> dictionary = new Dictionary<string, int>();
		HashSet<string> hashSet = new HashSet<string>();
		float num = 0f;
		foreach (SaleData dailySale in state.DailySales)
		{
			num += dailySale.Payment;
			if (!dictionary.ContainsKey(dailySale.Product))
			{
				dictionary.Add(dailySale.Product, dailySale.Quantity);
			}
			else
			{
				dictionary[dailySale.Product] += dailySale.Quantity;
			}
			hashSet.Add(dailySale.Location);
		}
		if (dealerPrefs.GetShowSummary())
		{
			string text = MoneyManager.FormatAmount(num, false, true);
			string text2 = "";
			foreach (string key in dictionary.Keys)
			{
				text2 += $"\n{Util.GetName(key)} ({dictionary[key]})";
			}
			string text3 = $"DAILY SUMMARY\nDid {state.DailySales.Count} deals in {hashSet.Count} locations; {state.Failures.Count} failed.\nMade {text} from:{text2}";
			if (stats.IsDealerHurt(out var status))
			{
				text3 = text3 + "\nDealer " + status;
			}
			MessageManager.Send(stats.Dealer, EIcon.Summary, text3, notify: false);
		}
		if (dealerPrefs.GetSendCustomers())
		{
			string text4 = $"CUSTOMER LOG ({state.TodaysSales.Count})";
			foreach (string key2 in state.TodaysSales.Keys)
			{
				stats.IsCustomerHurt(key2, out var status2);
				text4 = text4 + "\n" + key2 + ": " + state.TodaysSales[key2] + " " + status2;
			}
			MessageManager.Send(stats.Dealer, EIcon.Customer, text4, notify: false);
		}
		if (dealerPrefs.GetSendLocations())
		{
			string text5 = $"LOCATIONS ({hashSet.Count})";
			foreach (string item in hashSet)
			{
				text5 = text5 + "\n" + item;
			}
			MessageManager.Send(stats.Dealer, EIcon.Location, text5, notify: false);
		}
		if (dealerPrefs.GetSendFailures() && state.Failures.Count > 0)
		{
			string text6 = $"FAILURES ({state.Failures.Count})";
			foreach (FailureKey failure in state.Failures)
			{
				text6 = text6 + "\n" + failure.Customer + " " + Util.Prefix(failure.Location);
			}
			MessageManager.Send(stats.Dealer, EIcon.Failure, text6, notify: false);
		}
		state.ClearAll(daily: true);
	}

	public static void NPCInjuryAlert(NPC npc, EIcon type)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		string text = LocationManager.Describe(((Component)npc).transform.position, "at");
		string text2 = Vector3.Distance(Player.Local.PlayerBasePosition, ((Component)npc).transform.position).ToString("#.#");
		string text3 = type switch
		{
			EIcon.HasDied => "died", 
			EIcon.KnockOut => "been knocked out", 
			_ => "an unknown alert", 
		};
		Dealer val = (Dealer)(object)((npc is Dealer) ? npc : null);
		if (val != null)
		{
			EMsg dealerInjury = DealerPrefs.Prefs(((NPC)val).FirstName).GetDealerInjury();
			string message = $"I have {text3} in {npc.Region} {text}, {text2} meters from you.";
			if (dealerInjury != EMsg.Disable)
			{
				MessageManager.Send(val, type, message, dealerInjury == EMsg.Notify);
			}
			return;
		}
		foreach (DealerManager value2 in DealerManager.Dealers.Values)
		{
			foreach (string key in value2.Customers.Keys)
			{
				if (key == npc.fullName)
				{
					EMsg customerInjury = DealerPrefs.Prefs(((NPC)value2.Dealer).FirstName).GetCustomerInjury();
					if (!value2.State.RecentSale.TryGetValue(key, out var value))
					{
						value = "Never";
					}
					string message2 = $"{npc.fullName} has {text3} in {npc.Region} {text}, {text2} meters from you. Last sale was {value}.";
					if (customerInjury != EMsg.Disable)
					{
						MessageManager.Send(value2.Dealer, type, message2, customerInjury == EMsg.Notify);
					}
				}
			}
		}
	}

	public static void CheckProductAlert(Dealer dealer)
	{
		//IL_002c: 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_0056: Unknown result type (might be due to invalid IL or missing references)
		DealerPrefs dealerPrefs = DealerPrefs.Prefs(((NPC)dealer).FirstName);
		DealerManager stats = DealerManager.GetStats(dealer);
		stats.RefreshInventory();
		string text = MoneyManager.FormatAmount(dealer.Cash, false, false);
		string text2 = Vector3.Distance(Player.Local.PlayerBasePosition, ((Component)dealer).transform.position).ToString("#.#");
		string text3 = LocationManager.Describe(((Component)dealer).transform.position, "at");
		float num = 0f;
		foreach (string key in stats.State.Products.Keys)
		{
			num += (float)stats.State.Products[key];
		}
		if (dealerPrefs.GetCheckCash() != EMsg.Disable && dealer.Cash >= dealerPrefs.GetCashAlert())
		{
			MessageManager.Send(dealer, EIcon.ProdAlert, $"Cash threshold reached: {text}.\n{num} products left; I'm {text2} meters from you, {text3}.", dealerPrefs.GetCheckCash() == EMsg.Notify);
		}
		if (dealerPrefs.GetCheckProduct() != EMsg.Disable && num <= dealerPrefs.GetProductAlert())
		{
			MessageManager.Send(dealer, EIcon.ProdAlert, $"Product threshold reached: {num}.\n{text} cash; I'm {text2} meters from you, {text3}.", dealerPrefs.GetCheckProduct() == EMsg.Notify);
		}
	}
}
public class DealerPrefs
{
	public static DealerPrefs Master;

	private static bool MasterOnly;

	private static bool Initialized = false;

	private const string MasterName = "_Master";

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

	private MelonPreferences_Category DealerGroup;

	private MelonPreferences_Entry<bool> OverrideMaster;

	private MelonPreferences_Entry<bool> CreateDealers;

	private MelonPreferences_Entry<EMsg> ShowStarted;

	private MelonPreferences_Entry<EMsg> ShowSuccess;

	private MelonPreferences_Entry<EMsg> ShowFailure;

	private MelonPreferences_Entry<bool> ShowSummary;

	private MelonPreferences_Entry<bool> SendCustomers;

	private MelonPreferences_Entry<bool> SendLocations;

	private MelonPreferences_Entry<bool> SendFailures;

	private MelonPreferences_Entry<EMsg> CheckProduct;

	private MelonPreferences_Entry<float> ProductAlert;

	private MelonPreferences_Entry<EMsg> CheckCash;

	private MelonPreferences_Entry<float> CashAlert;

	private MelonPreferences_Entry<EMsg> CustomerInjury;

	private MelonPreferences_Entry<EMsg> DealerInjury;

	private MelonPreferences_Entry<EMsg> IsStuckAlert;

	private MelonPreferences_Entry<int> IsStuckCount;

	private MelonPreferences_Entry<int> IsStuckRadius;

	private MelonPreferences_Entry<EMsg> Navigation;

	private MelonPreferences_Entry<int> NavDeltaTime;

	public static void Initialize()
	{
		if (!Initialized)
		{
			Master = AddDealer("_Master");
			MasterOnly = !Master.CreateDealers.Value;
			Initialized = true;
		}
	}

	public EMsg GetShowStarted()
	{
		return GetValue<EMsg>(ShowStarted, Master.ShowStarted);
	}

	public EMsg GetShowSuccess()
	{
		return GetValue<EMsg>(ShowSuccess, Master.ShowSuccess);
	}

	public EMsg GetShowFailure()
	{
		return GetValue<EMsg>(ShowFailure, Master.ShowFailure);
	}

	public bool GetShowSummary()
	{
		return GetValue<bool>(ShowSummary, Master.ShowSummary);
	}

	public bool GetSendCustomers()
	{
		return GetValue<bool>(SendCustomers, Master.SendCustomers);
	}

	public bool GetSendLocations()
	{
		return GetValue<bool>(SendLocations, Master.SendLocations);
	}

	public bool GetSendFailures()
	{
		return GetValue<bool>(SendFailures, Master.SendFailures);
	}

	public EMsg GetCheckProduct()
	{
		return GetValue<EMsg>(CheckProduct, Master.CheckProduct);
	}

	public float GetProductAlert()
	{
		return GetValue<float>(ProductAlert, Master.ProductAlert);
	}

	public EMsg GetCheckCash()
	{
		return GetValue<EMsg>(CheckCash, Master.CheckCash);
	}

	public float GetCashAlert()
	{
		return GetValue<float>(CashAlert, Master.CashAlert);
	}

	public EMsg GetCustomerInjury()
	{
		return GetValue<EMsg>(CustomerInjury, Master.CustomerInjury);
	}

	public EMsg GetDealerInjury()
	{
		return GetValue<EMsg>(DealerInjury, Master.DealerInjury);
	}

	public EMsg GetIsStuckAlert()
	{
		return GetValue<EMsg>(IsStuckAlert, Master.IsStuckAlert);
	}

	public int GetIsStuckCount()
	{
		return GetValue<int>(IsStuckCount, Master.IsStuckCount);
	}

	public int GetIsStuckRadius()
	{
		return GetValue<int>(IsStuckRadius, Master.IsStuckRadius);
	}

	public EMsg GetNavigation()
	{
		return GetValue<EMsg>(Navigation, Master.Navigation);
	}

	public int GetINavDeltaTime()
	{
		return GetValue<int>(NavDeltaTime, Master.NavDeltaTime);
	}

	public static DealerPrefs Prefs(string firstName)
	{
		if (!MasterOnly)
		{
			if (!_dealerSettings.TryGetValue(firstName, out var value))
			{
				return AddDealer(firstName);
			}
			return value;
		}
		return Master;
	}

	private static DealerPrefs AddDealer(string name)
	{
		DealerPrefs dealerPrefs = new DealerPrefs
		{
			DealerGroup = MelonPreferences.CreateCategory("DealersSendTexts_" + name, name + " Preferences")
		};
		if (name == "_Master")
		{
			dealerPrefs.CreateDealers = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "00_CreateDealers", false, "Create settings per dealer [requires restart]", (string)null, false, false, (ValueValidator)null, (string)null);
			((MelonPreferences_Entry)dealerPrefs.CreateDealers).Comment = "Create separate settings panel for each dealer. Requires restart. (\"default\" false but will not auto delete if reset)";
		}
		else
		{
			dealerPrefs.OverrideMaster = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "00_OverrideMaster", false, "[Override Master]: Use these settings instead [false]", (string)null, false, false, (ValueValidator)null, (string)null);
			((MelonPreferences_Entry)dealerPrefs.OverrideMaster).Comment = "Use individual dealer profile settings instead of generic master settings (default false)";
		}
		dealerPrefs.ShowStarted = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "01_ShowStarted", EMsg.Silent, "Text: When a new deal is started [default silent]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.ShowSuccess = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "02_ShowSuccess", EMsg.Silent, "Text: When deal successfully completed [silent]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.ShowFailure = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "03_ShowFailure", EMsg.Notify, "Text: When deal failed / timed out [notify]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.ShowSummary = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "04_ShowSummary", true, "Daily: Summary of deals completed that day [true]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.SendCustomers = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "05_SendCustomers", false, "Daily: Time of today's deal, per customer [false]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.SendLocations = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "06_SendLocations", false, "Daily: List of locations visited today [false]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.SendFailures = dealerPrefs.DealerGroup.CreateEntry<bool>(name + "07_SendFailures", true, "Daily: List of failed customers / locations [true]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.CheckProduct = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "08_CheckProduct", EMsg.Silent, "Alert: When product count below threshold [silent]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.ProductAlert = dealerPrefs.DealerGroup.CreateEntry<float>(name + "09_ProductAlert", 20f, "--Product count (total) to trigger alert [20]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.CheckCash = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "10_CheckCashLevel", EMsg.Disable, "Alert: When cash is above threshold [disable]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.CashAlert = dealerPrefs.DealerGroup.CreateEntry<float>(name + "11_CashAlert", 50000f, "--Minimum cash amount to trigger alert [50,000]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.CustomerInjury = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "12_CustomerInjury", EMsg.Silent, "Alert: When customer knocked out or dies [silent]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.DealerInjury = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "13_DealerInjury", EMsg.Notify, "Alert: When dealer is knocked out or dies [notify]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.IsStuckAlert = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "14_IsStuckAlert", EMsg.Notify, "Alert: When dealer hasn't moved in too long [notify]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.IsStuckCount = dealerPrefs.DealerGroup.CreateEntry<int>(name + "15_IsStuckCount", 4, "Failed move checks before sending stuck alert [4]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.IsStuckRadius = dealerPrefs.DealerGroup.CreateEntry<int>(name + "16_IsStuckRadius", 5, "Min. move distance to not be considered stuck [5]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.Navigation = dealerPrefs.DealerGroup.CreateEntry<EMsg>(name + "17_Navigation", EMsg.Notify, "Text: When new navigation target set [notify]", (string)null, false, false, (ValueValidator)null, (string)null);
		dealerPrefs.NavDeltaTime = dealerPrefs.DealerGroup.CreateEntry<int>(name + "18_NavDeltaTime", 5, "Min. time between navigation messages (minutes) [5]", (string)null, false, false, (ValueValidator)null, (string)null);
		((MelonPreferences_Entry)dealerPrefs.ShowStarted).Comment = "Send a text message when a new deal is started (default silent)";
		((MelonPreferences_Entry)dealerPrefs.ShowSuccess).Comment = "Send a text message when a deal is successfully completed (default silent)";
		((MelonPreferences_Entry)dealerPrefs.ShowFailure).Comment = "Send a text message when a deal is failed or timed out (default notify)";
		((MelonPreferences_Entry)dealerPrefs.ShowSummary).Comment = "Send a summary each night of deals completed / failed and money made (default true)";
		((MelonPreferences_Entry)dealerPrefs.SendCustomers).Comment = "Send a summary each night time of deal with each customer (default false)";
		((MelonPreferences_Entry)dealerPrefs.SendLocations).Comment = "Send a summary each night of locations visited that day for successful deals (default false)";
		((MelonPreferences_Entry)dealerPrefs.SendFailures).Comment = "Send a summary each night of failed customers / locations (default true)";
		((MelonPreferences_Entry)dealerPrefs.CheckProduct).Comment = "Send a text message when product count (any type) dips below a threshold (default silent)";
		((MelonPreferences_Entry)dealerPrefs.ProductAlert).Comment = "Threshold to send low product alert (total count of product, e.g. brick = 20) (default 20)";
		((MelonPreferences_Entry)dealerPrefs.CheckCash).Comment = "Send a text message when cash on hand is above a threshold (default disable)";
		((MelonPreferences_Entry)dealerPrefs.CashAlert).Comment = "Amount of cash on hand to send cash alert (default 50,000)";
		((MelonPreferences_Entry)dealerPrefs.CustomerInjury).Comment = "Send a text message when a customer is knocked out or is killed (default silent)";
		((MelonPreferences_Entry)dealerPrefs.DealerInjury).Comment = "Send a text message when a dealer is knocked out or is killed (default notify)";
		((MelonPreferences_Entry)dealerPrefs.IsStuckAlert).Comment = "Send a text message when dealer has not moved from one spot for too long (default notify)";
		((MelonPreferences_Entry)dealerPrefs.IsStuckCount).Comment = "Number of failed checks to send stuck alert (every 20 min) (default 4)";
		((MelonPreferences_Entry)dealerPrefs.IsStuckRadius).Comment = "Minimum distance dealer must move to not be considered stuck (default 5)";
		((MelonPreferences_Entry)dealerPrefs.Navigation).Comment = "Send a message when setting a new movement destination (default notify)";
		((MelonPreferences_Entry)dealerPrefs.NavDeltaTime).Comment = "Minimum cooldown minutes between navigation messages in minutes (default 5)";
		dealerPrefs.DealerGroup.SetFilePath(ModSaveData.PrefsPath);
		_dealerSettings[name] = dealerPrefs;
		return dealerPrefs;
	}

	public static void ClearAll()
	{
		foreach (DealerPrefs value in _dealerSettings.Values)
		{
			value.DealerGroup.Entries.Clear();
			value.DealerGroup = null;
		}
		_dealerSettings.Clear();
		Initialized = false;
	}

	private T GetValue<T>(MelonPreferences_Entry<T> local, MelonPreferences_Entry<T> master)
	{
		if (!MasterOnly && OverrideMaster.Value)
		{
			return local.Value;
		}
		return master.Value;
	}
}
[Serializable]
public class SaleData
{
	public string Product;

	public string Description;

	public string Customer;

	public string Location;

	public string Window;

	public string Started;

	public string Cost;

	public string Status;

	public string Date;

	public float Payment;

	public int Quantity;

	public int StartTime;

	[JsonIgnore]
	public DealerManager Stats;

	public SaleData()
	{
	}

	public SaleData(Contract contract, DealerManager stats)
	{
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		Stats = stats;
		Product = contract.ProductList.entries[0].ProductID;
		Description = contract.ProductList.GetCommaSeperatedString();
		Customer component = ((Component)contract.Customer).GetComponent<Customer>();
		object obj;
		if (component == null)
		{
			obj = null;
		}
		else
		{
			NPC nPC = component.NPC;
			obj = ((nPC != null) ? nPC.fullName : null);
		}
		if (obj == null)
		{
			obj = "Unknown";
		}
		Customer = (string)obj;
		Location = contract.DeliveryLocation.LocationName;
		EDealWindow window = DealWindowInfo.GetWindow(contract.DeliveryWindow.WindowStartTime);
		Window = ((object)(EDealWindow)(ref window)).ToString();
		Started = TimeManager.Get12HourTime((float)contract.AcceptTime.time, true);
		Cost = MoneyManager.FormatAmount(contract.Payment * (1f - stats.Dealer.Cut), false, false);
		Status = "Accepted";
		Date = Util.DayDate();
		Payment = contract.Payment * (1f - stats.Dealer.Cut);
		Quantity = contract.ProductList.GetTotalQuantity();
		StartTime = contract.AcceptTime.time;
	}
}
[Serializable]
public class ModSaveData : SaveData
{
	public const string PATHBASE = "UserData\\DealersSendTexts";

	public const string DATAFILE = "Data.json";

	public const string PREFFILE = "Config.cfg";

	private static string _activeSaveDir;

	private static string _activeSavePath;

	public Dictionary<string, DealerState> DealerStates = new Dictionary<string, DealerState>();

	public HashSet<string> Completed = new HashSet<string>();

	public List<Location> Locations = new List<Location>();

	public static string DataPath => Path.Combine(_activeSavePath ?? "UserData\\DealersSendTexts", "Data.json");

	public static string PrefsPath => Path.Combine(_activeSavePath ?? "UserData\\DealersSendTexts", "Config.cfg");

	public static void SaveData(string pathFromSaver)
	{
		SetActiveSave(pathFromSaver);
		MelonLogger.Msg("Attempting to save " + DataPath);
		if (!Directory.Exists(Path.GetDirectoryName(DataPath)))
		{
			Directory.CreateDirectory(DataPath);
		}
		ModSaveData modSaveData = new ModSaveData
		{
			DealerStates = DealerManager.Dealers.ToDictionary((KeyValuePair<string, DealerManager> kvp) => kvp.Key, (KeyValuePair<string, DealerManager> kvp) => kvp.Value.State),
			Completed = ContractManager.Completed,
			Locations = LocationManager.All()
		};
		MelonLogger.Msg("Serializing:");
		try
		{
			string contents = JsonConvert.SerializeObject((object)modSaveData, (Formatting)1);
			File.WriteAllText(DataPath, contents);
		}
		catch (Exception arg)
		{
			MelonLogger.Error($"[DealersSendTexts] Failed to save to {DataPath}: {arg}");
		}
		MelonLogger.Msg("[DealersSendTexts] Successfully saved " + DataPath);
		MelonLogger.Msg(Application.version);
	}

	public static void LoadData(string pathFromLoader)
	{
		SetActiveSave(pathFromLoader);
		if (!File.Exists(DataPath))
		{
			DealerText.ClearAll();
			return;
		}
		try
		{
			MelonLogger.Msg("Loading " + DataPath + ":");
			ModSaveData modSaveData = JsonConvert.DeserializeObject<ModSaveData>(File.ReadAllText(DataPath));
			ContractManager.Completed = modSaveData.Completed;
			foreach (Location location in modSaveData.Locations)
			{
				LocationManager.Add(location);
			}
			foreach (KeyValuePair<string, DealerState> dealerState in modSaveData.DealerStates)
			{
				if (DealerManager.Dealers.TryGetValue(dealerState.Key, out var value))
				{
					value.State = dealerState.Value;
				}
				else
				{
					DealerManager.StateCache.Add(dealerState.Key, dealerState.Value);
				}
			}
		}
		catch (Exception arg)
		{
			MelonLogger.Error($"[DealersSendTexts] Failed to load from {DataPath}: {arg}");
		}
		MelonLogger.Msg("[DealersSendTexts] Successfully loaded " + DataPath);
	}

	private static void SetActiveSave(string gamePath)
	{
		string text = gamePath;
		if (string.IsNullOrEmpty(text))
		{
			text = Singleton<SaveManager>.Instance.IndividualSavesContainerPath;
		}
		string text2 = (Directory.Exists(text) ? text : Path.GetDirectoryName(text));
		_activeSaveDir = (string.IsNullOrEmpty(text2) ? "Unknown Save" : new DirectoryInfo(text2).Name);
		_activeSavePath = Path.Combine("UserData\\DealersSendTexts", _activeSaveDir);
		Directory.CreateDirectory(_activeSavePath);
	}
}
public class Location
{
	public SerialVector3 Position;

	public string Name;

	public string Region;

	public int StartedCount;

	public int SuccessCount;

	public int FailureCount;

	public Dictionary<string, int> CustomerCount { get; } = new Dictionary<string, int>();


	public Dictionary<string, int> DealerCount { get; } = new Dictionary<string, int>();


	public void RecordStarted()
	{
		StartedCount++;
	}

	public void RecordSuccess()
	{
		SuccessCount++;
	}

	public void RecordFailure()
	{
		FailureCount++;
	}

	public void RecordCustomer(string name)
	{
		CustomerCount[name] = ((!CustomerCount.TryGetValue(name, out var value)) ? 1 : (value + 1));
	}

	public void RecordDealer(string name)
	{
		DealerCount[name] = ((!DealerCount.TryGetValue(name, out var value)) ? 1 : (value + 1));
	}

	public float DistanceTo(Vector3 location)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		return Vector3.Distance(location, Position.ToVector3());
	}
}
public static class LocationManager
{
	private static readonly List<Location> Locations = new List<Location>();

	public static void Register(string name, Vector3 position)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		if (!Locations.Any((Location l) => l.Name == name))
		{
			Locations.Add(new Location
			{
				Name = name,
				Position = new SerialVector3(position)
			});
		}
	}

	public static void Add(Location location)
	{
		if (!Locations.Contains(location))
		{
			Locations.Add(location);
		}
	}

	public static Location GetNearest(Vector3 position)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		return Locations.OrderBy((Location l) => l.DistanceTo(position)).FirstOrDefault();
	}

	public static Location GetNearest(Vector3 position, out float distance)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		Location nearest = GetNearest(position);
		distance = Vector3.Distance(nearest.Position.ToVector3(), position);
		return nearest;
	}

	public static string Describe(Vector3 position, string noDistPrefix = "to")
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		float distance;
		Location nearest = GetNearest(position, out distance);
		if (!(distance > 1.5f))
		{
			return noDistPrefix + " " + nearest.Name;
		}
		return $"{distance:#.#} meters from {nearest.Name}";
	}

	public static List<Location> All()
	{
		return Locations;
	}

	public static void ClearAll()
	{
		foreach (Location location in Locations)
		{
			location.CustomerCount.Clear();
			location.DealerCount.Clear();
		}
		Locations.Clear();
	}
}
public enum EIcon
{
	None = -1,
	Started,
	Success,
	Failure,
	Summary,
	Customer,
	Location,
	ProdAlert,
	HurtAlert,
	KnockOut,
	HasDied,
	Navigate
}
public static class MessageManager
{
	public static string Create(EContract state, Contract contract, SaleData sale)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_025a: Unknown result type (might be due to invalid IL or missing references)
		Vector3 position = ((Component)contract.DeliveryLocation.CustomerStandPoint).transform.position;
		Vector3 position2 = ((Component)contract.Dealer).transform.position;
		string text = Vector3.Distance(position, position2).ToString("#.#");
		int num = contract.Dealer.ActiveContracts.Count - 1;
		switch (state)
		{
		case EContract.Started:
		{
			string text3 = Util.Prefix(sale.Location);
			return sale.Customer + " at " + sale.Started + ": " + sale.Description + " for " + sale.Cost + ", " + text3 + " (" + text + " meters), " + sale.Window + ".";
		}
		case EContract.Success:
		{
			int num3 = TimeManager.AddMinutesTo24HourTime(contract.DeliveryWindow.WindowStartTime, 60);
			GameDateTime val = default(GameDateTime);
			((GameDateTime)(ref val))..ctor(contract.AcceptTime.elapsedDays, num3);
			float num4 = 0f;
			if (NetworkSingleton<TimeManager>.Instance.IsCurrentDateWithinRange(contract.AcceptTime, val))
			{
				num4 = sale.Payment * 0.1f;
				sale.Payment += num4;
				sale.Cost = MoneyManager.FormatAmount(sale.Payment, false, false);
			}
			string text4 = Util.HoursMins(Util.TimeDiff(sale.StartTime, Util.IntTime()));
			string text5 = ((num4 > 0f) ? (" (" + MoneyManager.FormatAmount(num4, false, true) + " bonus incl.)") : "");
			return $"Completed deal for {sale.Customer} in {text4}, made {sale.Cost}{text5}; {num} active deals";
		}
		case EContract.Failure:
		{
			string text2 = "Failed deal: ";
			int num2 = 5;
			text2 = (sale.Stats.IsDealerHurt(out var status) ? (text2 + "Dealer " + status) : (sale.Stats.IsCustomerHurt(sale.Customer, out status) ? (text2 + "Customer " + status) : (Pathing.IsStuck(sale.Stats.Dealer) ? (text2 + "Stuck " + LocationManager.Describe(((Component)sale.Stats.Dealer).transform.position, "at")) : ((Util.TimeDiff(contract.DeliveryWindow.WindowEndTime, Util.IntTime(), is24hour: false) < num2) ? (text2 + "Ran out of time") : ((Math.Abs(Util.IntTime() - 700) >= num2) ? (text2 + "Unknown reason") : (text2 + "Went to bed too early"))))));
			return $"{text2} for {sale.Customer} from {text} meters, lost potential {sale.Cost}; {num} active deals.";
		}
		default:
			return "";
		}
	}

	public static void Send(Dealer dealer, EIcon icon, string message, bool notify)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		if (((NPC)dealer).MSGConversation != null)
		{
			Message val = new Message(GetIcon(icon) + message, (ESenderType)1, false, -1);
			((NPC)dealer).MSGConversation.SendMessage(val, notify, false);
		}
	}

	private static string GetIcon(EIcon state)
	{
		return state switch
		{
			EIcon.Started => " <color=blue><b>▶</b></color> ", 
			EIcon.Success => " <color=green><b>✔</b></color> ", 
			EIcon.Failure => " <color=red><b>✖</b></color> ", 
			EIcon.Summary => " <color=brown><b>☰</b></color> ", 
			EIcon.Customer => " <color=darkblue><b>✪</b></color> ", 
			EIcon.Location => " <color=teal><b>✦</b></color> ", 
			EIcon.ProdAlert => " <color=purple><b>⁉</b></color> ", 
			EIcon.HurtAlert => " <color=maroon><b>‼</b></color> ", 
			EIcon.KnockOut => " <color=orange><b>⁂</b></color> ", 
			EIcon.HasDied => " <color=red><b>☠</b></color> ", 
			EIcon.Navigate => " <color=blue>∇</color> ", 
			_ => "", 
		};
	}
}
[Serializable]
public class DealerState
{
	public string MostRecent = "Never";

	public int SaleCount;

	public Dictionary<string, string> TodaysSales = new Dictionary<string, string>();

	public Dictionary<string, string> RecentSale = new Dictionary<string, string>();

	public Dictionary<string, int> Products = new Dictionary<string, int>();

	public HashSet<FailureKey> Failures = new HashSet<FailureKey>();

	public List<SaleData> DailySales = new List<SaleData>();

	public List<SaleData> TotalSales = new List<SaleData>();

	public void ClearAll(bool daily = false)
	{
		DailySales.Clear();
		TodaysSales.Clear();
		Failures.Clear();
		if (!daily)
		{
			Products.Clear();
			RecentSale.Clear();
			TotalSales.Clear();
		}
	}
}
[Serializable]
public struct SerialVector3
{
	public float x;

	public float y;

	public float z;

	public SerialVector3(float x, float y, float z)
	{
		this.x = x;
		this.y = y;
		this.z = z;
	}

	public SerialVector3(Vector3 v)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		x = v.x;
		y = v.y;
		z = v.z;
	}

	public readonly Vector3 ToVector3()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		return new Vector3(x, y, z);
	}
}
[Serializable]
public struct FailureKey
{
	public string Customer;

	public string Location;

	public FailureKey(string customer, string location)
	{
		Customer = customer;
		Location = location;
	}

	public override readonly bool Equals(object obj)
	{
		if (obj is FailureKey failureKey && Customer == failureKey.Customer)
		{
			return Location == failureKey.Location;
		}
		return false;
	}

	public override readonly int GetHashCode()
	{
		return Customer.GetHashCode() ^ Location.GetHashCode();
	}
}
[HarmonyPatch(typeof(Contract), "InitializeContract")]
public class ContractInitializeContractPatch
{
	private static void Postfix(Contract __instance)
	{
		if (__instance != null && __instance.Dealer != null && !DealerManager.IsCartel(__instance.Dealer))
		{
			ContractManager.ProcessContract(__instance, EContract.Started);
		}
	}
}
[HarmonyPatch(typeof(Contract), "End")]
public class ContractEndPatch
{
	private static void Prefix(Contract __instance)
	{
		if (__instance != null && __instance.Dealer != null && !DealerManager.IsCartel(__instance.Dealer))
		{
			ContractManager.ProcessContract(__instance, EContract.Failure);
		}
	}
}
[HarmonyPatch(typeof(Contract), "Complete")]
public class ContractCompletePatch
{
	private static void Prefix(Contract __instance)
	{
		if (__instance != null && __instance.Dealer != null && !DealerManager.IsCartel(__instance.Dealer))
		{
			HashSet<string> completed = ContractManager.Completed;
			Customer component = ((Component)__instance.Customer).GetComponent<Customer>();
			object obj;
			if (component == null)
			{
				obj = null;
			}
			else
			{
				NPC nPC = component.NPC;
				obj = ((nPC != null) ? nPC.fullName : null);
			}
			if (obj == null)
			{
				obj = "Unknown";
			}
			completed.Add((string)obj);
		}
	}
}
[HarmonyPatch(typeof(Dealer), "Start")]
public class DealerStartPatch
{
	private static void Postfix(Dealer __instance)
	{
		if (__instance != null && !DealerManager.IsCartel(__instance))
		{
			DealerPrefs.Prefs(((NPC)__instance).FirstName);
		}
	}
}
[HarmonyPatch(typeof(LoadManager), "StartGame")]
public class LoadManagerStartGamePatch
{
	private static void Postfix(SaveInfo info)
	{
		ModSaveData.LoadData(info.SavePath);
	}
}
[HarmonyPatch(typeof(NPC), "Start")]
public class NPCStartPatch
{
	private static void Postfix(NPC __instance)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Expected O, but got Unknown
		if (__instance.Health != null)
		{
			__instance.Health.onDie.AddListener((UnityAction)delegate
			{
				AlertManager.NPCInjuryAlert(__instance, EIcon.HasDied);
			});
			__instance.Health.onKnockedOut.AddListener((UnityAction)delegate
			{
				AlertManager.NPCInjuryAlert(__instance, EIcon.KnockOut);
			});
		}
	}
}
[HarmonyPatch(typeof(NPCMovement))]
public class NPCMovementSetDestinationPatch
{
	private static MethodBase TargetMethod()
	{
		return AccessTools.GetDeclaredMethods(typeof(NPCMovement)).FirstOrDefault((MethodInfo method) => method.Name == "SetDestination" && method.GetParameters().Length == 5);
	}

	private static void Postfix(NPCMovement __instance, Vector3 pos, Action<WalkResult> callback, bool interruptExistingCallback, float successThreshold, float cacheMaxDistSqr)
	{
		//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_0077: Unknown result type (might be due to invalid IL or missing references)
		if (__instance == null)
		{
			return;
		}
		FieldInfo fieldInfo = AccessTools.Field(typeof(NPCMovement), "npc");
		if (!(fieldInfo != null))
		{
			return;
		}
		object? value = fieldInfo.GetValue(__instance);
		Dealer val = (Dealer)((value is Dealer) ? value : null);
		if (val != null && !DealerManager.IsCartel(val))
		{
			EMsg navigation = DealerPrefs.Prefs(((NPC)val).FirstName).GetNavigation();
			if (navigation != EMsg.Disable && DealerManager.CheckPing(val))
			{
				DealerManager.SetPing(val, Util.AbsTime());
				float num = Vector3.Distance(((Component)val).transform.position, pos);
				string arg = LocationManager.Describe(pos);
				string message = $"Headed {arg}, {num:#.#} meters away.";
				MessageManager.Send(val, EIcon.Navigate, message, navigation == EMsg.Notify);
			}
		}
	}
}
[HarmonyPatch(typeof(Player), "SleepStart")]
public class PlayerSleepStartPatch
{
	private static void Prefix()
	{
		ContractManager.SendSummary();
	}
}
[HarmonyPatch(typeof(SaveManager), "Save", new Type[] { typeof(string) })]
public class SaveManagerSavePatch
{
	private static void Prefix(string saveFolderPath)
	{
		ModSaveData.SaveData(saveFolderPath);
	}
}
[HarmonyPatch(typeof(TimeManager), "Update")]
public class TimeManagerUpdatePatch
{
	[HarmonyPostfix]
	public static void PostFix()
	{
		int num = Util.AbsTime() % 20;
		if (num == 0 && DealerManager.CheckStuck)
		{
			DealerManager.CheckStuck = false;
			foreach (DealerManager value in DealerManager.Dealers.Values)
			{
				Pathing.CheckStuck(value);
			}
		}
		if (num == 1)
		{
			DealerManager.CheckStuck = true;
		}
	}
}
public class DealerManager
{
	public static Dictionary<string, DealerManager> Dealers = new Dictionary<string, DealerManager>();

	public static Dictionary<string, DealerState> StateCache = new Dictionary<string, DealerState>();

	public static bool CheckStuck;

	public Dictionary<string, NPC> Customers = new Dictionary<string, NPC>();

	public Dealer Dealer;

	public DealerState State;

	public Vector3 Home;

	private int LastPing = -1;

	public DealerManager(Dealer dealer)
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		Dealer = dealer;
		State = (StateCache.TryGetValue(((NPC)dealer).fullName, out var value) ? value : new DealerState());
		NPCEnterableBuilding home = dealer.Home;
		Transform transform = ((Component)dealer).transform;
		Home = ((Component)home.GetClosestDoor((transform != null) ? transform.position : Vector3.zero, true)).transform.position;
		LocationManager.Register(dealer.HomeName, Home);
		foreach (Customer assignedCustomer in dealer.AssignedCustomers)
		{
			string fullName = assignedCustomer.NPC.fullName;
			if (!State.TodaysSales.ContainsKey(fullName))
			{
				State.TodaysSales[fullName] = "Never";
			}
			if (!State.RecentSale.ContainsKey(fullName))
			{
				State.RecentSale[fullName] = "Never";
			}
			AddCustomer(fullName, assignedCustomer.NPC);
		}
	}

	public static DealerManager GetStats(Dealer dealer)
	{
		if (!Dealers.TryGetValue(((NPC)dealer).fullName, out var value))
		{
			value = new DealerManager(dealer);
			Dealers.Add(((NPC)dealer).fullName, value);
		}
		return value;
	}

	public void RefreshInventory()
	{
		List<ItemSlot> allSlots = Dealer.GetAllSlots();
		if (allSlots == null || allSlots.Count == 0)
		{
			return;
		}
		List<string> list = new List<string>();
		Dictionary<string, int> dictionary = new Dictionary<string, int>();
		foreach (ItemSlot item in allSlots)
		{
			if (item.Quantity != 0)
			{
				int num = item.Quantity;
				ItemInstance itemInstance = item.ItemInstance;
				ProductItemInstance val = (ProductItemInstance)(object)((itemInstance is ProductItemInstance) ? itemInstance : null);
				if (val != null)
				{
					num *= val.Amount;
				}
				if (list.Contains(item.ItemInstance.ID))
				{
					dictionary[item.ItemInstance.ID] += num;
					continue;
				}
				list.Add(item.ItemInstance.ID);
				dictionary.Add(item.ItemInstance.ID, num);
			}
		}
		State.Products = dictionary;
	}

	public void AddCustomer(string fullName, NPC npc)
	{
		if (!Customers.ContainsKey(fullName))
		{
			Customers.Add(fullName, npc);
		}
	}

	public bool IsCustomerHurt(string name, out string status)
	{
		status = "";
		if (!Customers.TryGetValue(name, out var value))
		{
			return false;
		}
		if (value.Health.IsDead)
		{
			status = $"<color=red>(Dead {value.Health.DaysPassedSinceDeath} days)</color>";
		}
		if (value.Health.IsKnockedOut)
		{
			status = "<color=orange>(Knocked Out)</color>";
		}
		return !string.IsNullOrEmpty(status);
	}

	public bool IsDealerHurt(out string status)
	{
		status = "";
		if (((NPC)Dealer).Health.IsDead)
		{
			status = $"<color=red>Dead ({((NPC)Dealer).Health.DaysPassedSinceDeath} days)</color>";
		}
		if (((NPC)Dealer).Health.IsKnockedOut)
		{
			status = "<color=orange>(Knocked Out)</color>";
		}
		return !string.IsNullOrEmpty(status);
	}

	public static bool IsCartel(Dealer dealer)
	{
		if (Application.version.CompareTo("0.4.0") < 0)
		{
			return false;
		}
		return Type.GetType("CartelDealer")?.IsInstanceOfType(dealer) ?? false;
	}

	public static bool CheckPing(Dealer dealer)
	{
		int iNavDeltaTime = DealerPrefs.Prefs(((NPC)dealer).FirstName).GetINavDeltaTime();
		if (iNavDeltaTime > -1)
		{
			return Util.AbsTime() - GetStats(dealer).LastPing > iNavDeltaTime;
		}
		return false;
	}

	public static void SetPing(Dealer dealer, int ping)
	{
		GetStats(dealer).LastPing = ping;
	}

	public static void ClearAll()
	{
		foreach (DealerManager value in Dealers.Values)
		{
			value.Customers.Clear();
			value.State.ClearAll();
		}
		Dealers.Clear();
		StateCache.Clear();
	}

	public Vector3 DistanceToHome()
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		return Dealer.Home.GetClosestDoor(((Component)Dealer).transform.position, true).AccessPoint.position;
	}
}
public enum EMsg
{
	Notify,
	Silent,
	Disable
}
public class DealerText : MelonMod
{
	public const string ModName = "Dealers Send Texts";

	public const string Version = "2.1.0";

	public const string ModDesc = "Dealers text updates on deals and daily summary in Schedule One";

	public override void OnSceneWasLoaded(int buildIndex, string sceneName)
	{
		if (sceneName.Equals("main", StringComparison.OrdinalIgnoreCase))
		{
			DealerPrefs.Initialize();
		}
	}

	public override void OnSceneWasUnloaded(int buildIndex, string sceneName)
	{
		if (sceneName.Equals("main", StringComparison.OrdinalIgnoreCase))
		{
			ClearAll();
		}
	}

	public static void ClearAll()
	{
		ContractManager.ClearAll();
		LocationManager.ClearAll();
		DealerManager.ClearAll();
		DealerPrefs.ClearAll();
		Pathing.ClearAll();
	}
}
public static class Pathing
{
	private const int MINTIME = 5;

	private const int MAXHISTORY = 10;

	private static readonly Dictionary<string, Queue<(Vector3, int)>> PositionHistory = new Dictionary<string, Queue<(Vector3, int)>>();

	public static void RecordPosition(Dealer dealer)
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		if (dealer != null && dealer.ActiveContracts.Count != 0)
		{
			string fullName = ((NPC)dealer).fullName;
			int num = Util.AbsTime();
			if (!PositionHistory.TryGetValue(fullName, out var value))
			{
				value = (PositionHistory[fullName] = new Queue<(Vector3, int)>());
			}
			if (value.Count == 0 || Math.Abs(value.Peek().Item2 - num) >= 5)
			{
				value.Enqueue((((Component)dealer).transform.position, num));
			}
			if (value.Count > 10)
			{
				value.Dequeue();
			}
		}
	}

	public static bool IsStuck(Dealer dealer)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		if (dealer == null || !PositionHistory.TryGetValue(((NPC)dealer).fullName, out var value))
		{
			return false;
		}
		DealerPrefs dealerPrefs = DealerPrefs.Prefs(((NPC)dealer).FirstName);
		int isStuckCount = dealerPrefs.GetIsStuckCount();
		if (value.Count < isStuckCount)
		{
			return false;
		}
		(Vector3, int)[] array = value.Reverse().Take(isStuckCount).ToArray();
		Vector3 item = array[0].Item1;
		(Vector3, int)[] array2 = array;
		for (int i = 0; i < array2.Length; i++)
		{
			if (Vector3.Distance(array2[i].Item1, item) > (float)dealerPrefs.GetIsStuckRadius())
			{
				return false;
			}
		}
		return true;
	}

	public static void CheckStuck(DealerManager stats)
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		Dealer dealer = stats.Dealer;
		RecordPosition(dealer);
		if (IsStuck(dealer) && !stats.IsDealerHurt(out var _) && dealer.ActiveContracts.Count != 0)
		{
			bool notify = DealerPrefs.Prefs(((NPC)dealer).FirstName).GetIsStuckAlert() == EMsg.Notify;
			string arg = LocationManager.Describe(((Component)dealer).transform.position, "at");
			string message = $"I may be stuck in {((NPC)dealer).Region} {arg}. Most recent deal was {stats.State.MostRecent}.";
			MessageManager.Send(dealer, EIcon.HurtAlert, message, notify);
		}
	}

	public static void ClearAll()
	{
		PositionHistory.Clear();
	}
}
public enum EContract
{
	Started,
	Success,
	Failure
}
public static class ContractManager
{
	public static HashSet<string> Completed = new HashSet<string>();

	public static void ProcessContract(Contract contract, EContract state)
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		if (contract != null && contract.Dealer != null)
		{
			DealerPrefs prefs = DealerPrefs.Prefs(((NPC)contract.Dealer).FirstName);
			DealerManager stats = DealerManager.GetStats(contract.Dealer);
			SaleData saleData = new SaleData(contract, stats);
			NPC nPC = ((Component)contract.Customer).GetComponent<Customer>().NPC;
			Vector3 position = contract.DeliveryLocation.CustomerStandPoint.position;
			stats.AddCustomer(nPC.fullName, nPC);
			LocationManager.Register(saleData.Location, position);
			Location nearest = LocationManager.GetNearest(position);
			if (state == EContract.Failure && Completed.Contains(saleData.Customer))
			{
				state = EContract.Success;
			}
			if (state == EContract.Started)
			{
				ContractStarted(contract, saleData, nearest, prefs);
			}
			if (state == EContract.Success)
			{
				ContractSuccess(contract, saleData, nearest, prefs);
			}
			if (state == EContract.Failure)
			{
				ContractFailure(contract, saleData, nearest, prefs);
			}
		}
	}

	public static void SendSummary()
	{
		Dictionary<string, DealerManager> dealers = DealerManager.Dealers;
		if (dealers == null || dealers.Count == 0)
		{
			return;
		}
		foreach (DealerManager value in dealers.Values)
		{
			AlertManager.DailySummary(value);
		}
	}

	private static void ContractStarted(Contract contract, SaleData sale, Location location, DealerPrefs prefs)
	{
		location.RecordStarted();
		location.RecordCustomer(sale.Customer);
		location.RecordDealer(((NPC)contract.Dealer).fullName);
		sale.Stats.State.TotalSales.Add(sale);
		sale.Stats.State.SaleCount++;
		string message = MessageManager.Create(EContract.Started, contract, sale);
		if (prefs.GetShowStarted() != EMsg.Disable)
		{
			MessageManager.Send(contract.Dealer, EIcon.Started, message, prefs.GetShowStarted() == EMsg.Notify);
		}
	}

	private static void ContractSuccess(Contract contract, SaleData sale, Location location, DealerPrefs prefs)
	{
		sale.Status = "Success";
		DealerState state = sale.Stats.State;
		Completed.Remove(sale.Customer);
		Dictionary<string, string> todaysSales = state.TodaysSales;
		string customer = sale.Customer;
		string value = (state.RecentSale[sale.Customer] = (state.MostRecent = Util.Time()));
		todaysSales[customer] = value;
		state.DailySales.Add(sale);
		location.RecordSuccess();
		string message = MessageManager.Create(EContract.Success, contract, sale);
		if (prefs.GetShowSuccess() != EMsg.Disable)
		{
			MessageManager.Send(contract.Dealer, EIcon.Success, message, prefs.GetShowSuccess() == EMsg.Notify);
		}
		AlertManager.CheckProductAlert(contract.Dealer);
	}

	private static void ContractFailure(Contract contract, SaleData sale, Location location, DealerPrefs prefs)
	{
		sale.Status = "Failure";
		sale.Stats.State.Failures.Add(new FailureKey(sale.Customer, sale.Location));
		location.RecordFailure();
		string message = MessageManager.Create(EContract.Failure, contract, sale);
		if (prefs.GetShowFailure() != EMsg.Disable)
		{
			MessageManager.Send(contract.Dealer, EIcon.Failure, message, prefs.GetShowFailure() == EMsg.Notify);
		}
	}

	public static void ClearAll()
	{
		Completed.Clear();
	}
}
public static class Util
{
	public static int TimeDiff(int start, int end, bool is24hour = true)
	{
		start = (is24hour ? TimeManager.GetMinSumFrom24HourTime(start) : start);
		end = (is24hour ? TimeManager.GetMinSumFrom24HourTime(end) : end);
		int num = end - start;
		if (num < 0)
		{
			num += 1440;
		}
		return num;
	}

	public static string GetName(string input)
	{
		string text = Registry.GetItem(input)?.Name;
		if (string.IsNullOrEmpty(text))
		{
			return input;
		}
		return text;
	}

	public static string Prefix(string input)
	{
		if (input.ToLower().StartsWith("outside") || input.ToLower().StartsWith("next") || input.ToLower().StartsWith("under") || input.ToLower().StartsWith("behind") || input.ToLower().StartsWith("in "))
		{
			return input;
		}
		return "at the " + input;
	}

	public static string DayDate()
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		return $"{NetworkSingleton<TimeManager>.Instance.CurrentDay}, Day {NetworkSingleton<TimeManager>.Instance.ElapsedDays}";
	}

	public static int IntTime()
	{
		return NetworkSingleton<TimeManager>.Instance.CurrentTime;
	}

	public static int AbsTime()
	{
		return NetworkSingleton<TimeManager>.Instance.GetTotalMinSum();
	}

	public static string Time()
	{
		return TimeManager.Get12HourTime((float)IntTime(), true);
	}

	public static string HoursMins(int time)
	{
		return ((time / 60 > 0) ? (time / 60 + "hr ") : "") + time % 60 + "min";
	}
}