Decompiled source of BetterSpecialCustomers v1.0.0

BetterSpecialCustomers_Mono.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using BetterSpecialCustomers;
using BetterSpecialCustomers.Diagnostics;
using BetterSpecialCustomers.Quests;
using BetterSpecialCustomers.Relationships;
using FishNet;
using FishNet.Connection;
using HarmonyLib;
using MelonLoader;
using Microsoft.CodeAnalysis;
using S1API.Entities;
using S1API.Entities.Appearances.AccessoryFields;
using S1API.Entities.Appearances.Base;
using S1API.Entities.Appearances.BodyLayerFields;
using S1API.Entities.Appearances.CustomizationFields;
using S1API.Entities.Appearances.FaceLayerFields;
using S1API.Entities.Schedule;
using S1API.Internal.Abstraction;
using S1API.Leveling;
using S1API.Map;
using S1API.Map.Buildings;
using S1API.Money;
using S1API.Quests;
using S1API.Saveables;
using ScheduleOne;
using ScheduleOne.Core.Items.Framework;
using ScheduleOne.DevUtilities;
using ScheduleOne.Dialogue;
using ScheduleOne.Economy;
using ScheduleOne.ItemFramework;
using ScheduleOne.Levelling;
using ScheduleOne.Map;
using ScheduleOne.NPCs;
using ScheduleOne.NPCs.Relation;
using ScheduleOne.NPCs.Responses;
using ScheduleOne.PlayerScripts;
using ScheduleOne.Police;
using ScheduleOne.SpecialCustomers;
using ScheduleOne.Storage;
using UnityEngine;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "BetterSpecialCustomers", "1.0.0", "Lumahex", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: HarmonyDontPatchAll]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("BetterSpecialCustomers_Mono")]
[assembly: AssemblyConfiguration("MONO")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c7e4d29411e07561897b504f24ae00b062ef3bf0")]
[assembly: AssemblyProduct("BetterSpecialCustomers_Mono")]
[assembly: AssemblyTitle("BetterSpecialCustomers_Mono")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MONO_IL2CPP_Template.TemplateUtils
{
	public static class AssetBundleUtils
	{
		private static Core mod = MelonAssembly.FindMelonInstance<Core>();

		private static MelonAssembly melonAssembly = ((MelonBase)mod).MelonAssembly;

		public static AssetBundle LoadAssetBundle(string bundleFileName)
		{
			try
			{
				string text = typeof(Core).Namespace + ".Assets." + bundleFileName;
				Stream manifestResourceStream = melonAssembly.Assembly.GetManifestResourceStream(text ?? "");
				if (manifestResourceStream == null)
				{
					((MelonBase)mod).Unregister("AssetBundle: '" + text + "' not found. \nOpen .csproj file and search for '" + bundleFileName + "'.\nIf it doesn't exist,\nCopy your asset to Assets/ folder then look for 'your.assetbundle' in .csproj file.", false);
					return null;
				}
				return AssetBundle.LoadFromStream(manifestResourceStream);
			}
			catch (Exception arg)
			{
				((MelonBase)mod).Unregister($"Failed to load AssetBundle. Please report to dev: {arg}", false);
				return null;
			}
		}

		public static AssetBundle GetLoadedAssetBundle(string asset_name_flag)
		{
			AssetBundle[] array = AssetBundle.GetAllLoadedAssetBundles().ToArray();
			try
			{
				AssetBundle[] array2 = array;
				foreach (AssetBundle val in array2)
				{
					if (val.Contains(asset_name_flag))
					{
						return val;
					}
				}
				string text = "";
				AssetBundle[] array3 = array;
				foreach (AssetBundle val2 in array3)
				{
					string[] allAssetNames = val2.GetAllAssetNames();
					string text2 = string.Join("\n\r -", allAssetNames);
					text = text + ((Object)val2).name + $"({allAssetNames.Length} assets):" + text2;
				}
				throw new Exception($"Asset '{asset_name_flag}' not found in {array.Length} bundle(s).\n{text}");
			}
			catch (Exception arg)
			{
				((MelonBase)mod).Unregister($"Failed to get loaded AssetBundle. Please report to dev: \n{arg}", false);
				return null;
			}
		}

		public static GameObject LoadAssetFromBundle(string asset_name)
		{
			AssetBundle loadedAssetBundle = GetLoadedAssetBundle(asset_name);
			return loadedAssetBundle.LoadAsset<GameObject>(asset_name);
		}
	}
}
namespace BetterSpecialCustomers
{
	public class Core : MelonMod
	{
		private float _nextQuestTick;

		public override void OnInitializeMelon()
		{
			((MelonBase)this).LoggerInstance.Msg("Initialized.");
			PatchDiagnostics.Probe();
			PatchDiagnostics.PatchAllClasses(((MelonBase)this).HarmonyInstance);
			PatchDiagnostics.ListPatchedMethods(((MelonBase)this).HarmonyInstance);
		}

		public override void OnUpdate()
		{
			if (Time.unscaledTime >= _nextQuestTick)
			{
				_nextQuestTick = Time.unscaledTime + 1f;
				HippiesTask.Tick();
			}
		}
	}
}
namespace BetterSpecialCustomers.Relationships
{
	public static class GroupRelationships
	{
		private class State
		{
			public float Current = 2.5f;

			public float Applied = 2.5f;

			public bool InVisit;

			public bool GainedThisVisit;

			public int SoldThisVisit;
		}

		public const float Min = 0f;

		public const float Max = 5f;

		public const float DefaultValue = 2.5f;

		public const float MultiplierAtMin = 0.5f;

		public const float MultiplierAtMax = 1.5f;

		public const float GainPerVisit = 0.5f;

		public const float MinFractionOfLimitForGain = 0.75f;

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

		public const string JsonKey = "BetterSpecialCustomers_Relationships";

		private static readonly Regex SectionRegex = new Regex("\"BetterSpecialCustomers_Relationships\"\\s*:\\s*\\[(.*?)\\]", RegexOptions.Compiled | RegexOptions.Singleline);

		private static readonly Regex EntryRegex = new Regex("\\{([^}]*)\\}", RegexOptions.Compiled);

		private static string Key(string groupId)
		{
			return (groupId ?? string.Empty).ToLowerInvariant();
		}

		private static State GetState(string groupId)
		{
			string key = Key(groupId);
			if (!States.TryGetValue(key, out var value))
			{
				value = new State();
				States[key] = value;
			}
			return value;
		}

		private static float Clamp(float value)
		{
			return Math.Max(0f, Math.Min(5f, value));
		}

		public static float GetCurrent(string groupId)
		{
			return GetState(groupId).Current;
		}

		public static float GetApplied(string groupId)
		{
			return GetState(groupId).Applied;
		}

		public static ERelationshipCategory GetCategory(float value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return RelationshipCategory.GetCategory(value);
		}

		public static float GetMultiplier(string groupId)
		{
			float num = (GetApplied(groupId) - 0f) / 5f;
			return 0.5f + 1f * num;
		}

		public static void BeginVisit(string groupId)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			State state = GetState(groupId);
			if (!state.InVisit)
			{
				state.InVisit = true;
				state.GainedThisVisit = false;
				state.SoldThisVisit = 0;
				state.Applied = state.Current;
				Melon<Core>.Logger.Msg($"[{groupId}] new visit. Relationship {state.Applied:0.00} ({GetCategory(state.Applied)}) now applies: buy multiplier x{GetMultiplier(groupId):0.00}");
			}
		}

		public static void EndVisit(string groupId)
		{
			GetState(groupId).InVisit = false;
		}

		public static void RegisterSale(string groupId, int unitsSold, int limit)
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			State state = GetState(groupId);
			if (!state.GainedThisVisit && limit > 0)
			{
				state.SoldThisVisit += unitsSold;
				float num = (float)state.SoldThisVisit / (float)limit;
				if (num < 0.75f)
				{
					Melon<Core>.Logger.Msg($"[{groupId}] sold {state.SoldThisVisit} so far this visit ({num:P0} of their limit of {limit}); need {0.75f:P0} for a relationship gain");
				}
				else
				{
					state.GainedThisVisit = true;
					float current = state.Current;
					state.Current = Clamp(current + 0.5f);
					Melon<Core>.Logger.Msg($"[{groupId}] relationship {current:0.00} -> {state.Current:0.00} ({GetCategory(state.Current)}). It applies from their next visit; this visit stays at x{GetMultiplier(groupId):0.00}");
				}
			}
		}

		public static void Penalize(string groupId, float amount, string reason)
		{
			State state = GetState(groupId);
			float current = state.Current;
			state.Current = Clamp(current - amount);
			Melon<Core>.Logger.Msg($"[{groupId}] relationship {current:0.00} -> {state.Current:0.00} ({reason})");
		}

		public static void Reward(string groupId, float amount, string reason)
		{
			State state = GetState(groupId);
			float current = state.Current;
			state.Current = Clamp(current + amount);
			Melon<Core>.Logger.Msg($"[{groupId}] relationship {current:0.00} -> {state.Current:0.00} ({reason})");
		}

		private static string ReadField(string entry, string name)
		{
			Match match = Regex.Match(entry, "\"" + name + "\"\\s*:\\s*\"?([^\",\\s}]+)");
			return match.Success ? match.Groups[1].Value : null;
		}

		private static float ReadFloat(string entry, string name, float fallback)
		{
			string text = ReadField(entry, name);
			float result;
			return (text != null && float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out result)) ? Clamp(result) : fallback;
		}

		public static void Clear()
		{
			States.Clear();
		}

		public static string ToJsonProperty()
		{
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, State> state in States)
			{
				State value = state.Value;
				list.Add(string.Format(CultureInfo.InvariantCulture, "{{ \"id\": \"{0}\", \"current\": {1}, \"applied\": {2}, \"inVisit\": {3}, \"gained\": {4}, \"sold\": {5} }}", state.Key, value.Current.ToString("R", CultureInfo.InvariantCulture), value.Applied.ToString("R", CultureInfo.InvariantCulture), value.InVisit ? 1 : 0, value.GainedThisVisit ? 1 : 0, value.SoldThisVisit));
			}
			return "\"BetterSpecialCustomers_Relationships\": [ " + string.Join(", ", list) + " ]";
		}

		public static void LoadFromJson(string json)
		{
			States.Clear();
			if (string.IsNullOrEmpty(json))
			{
				return;
			}
			Match match = SectionRegex.Match(json);
			if (!match.Success)
			{
				return;
			}
			foreach (Match item in EntryRegex.Matches(match.Groups[1].Value))
			{
				string value = item.Groups[1].Value;
				string text = ReadField(value, "id");
				if (!string.IsNullOrEmpty(text))
				{
					State state = GetState(text);
					state.Current = ReadFloat(value, "current", 2.5f);
					state.Applied = ReadFloat(value, "applied", state.Current);
					state.InVisit = ReadField(value, "inVisit") == "1";
					state.GainedThisVisit = ReadField(value, "gained") == "1";
					state.SoldThisVisit = (int.TryParse(ReadField(value, "sold"), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) ? result : 0);
				}
			}
		}
	}
}
namespace BetterSpecialCustomers.Quests
{
	public class BikersQuestSave : Saveable
	{
		[SaveableField("bikers_quest")]
		public JobSaveData Data = new JobSaveData();

		public static BikersQuestSave Instance { get; private set; }

		public BikersQuestSave()
		{
			Instance = this;
		}

		protected override void OnLoaded()
		{
			Instance = this;
			Informant.Refresh();
		}
	}
	public sealed class Informant : NPC
	{
		private const string NpcId = "bikers_informant";

		private const string HideoutName = "The Piss Hut";

		private static Informant _instance;

		private static int _lookAppliedForRun = -1;

		private static readonly string[] FirstNames = new string[12]
		{
			"Dale", "Rusty", "Vince", "Eddie", "Skeet", "Lenny", "Wade", "Cody", "Marv", "Gus",
			"Terry", "Bo"
		};

		private static readonly string[] LastNames = new string[12]
		{
			"Hollis", "Brandt", "Keane", "Mercer", "Voss", "Pruitt", "Lund", "Garrity", "Boone", "Sykes",
			"Dunn", "Rourke"
		};

		private static readonly Color32[] HairColors = (Color32[])(object)new Color32[5]
		{
			new Color32((byte)30, (byte)22, (byte)18, byte.MaxValue),
			new Color32((byte)60, (byte)40, (byte)28, byte.MaxValue),
			new Color32((byte)95, (byte)65, (byte)40, byte.MaxValue),
			new Color32((byte)150, (byte)120, (byte)80, byte.MaxValue),
			new Color32((byte)120, (byte)120, (byte)120, byte.MaxValue)
		};

		private static readonly string[] HairStyles = new string[7] { "Avatar/Hair/buzzcut/BuzzCut", "Avatar/Hair/closebuzzcut/CloseBuzzCut", "Avatar/Hair/balding/Balding", "Avatar/Hair/receding/Receding", "Avatar/Hair/longslicked/LongSlicked", "Avatar/Hair/shoulderlength/ShoulderLength", "Avatar/Hair/peaked/Peaked" };

		private static readonly Color32[] ShirtColors = (Color32[])(object)new Color32[4]
		{
			new Color32((byte)25, (byte)25, (byte)25, byte.MaxValue),
			new Color32((byte)60, (byte)60, (byte)60, byte.MaxValue),
			new Color32((byte)110, (byte)25, (byte)25, byte.MaxValue),
			new Color32((byte)90, (byte)90, (byte)90, byte.MaxValue)
		};

		private static readonly Color32[] JeansColors = (Color32[])(object)new Color32[3]
		{
			new Color32((byte)30, (byte)38, (byte)60, byte.MaxValue),
			new Color32((byte)20, (byte)20, (byte)24, byte.MaxValue),
			new Color32((byte)55, (byte)65, (byte)90, byte.MaxValue)
		};

		public override bool IsPhysical => true;

		public static string PickName()
		{
			return QuestNpcUtil.RandomName(FirstNames, LastNames);
		}

		protected override void ConfigurePrefab(NPCPrefabBuilder builder)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			builder.WithIdentity("bikers_informant", "Informant", string.Empty).WithSpawnPosition(QuestNpcUtil.ParkedPosition).WithSchedule((Action<PrefabScheduleBuilder>)delegate(PrefabScheduleBuilder plan)
			{
				plan.StayInBuilding(Building.Get<ThePissHut>(), 0, 1439, (int?)null, (string)null);
			});
		}

		protected override void OnCreated()
		{
			((NPC)this).OnCreated();
			((NPC)this).Appearance.Build();
			_instance = this;
			Melon<Core>.Logger.Msg("[quest] the informant NPC was created");
			((NPC)this).OnDeath += BikersQuest.Job.OnObjectiveDone;
			Refresh();
		}

		public static void Refresh()
		{
			if (_instance == null)
			{
				return;
			}
			JobSaveData jobSaveData = BikersQuestSave.Instance?.Data;
			bool flag = jobSaveData != null && jobSaveData.Accepted && !jobSaveData.TargetKilled;
			try
			{
				if (flag)
				{
					_instance.Deploy(jobSaveData);
				}
				else
				{
					QuestNpcUtil.Park((NPC)(object)_instance, "bikers_informant");
				}
			}
			catch (Exception arg)
			{
				Melon<Core>.Logger.Error(string.Format("[quest] informant {0} failed: {1}", flag ? "deploy" : "park", arg));
			}
		}

		private void Deploy(JobSaveData save)
		{
			QuestNpcUtil.ApplyName((NPC)(object)this, save.TargetName);
			if (_lookAppliedForRun != save.Runs)
			{
				_lookAppliedForRun = save.Runs;
				ApplyBikerLook();
			}
			QuestNpcUtil.Revive((NPC)(object)this);
			QuestNpcUtil.EnterBuilding("bikers_informant", "The Piss Hut");
			((NPC)this).Schedule.Enable();
		}

		private void ApplyBikerLook()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_041b: Unknown result type (might be due to invalid IL or missing references)
			AppearanceUtil.ClearLayers(((NPC)this).Appearance);
			Color val = Color32.op_Implicit(QuestNpcUtil.Pick(HairColors));
			((NPC)this).Appearance.Set<Gender>((object)0f);
			((NPC)this).Appearance.Set<Height>((object)Random.Range(0.95f, 1.05f));
			((NPC)this).Appearance.Set<Weight>((object)Random.Range(0.45f, 0.75f));
			((NPC)this).Appearance.Set<SkinColor>((object)Color32.op_Implicit(QuestNpcUtil.Pick(QuestNpcUtil.SkinTones)));
			((NPC)this).Appearance.Set<EyeBallTint>((object)Color.white);
			((NPC)this).Appearance.Set<HairColor>((object)val);
			((NPC)this).Appearance.Set<HairStyle>((object)QuestNpcUtil.Pick(HairStyles));
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(0.15f, 0.1f, 0.1f);
			((NPC)this).Appearance.WithFaceLayer<Face>((Random.value < 0.5f) ? "Avatar/Layers/Face/Face_Neutral" : "Avatar/Layers/Face/Face_SlightFrown", val2);
			if (Random.value < 0.6f)
			{
				((NPC)this).Appearance.WithFaceLayer<FacialHair>((Random.value < 0.5f) ? "Avatar/Layers/Face/FacialHair_Stubble" : "Avatar/Layers/Face/FacialHair_Goatee", val);
			}
			string[] items = new string[5] { "Avatar/Layers/Top/T-Shirt", "Avatar/Layers/Top/T-Shirt", "Avatar/Layers/Top/V-Neck", "Avatar/Layers/Top/FlannelButtonUp", "Avatar/Layers/Top/RolledButtonUp" };
			((NPC)this).Appearance.WithBodyLayer<Shirts>(QuestNpcUtil.Pick(items), Color32.op_Implicit(QuestNpcUtil.Pick(ShirtColors)));
			((NPC)this).Appearance.WithBodyLayer<Pants>((Random.value < 0.7f) ? "Avatar/Layers/Bottom/Jeans" : "Avatar/Layers/Bottom/CargoPants", Color32.op_Implicit(QuestNpcUtil.Pick(JeansColors)));
			Color color = ((Random.value < 0.7f) ? new Color(0.08f, 0.08f, 0.08f) : new Color(0.2f, 0.12f, 0.07f));
			float value = Random.value;
			if (value < 0.6f)
			{
				AppearanceUtil.Wear<Chest>(((NPC)this).Appearance, color, new string[2] { "Avatar/Accessories/Chest/BikerJacket/BikerJacket", "Avatar/Accessories/Chest/BikerJacket/Biker Jacket" });
			}
			else if (value < 0.85f)
			{
				AppearanceUtil.Wear<Chest>(((NPC)this).Appearance, color, new string[2] { "Avatar/Accessories/Chest/LeatherJacket/LeatherJacket", "Avatar/Accessories/Chest/LeatherJacket/leatherJacket" });
			}
			else
			{
				AppearanceUtil.Wear<Chest>(((NPC)this).Appearance, color, new string[1] { "Avatar/Accessories/Chest/OpenVest/OpenVest" });
			}
			AppearanceUtil.Wear<Feet>(((NPC)this).Appearance, new Color(0.1f, 0.1f, 0.1f), new string[1] { (Random.value < 0.85f) ? "Avatar/Accessories/Feet/CombatBoots/CombatBoots" : "Avatar/Accessories/Feet/Sneakers/Sneakers" });
			if (Random.value < 0.5f)
			{
				AppearanceUtil.Wear<Waist>(((NPC)this).Appearance, new Color(0.1f, 0.07f, 0.05f), new string[1] { "Avatar/Accessories/Waist/Belt/Belt" });
			}
			float value2 = Random.value;
			if (value2 < 0.3f)
			{
				AppearanceUtil.Wear<Head>(((NPC)this).Appearance, new Color(0.1f, 0.1f, 0.1f), new string[2] { "Avatar/Accessories/Head/Bandana/Bandana", "Avatar/Accessories/Head/PaisleyBandana/PaisleyBandana" });
			}
			else if (value2 < 0.5f)
			{
				AppearanceUtil.Wear<Head>(((NPC)this).Appearance, new Color(0.12f, 0.12f, 0.12f), new string[1] { "Avatar/Accessories/Head/Beanie/Beanie" });
			}
			else if (value2 < 0.7f)
			{
				AppearanceUtil.Wear<Head>(((NPC)this).Appearance, Color.white, new string[1] { "Avatar/Accessories/Head/Oakleys/Oakleys" });
			}
			if (Random.value < 0.4f)
			{
				AppearanceUtil.Wear<Hands>(((NPC)this).Appearance, new Color(0.1f, 0.1f, 0.1f), new string[1] { "Avatar/Accessories/Hands/FingerlessGloves/FingerlessGloves" });
			}
			if (Random.value < 0.4f)
			{
				AppearanceUtil.Wear<Neck>(((NPC)this).Appearance, Color.white, new string[2] { "Avatar/Accessories/Neck/GoldChain/GoldChain", "Avatar/Accessories/Neck/SilverChain/SilverChain" });
			}
			AppearanceUtil.ApplyNow(((NPC)this).Appearance);
		}
	}
	public class BikersQuest : Quest
	{
		internal static readonly JobController Job = new JobController(new JobDefinition
		{
			QuestIdPrefix = "bikers_informant_quest",
			RewardLogLabel = "Diesel's informant job",
			GetSave = () => BikersQuestSave.Instance?.Data,
			PickName = Informant.PickName,
			RefreshTarget = Informant.Refresh,
			CreateQuest = (string id) => QuestManager.CreateQuest<BikersQuest>(id)
		});

		protected override string Title => "Loose Lips";

		protected override string Description => "Someone in the club has been talking to the cops. Diesel Rodd wants the informant dealt with.";

		protected override bool AutoBegin => true;

		protected override void OnCreated()
		{
			((Registerable)this).OnCreated();
			Job.Attach((Quest)(object)this);
			string text = Job.TargetNameOr("the informant");
			QuestEntry val = ((Quest)this).AddEntry("Knock on the door of The Piss Hut and kill " + text, (Vector3?)null);
			val.SetPOIToNPC<Informant>();
			((Quest)this).AddEntry("Tell Diesel " + text + " is dealt with", (Vector3?)null);
		}

		protected override void OnLoaded()
		{
			((Saveable)this).OnLoaded();
			Job.Attach((Quest)(object)this);
		}
	}
	public class BusinessmenQuestSave : Saveable
	{
		[SaveableField("businessmen_quest")]
		public JobSaveData Data = new JobSaveData();

		public static BusinessmenQuestSave Instance { get; private set; }

		public BusinessmenQuestSave()
		{
			Instance = this;
		}

		protected override void OnLoaded()
		{
			Instance = this;
			Auditor.Refresh();
		}
	}
	public sealed class Auditor : NPC
	{
		private const string NpcId = "businessmen_auditor";

		private const string HomeName = "Upscale Apartments";

		private static Auditor _instance;

		private static int _lookAppliedForRun = -1;

		private static readonly string[] FirstNames = new string[10] { "Gordon", "Nigel", "Howard", "Clive", "Warren", "Preston", "Desmond", "Edwin", "Leonard", "Franklin" };

		private static readonly string[] LastNames = new string[10] { "Whitlock", "Ashcroft", "Pembroke", "Thorne", "Hargrove", "Fairbanks", "Lockhart", "Sterling", "Winslow", "Crane" };

		private static readonly Color32[] HairColors = (Color32[])(object)new Color32[5]
		{
			new Color32((byte)30, (byte)22, (byte)18, byte.MaxValue),
			new Color32((byte)60, (byte)40, (byte)28, byte.MaxValue),
			new Color32((byte)95, (byte)65, (byte)40, byte.MaxValue),
			new Color32((byte)120, (byte)120, (byte)120, byte.MaxValue),
			new Color32((byte)190, (byte)190, (byte)190, byte.MaxValue)
		};

		private static readonly string[] HairStyles = new string[6] { "Avatar/Hair/closebuzzcut/CloseBuzzCut", "Avatar/Hair/peaked/Peaked", "Avatar/Hair/receding/Receding", "Avatar/Hair/balding/Balding", "Avatar/Hair/midfringe/MidFringe", "Avatar/Hair/sidepartbob/SidePartBob" };

		private static readonly Color32[] ShirtColors = (Color32[])(object)new Color32[3]
		{
			new Color32((byte)235, (byte)235, (byte)235, byte.MaxValue),
			new Color32((byte)180, (byte)200, (byte)225, byte.MaxValue),
			new Color32((byte)210, (byte)210, (byte)200, byte.MaxValue)
		};

		private static readonly Color32[] SuitColors = (Color32[])(object)new Color32[4]
		{
			new Color32((byte)25, (byte)28, (byte)40, byte.MaxValue),
			new Color32((byte)45, (byte)45, (byte)50, byte.MaxValue),
			new Color32((byte)20, (byte)20, (byte)20, byte.MaxValue),
			new Color32((byte)55, (byte)45, (byte)38, byte.MaxValue)
		};

		public override bool IsPhysical => true;

		public static string PickName()
		{
			return QuestNpcUtil.RandomName(FirstNames, LastNames);
		}

		protected override void ConfigurePrefab(NPCPrefabBuilder builder)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			builder.WithIdentity("businessmen_auditor", "Auditor", string.Empty).WithSpawnPosition(QuestNpcUtil.ParkedPosition).WithSchedule((Action<PrefabScheduleBuilder>)delegate(PrefabScheduleBuilder plan)
			{
				Building val = Building.Get<UpscaleApartments>();
				Building val2 = Building.Get<TallTower>();
				Building val3 = Building.Get<Cafe>();
				Building val4 = Building.Get<ChineseRestaurant>();
				plan.StayInBuilding(val, 0, 450, (int?)null, (string)null);
				plan.StayInBuilding(val2, 800, 240, (int?)null, (string)null);
				plan.StayInBuilding(val3, 1215, 60, (int?)null, (string)null);
				plan.StayInBuilding(val2, 1330, 210, (int?)null, (string)null);
				plan.StayInBuilding(val4, 1800, 120, (int?)null, (string)null);
				plan.StayInBuilding(val, 2100, 179, (int?)null, (string)null);
			});
		}

		protected override void OnCreated()
		{
			((NPC)this).OnCreated();
			((NPC)this).Appearance.Build();
			_instance = this;
			Melon<Core>.Logger.Msg("[quest] the auditor NPC was created");
			((NPC)this).OnDeath += AuditQuest.Job.OnObjectiveDone;
			Refresh();
		}

		public static void Refresh()
		{
			if (_instance == null)
			{
				return;
			}
			JobSaveData jobSaveData = BusinessmenQuestSave.Instance?.Data;
			bool flag = jobSaveData != null && jobSaveData.Accepted && !jobSaveData.TargetKilled;
			try
			{
				if (flag)
				{
					_instance.Deploy(jobSaveData);
				}
				else
				{
					QuestNpcUtil.Park((NPC)(object)_instance, "businessmen_auditor");
				}
			}
			catch (Exception arg)
			{
				Melon<Core>.Logger.Error(string.Format("[quest] auditor {0} failed: {1}", flag ? "deploy" : "park", arg));
			}
		}

		private void Deploy(JobSaveData save)
		{
			QuestNpcUtil.ApplyName((NPC)(object)this, save.TargetName);
			if (_lookAppliedForRun != save.Runs)
			{
				_lookAppliedForRun = save.Runs;
				ApplyBusinessLook();
			}
			QuestNpcUtil.Revive((NPC)(object)this);
			QuestNpcUtil.EnterBuilding("businessmen_auditor", "Upscale Apartments");
			((NPC)this).Schedule.Enable();
			((NPC)this).Schedule.EnforceState();
		}

		private void ApplyBusinessLook()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			AppearanceUtil.ClearLayers(((NPC)this).Appearance);
			Color val = Color32.op_Implicit(QuestNpcUtil.Pick(HairColors));
			((NPC)this).Appearance.Set<Gender>((object)0f);
			((NPC)this).Appearance.Set<Height>((object)Random.Range(0.95f, 1.05f));
			((NPC)this).Appearance.Set<Weight>((object)Random.Range(0.35f, 0.65f));
			((NPC)this).Appearance.Set<SkinColor>((object)Color32.op_Implicit(QuestNpcUtil.Pick(QuestNpcUtil.SkinTones)));
			((NPC)this).Appearance.Set<EyeBallTint>((object)Color.white);
			((NPC)this).Appearance.Set<HairColor>((object)val);
			((NPC)this).Appearance.Set<HairStyle>((object)QuestNpcUtil.Pick(HairStyles));
			((NPC)this).Appearance.WithFaceLayer<Face>((Random.value < 0.6f) ? "Avatar/Layers/Face/Face_Neutral" : "Avatar/Layers/Face/Face_SlightFrown", new Color(0.15f, 0.1f, 0.1f));
			if (Random.value < 0.25f)
			{
				((NPC)this).Appearance.WithFaceLayer<FacialHair>("Avatar/Layers/Face/FacialHair_Stubble", val);
			}
			Color val2 = Color32.op_Implicit(QuestNpcUtil.Pick(SuitColors));
			((NPC)this).Appearance.WithBodyLayer<Shirts>((Random.value < 0.8f) ? "Avatar/Layers/Top/Buttonup" : "Avatar/Layers/Top/RolledButtonUp", Color32.op_Implicit(QuestNpcUtil.Pick(ShirtColors)));
			((NPC)this).Appearance.WithBodyLayer<Pants>("Avatar/Layers/Bottom/Jeans", val2);
			AppearanceUtil.Wear<Chest>(((NPC)this).Appearance, val2, new string[1] { "Avatar/Accessories/Chest/Blazer/Blazer" });
			AppearanceUtil.Wear<Feet>(((NPC)this).Appearance, new Color(0.08f, 0.06f, 0.05f), new string[1] { "Avatar/Accessories/Feet/DressShoes/DressShoes" });
			string[] items = new string[4] { "Avatar/Accessories/Neck/StripedTie/StripedTie", "Avatar/Accessories/Neck/CheckedTie/CheckedTie", "Avatar/Accessories/Neck/DottedTie/DottedTie", "Avatar/Accessories/Neck/CrossTie/CrossTie" };
			AppearanceUtil.Wear<Neck>(((NPC)this).Appearance, new Color(0.35f, 0.08f, 0.1f), new string[1] { QuestNpcUtil.Pick(items) });
			float value = Random.value;
			if (value < 0.35f)
			{
				AppearanceUtil.Wear<Head>(((NPC)this).Appearance, Color.white, new string[1] { "Avatar/Accessories/Head/RectangleFrameGlasses/RectangleFrameGlasses" });
			}
			else if (value < 0.5f)
			{
				AppearanceUtil.Wear<Head>(((NPC)this).Appearance, Color.white, new string[1] { "Avatar/Accessories/Head/SmallRoundGlasses/SmallRoundGlasses" });
			}
			if (Random.value < 0.5f)
			{
				AppearanceUtil.Wear<Hands>(((NPC)this).Appearance, Color.white, new string[1] { "Avatar/Accessories/Hands/Polex/Polex" });
			}
			AppearanceUtil.ApplyNow(((NPC)this).Appearance);
		}
	}
	public class AuditQuest : Quest
	{
		internal static readonly JobController Job = new JobController(new JobDefinition
		{
			QuestIdPrefix = "businessmen_audit_quest",
			RewardLogLabel = "Johnny's audit job",
			GetSave = () => BusinessmenQuestSave.Instance?.Data,
			PickName = Auditor.PickName,
			RefreshTarget = Auditor.Refresh,
			CreateQuest = (string id) => QuestManager.CreateQuest<AuditQuest>(id)
		});

		protected override string Title => "The Audit";

		protected override string Description => "An auditor is getting too close to the books. Johnny Smith wants him dealt with.";

		protected override bool AutoBegin => true;

		protected override void OnCreated()
		{
			((Registerable)this).OnCreated();
			Job.Attach((Quest)(object)this);
			string text = Job.TargetNameOr("the auditor");
			QuestEntry val = ((Quest)this).AddEntry("Find and kill " + text + ". He keeps a strict daily routine", (Vector3?)null);
			val.SetPOIToNPC<Auditor>();
			((Quest)this).AddEntry("Tell Johnny Smith " + text + " is dealt with", (Vector3?)null);
		}

		protected override void OnLoaded()
		{
			((Saveable)this).OnLoaded();
			Job.Attach((Quest)(object)this);
		}
	}
	public class DropRun
	{
		public string FromGuid = string.Empty;

		public string ToGuid = string.Empty;

		public string FromName = string.Empty;

		public string ToName = string.Empty;

		public bool Placed;

		public bool PickedUp;
	}
	public class HippiesQuestSave : Saveable
	{
		[SaveableField("hippies_quest")]
		public JobSaveData Data = new JobSaveData();

		[SaveableField("hippies_quest_drops")]
		public DropRun Drops = new DropRun();

		public static HippiesQuestSave Instance { get; private set; }

		public HippiesQuestSave()
		{
			Instance = this;
		}

		protected override void OnLoaded()
		{
			Instance = this;
		}
	}
	internal static class HippiesTask
	{
		private const string PackageItemId = "paisleybandana";

		private static readonly KeyValuePair<string, int>[] Supplies = new KeyValuePair<string, int>[4]
		{
			new KeyValuePair<string, int>("extralonglifesoil", 3),
			new KeyValuePair<string, int>("speedgrow", 3),
			new KeyValuePair<string, int>("pgr", 1),
			new KeyValuePair<string, int>("fertilizer", 1)
		};

		private const float MinDropDistance = 80f;

		private static HippiesQuestSave Save => HippiesQuestSave.Instance;

		private static DeadDrop FindDrop(string guid)
		{
			if (string.IsNullOrEmpty(guid))
			{
				return null;
			}
			foreach (DeadDrop deadDrop in DeadDrop.DeadDrops)
			{
				if ((Object)(object)deadDrop != (Object)null && string.Equals(deadDrop.GUID.ToString(), guid, StringComparison.OrdinalIgnoreCase))
				{
					return deadDrop;
				}
			}
			return null;
		}

		public static Vector3? PositionOf(string guid)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			DeadDrop val = FindDrop(guid);
			return ((Object)(object)val != (Object)null) ? new Vector3?(((Component)val).transform.position) : ((Vector3?)null);
		}

		private static int Count(DeadDrop drop, string itemId)
		{
			int num = 0;
			foreach (ItemSlot itemSlot in ((StorageEntity)drop.Storage).ItemSlots)
			{
				ItemInstance itemInstance = itemSlot.ItemInstance;
				if (itemInstance != null && ((BaseItemDefinition)itemInstance.Definition).ID == itemId)
				{
					num += ((BaseItemInstance)itemInstance).Quantity;
				}
			}
			return num;
		}

		private static void RemoveAll(DeadDrop drop, string itemId)
		{
			foreach (ItemSlot itemSlot in ((StorageEntity)drop.Storage).ItemSlots)
			{
				ItemInstance itemInstance = itemSlot.ItemInstance;
				if (itemInstance != null && ((BaseItemDefinition)itemInstance.Definition).ID == itemId)
				{
					itemSlot.ClearStoredInstance(false);
				}
			}
		}

		public static void Refresh()
		{
			HippiesQuestSave save = Save;
			if (save == null)
			{
				return;
			}
			if (save.Data.Accepted && !save.Data.TargetKilled)
			{
				if (!save.Drops.Placed)
				{
					ChooseAndPlace(save.Drops);
				}
			}
			else
			{
				Cleanup(save);
			}
		}

		private static void ChooseAndPlace(DropRun run)
		{
			List<DeadDrop> list = new List<DeadDrop>();
			foreach (DeadDrop deadDrop in DeadDrop.DeadDrops)
			{
				if ((Object)(object)deadDrop != (Object)null && ((StorageEntity)deadDrop.Storage).ItemCount == 0)
				{
					list.Add(deadDrop);
				}
			}
			if (list.Count < 2)
			{
				Melon<Core>.Logger.Warning("[quest] not enough empty dead drops for the hippies' job");
				return;
			}
			DeadDrop from = list[Random.Range(0, list.Count)];
			List<DeadDrop> list2 = list.FindAll(delegate(DeadDrop d)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val3 = ((Component)d).transform.position - ((Component)from).transform.position;
				return ((Vector3)(ref val3)).magnitude >= 80f;
			});
			DeadDrop val = (DeadDrop)((list2.Count > 0) ? ((object)list2[Random.Range(0, list2.Count)]) : ((object)list.Find((DeadDrop d) => (Object)(object)d != (Object)(object)from)));
			ItemInstance val2 = RewardUtil.CreateItem("paisleybandana", 1);
			if (val2 != null)
			{
				((StorageEntity)from.Storage).InsertItem(val2, true);
				run.FromGuid = from.GUID.ToString();
				run.ToGuid = val.GUID.ToString();
				run.FromName = from.DeadDropName;
				run.ToName = val.DeadDropName;
				run.Placed = true;
				run.PickedUp = false;
				Melon<Core>.Logger.Msg("[quest] package placed at '" + run.FromName + "', to be delivered to '" + run.ToName + "'");
			}
		}

		private static void Cleanup(HippiesQuestSave save)
		{
			DropRun drops = save.Drops;
			if (drops.Placed)
			{
				DeadDrop val = FindDrop(drops.FromGuid);
				DeadDrop val2 = FindDrop(drops.ToGuid);
				if ((Object)(object)val != (Object)null)
				{
					RemoveAll(val, "paisleybandana");
				}
				if ((Object)(object)val2 != (Object)null)
				{
					RemoveAll(val2, "paisleybandana");
				}
				save.Drops = new DropRun();
			}
		}

		public static void Tick()
		{
			try
			{
				HippiesQuestSave save = Save;
				if (save == null || !save.Data.Accepted || save.Data.TargetKilled || save.Data.Completed)
				{
					return;
				}
				DropRun drops = save.Drops;
				if (!drops.Placed)
				{
					return;
				}
				JobController job = HippiesQuest.Job;
				if (!drops.PickedUp)
				{
					DeadDrop val = FindDrop(drops.FromGuid);
					if ((Object)(object)val == (Object)null || Count(val, "paisleybandana") > 0)
					{
						return;
					}
					drops.PickedUp = true;
					List<QuestEntry> list = job.Quest?.QuestEntries;
					if (list != null && list.Count > 1)
					{
						list[0].Complete();
						list[1].Begin();
					}
					Melon<Core>.Logger.Msg("[quest] package picked up");
				}
				DeadDrop val2 = FindDrop(drops.ToGuid);
				if ((Object)(object)val2 != (Object)null && Count(val2, "paisleybandana") > 0)
				{
					job.OnObjectiveDone();
				}
			}
			catch (Exception ex)
			{
				Melon<Core>.Logger.Warning("[quest] hippies tick failed: " + ex.Message);
			}
		}

		public static void GiveSupplies()
		{
			RewardUtil.GiveItems(Supplies);
		}
	}
	public class HippiesQuest : Quest
	{
		internal static readonly JobController Job = new JobController(new JobDefinition
		{
			QuestIdPrefix = "hippies_package_quest",
			RewardLogLabel = "Krystal's package job",
			CashReward = 0f,
			GetSave = () => HippiesQuestSave.Instance?.Data,
			PickName = () => string.Empty,
			RefreshTarget = HippiesTask.Refresh,
			GrantExtraReward = HippiesTask.GiveSupplies,
			CreateQuest = (string id) => QuestManager.CreateQuest<HippiesQuest>(id)
		});

		protected override string Title => "Special Delivery";

		protected override string Description => "Krystal Bird needs a package carried between two dead drops.";

		protected override bool AutoBegin => true;

		protected override void OnCreated()
		{
			((Registerable)this).OnCreated();
			Job.Attach((Quest)(object)this);
			DropRun dropRun = HippiesQuestSave.Instance?.Drops ?? new DropRun();
			((Quest)this).AddEntry("Pick up the package from the dead drop at " + dropRun.FromName, HippiesTask.PositionOf(dropRun.FromGuid));
			((Quest)this).AddEntry("Deliver it to the dead drop at " + dropRun.ToName, HippiesTask.PositionOf(dropRun.ToGuid));
			((Quest)this).AddEntry("Tell Krystal Bird the package has been delivered", (Vector3?)null);
		}

		protected override void OnLoaded()
		{
			((Saveable)this).OnLoaded();
			Job.Attach((Quest)(object)this);
		}
	}
	public class PartyRun
	{
		public List<string> Asked = new List<string>();

		public int Accepted;
	}
	public class PartyBusQuestSave : Saveable
	{
		[SaveableField("partybus_quest")]
		public JobSaveData Data = new JobSaveData();

		[SaveableField("partybus_quest_run")]
		public PartyRun Run = new PartyRun();

		public static PartyBusQuestSave Instance { get; private set; }

		public PartyBusQuestSave()
		{
			Instance = this;
		}

		protected override void OnLoaded()
		{
			Instance = this;
		}
	}
	internal static class PartyTask
	{
		public const int PeopleNeeded = 5;

		public const float RejectChance = 0.3f;

		private static readonly KeyValuePair<string, int>[] PartySupplies = new KeyValuePair<string, int>[1]
		{
			new KeyValuePair<string, int>("energydrink", 5)
		};

		private static PartyBusQuestSave Save => PartyBusQuestSave.Instance;

		public static void GiveSupplies()
		{
			RewardUtil.GiveItems(PartySupplies);
		}

		public static bool CanInvite(string npcId)
		{
			PartyBusQuestSave save = Save;
			if (save == null || string.IsNullOrEmpty(npcId))
			{
				return false;
			}
			JobSaveData data = save.Data;
			return data.Accepted && !data.TargetKilled && !data.Completed && !save.Run.Asked.Contains(npcId);
		}

		public static bool Invite(string npcId)
		{
			PartyBusQuestSave save = Save;
			if (save == null || !CanInvite(npcId))
			{
				return false;
			}
			save.Run.Asked.Add(npcId);
			bool flag = Random.value >= 0.3f;
			if (flag)
			{
				save.Run.Accepted++;
			}
			UpdateObjectiveTitle();
			Melon<Core>.Logger.Msg(string.Format("[quest] {0} {1} the invite ({2}/{3})", npcId, flag ? "accepted" : "turned down", save.Run.Accepted, 5));
			if (save.Run.Accepted >= 5)
			{
				PartyBusQuest.Job.OnObjectiveDone();
			}
			return flag;
		}

		public static string ObjectiveText(int accepted)
		{
			return $"Tell people the Party Bus is in town ({Math.Min(accepted, 5)}/{5})";
		}

		private static void UpdateObjectiveTitle()
		{
			List<QuestEntry> list = PartyBusQuest.Job.Quest?.QuestEntries;
			if (list != null && list.Count > 0)
			{
				list[0].Title = ObjectiveText(Save.Run.Accepted);
			}
		}

		public static void Refresh()
		{
			PartyBusQuestSave save = Save;
			if (save != null && (!save.Data.Accepted || save.Data.TargetKilled) && (save.Run.Asked.Count > 0 || save.Run.Accepted > 0))
			{
				save.Run = new PartyRun();
			}
		}
	}
	public class PartyBusQuest : Quest
	{
		internal static readonly JobController Job = new JobController(new JobDefinition
		{
			QuestIdPrefix = "partybus_word_quest",
			RewardLogLabel = "Chad's spread-the-word job",
			CashReward = 2500f,
			GrantExtraReward = PartyTask.GiveSupplies,
			GetSave = () => PartyBusQuestSave.Instance?.Data,
			PickName = () => string.Empty,
			RefreshTarget = PartyTask.Refresh,
			CreateQuest = (string id) => QuestManager.CreateQuest<PartyBusQuest>(id)
		});

		protected override string Title => "Spread the Word";

		protected override string Description => "Chad Chaddington wants the whole town to know the Party Bus is here.";

		protected override bool AutoBegin => true;

		protected override void OnCreated()
		{
			((Registerable)this).OnCreated();
			Job.Attach((Quest)(object)this);
			int accepted = PartyBusQuestSave.Instance?.Run.Accepted ?? 0;
			((Quest)this).AddEntry(PartyTask.ObjectiveText(accepted), (Vector3?)null);
			((Quest)this).AddEntry("Tell Chad Chaddington the word is out", (Vector3?)null);
		}

		protected override void OnLoaded()
		{
			((Saveable)this).OnLoaded();
			Job.Attach((Quest)(object)this);
		}
	}
	public class JobSaveData
	{
		public bool Accepted;

		public string GroupId = string.Empty;

		public bool TargetKilled;

		public bool Completed;

		public int OfferState;

		public string TargetName = string.Empty;

		public int Runs;
	}
	internal sealed class JobDefinition
	{
		public string QuestIdPrefix;

		public string RewardLogLabel;

		public float RelationshipReward = 0.5f;

		public float CashReward = 5000f;

		public int XpReward = 100;

		public Action GrantExtraReward;

		public Func<JobSaveData> GetSave;

		public Func<string> PickName;

		public Action RefreshTarget;

		public Func<string, Quest> CreateQuest;
	}
	internal sealed class JobLines
	{
		public Func<string, string> OfferLine;

		public string[] NoJobLines;

		public string AlreadyDoneLine;

		public string HandInLine;
	}
	internal sealed class JobController
	{
		public static readonly bool DebugMode = false;

		private static readonly List<JobController> All = new List<JobController>();

		private readonly JobDefinition _def;

		private Quest _quest;

		public static float OfferChance => DebugMode ? 1f : 0.5f;

		public JobSaveData Save => _def.GetSave();

		public Quest Quest => _quest;

		public JobController(JobDefinition def)
		{
			_def = def;
			All.Add(this);
		}

		public void Attach(Quest quest)
		{
			_quest = quest;
			quest.OnComplete -= HandleComplete;
			quest.OnComplete += HandleComplete;
		}

		public string TargetNameOr(string fallback)
		{
			string text = Save?.TargetName;
			return string.IsNullOrEmpty(text) ? fallback : text;
		}

		public bool CanAsk()
		{
			JobSaveData save = Save;
			return save == null || !save.Accepted || save.Completed;
		}

		public bool IsReadyToHandIn()
		{
			JobSaveData save = Save;
			return save != null && save.TargetKilled && !save.Completed;
		}

		public string Ask(string groupId, JobLines lines)
		{
			JobSaveData save = Save;
			if (save == null)
			{
				return null;
			}
			if (!string.IsNullOrEmpty(groupId))
			{
				save.GroupId = groupId;
			}
			if (save.Completed && !DebugMode)
			{
				return lines.AlreadyDoneLine;
			}
			if (save.Completed)
			{
				ResetRun();
			}
			if (save.OfferState == 0 || DebugMode)
			{
				save.OfferState = ((Random.value < OfferChance) ? 1 : 2);
			}
			if (save.OfferState == 1)
			{
				Start(groupId);
				return lines.OfferLine(save.TargetName);
			}
			return lines.NoJobLines[Random.Range(0, lines.NoJobLines.Length)];
		}

		public void Start(string groupId)
		{
			JobSaveData save = Save;
			if (save != null)
			{
				save.Accepted = true;
				save.GroupId = groupId ?? string.Empty;
				save.Runs++;
				save.TargetName = _def.PickName();
			}
			if (_quest == null)
			{
				_quest = _def.CreateQuest($"{_def.QuestIdPrefix}_{save?.Runs ?? 0}");
			}
			if (_quest == null)
			{
				Melon<Core>.Logger.Error("[quest] could not create the quest");
				return;
			}
			_def.RefreshTarget();
			Melon<Core>.Logger.Msg("[quest] created; S1API sets up its entries and begins it on the next frame");
		}

		public void OnObjectiveDone()
		{
			JobSaveData save = Save;
			if (_quest != null && save != null && save.Accepted && !save.TargetKilled)
			{
				save.TargetKilled = true;
				int num = _quest.QuestEntries.Count - 1;
				for (int i = 0; i < num; i++)
				{
					_quest.QuestEntries[i].Complete();
				}
				if (num > 0)
				{
					_quest.QuestEntries[num].Begin();
				}
				Melon<Core>.Logger.Msg("[quest] task done, report back to the leader");
			}
		}

		public void HandIn()
		{
			JobSaveData save = Save;
			if (_quest != null && save != null && save.TargetKilled && !save.Completed)
			{
				save.Completed = true;
				int num = _quest.QuestEntries.Count - 1;
				if (num > 0)
				{
					_quest.QuestEntries[num].Complete();
				}
				_quest.Complete();
				_def.RefreshTarget();
			}
		}

		private void HandleComplete()
		{
			if (_def.CashReward > 0f)
			{
				Money.ChangeCashBalance(_def.CashReward, true, true);
			}
			LevelManager.AddXP(_def.XpReward);
			_def.GrantExtraReward?.Invoke();
			Melon<Core>.Logger.Msg($"[quest] paid ${_def.CashReward}, {_def.XpReward} XP");
			string text = Save?.GroupId;
			if (!string.IsNullOrEmpty(text))
			{
				GroupRelationships.Reward(text, _def.RelationshipReward, "completed " + _def.RewardLogLabel);
			}
			else
			{
				Melon<Core>.Logger.Warning("[quest] no saved group id, relationship reward skipped");
			}
		}

		public void ResetRun()
		{
			JobSaveData save = Save;
			if (save != null)
			{
				if (_quest != null && save.Accepted && !save.Completed)
				{
					_quest.Cancel();
				}
				_quest = null;
				save.OfferState = 0;
				save.TargetName = string.Empty;
				save.Accepted = false;
				save.TargetKilled = false;
				save.Completed = false;
				_def.RefreshTarget();
			}
		}

		public static void OnGroupLeft(string groupId)
		{
			foreach (JobController item in All)
			{
				JobSaveData save = item.Save;
				if (save != null && string.Equals(save.GroupId, groupId, StringComparison.OrdinalIgnoreCase))
				{
					item.ResetRun();
					Melon<Core>.Logger.Msg("[quest] " + groupId + " left, their job can be taken again next visit");
				}
			}
		}
	}
	internal static class RewardUtil
	{
		public static ItemInstance CreateItem(string itemId, int quantity)
		{
			ItemDefinition item = Registry.GetItem(itemId);
			if ((Object)(object)item == (Object)null)
			{
				Melon<Core>.Logger.Warning("[quest] item '" + itemId + "' does not exist");
				return null;
			}
			return item.GetDefaultInstance(quantity);
		}

		public static void GiveItems(params KeyValuePair<string, int>[] items)
		{
			PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				Melon<Core>.Logger.Warning("[quest] no inventory to give the reward to");
				return;
			}
			for (int i = 0; i < items.Length; i++)
			{
				KeyValuePair<string, int> keyValuePair = items[i];
				ItemInstance val = CreateItem(keyValuePair.Key, keyValuePair.Value);
				if (val != null)
				{
					instance.AddItemToInventory(val);
				}
			}
		}
	}
	internal static class QuestNpcUtil
	{
		public static readonly Vector3 ParkedPosition = new Vector3(0f, -200f, 0f);

		public static readonly Color32[] SkinTones = (Color32[])(object)new Color32[5]
		{
			new Color32((byte)236, (byte)200, (byte)170, byte.MaxValue),
			new Color32((byte)224, (byte)172, (byte)138, byte.MaxValue),
			new Color32((byte)198, (byte)140, (byte)100, byte.MaxValue),
			new Color32((byte)168, (byte)112, (byte)78, byte.MaxValue),
			new Color32((byte)130, (byte)84, (byte)58, byte.MaxValue)
		};

		public static T Pick<T>(T[] items)
		{
			return items[Random.Range(0, items.Length)];
		}

		public static string RandomName(string[] firstNames, string[] lastNames)
		{
			return Pick(firstNames) + " " + Pick(lastNames);
		}

		public static void ApplyName(NPC npc, string fullName)
		{
			if (!string.IsNullOrEmpty(fullName))
			{
				int num = fullName.IndexOf(' ');
				npc.FirstName = ((num > 0) ? fullName.Substring(0, num) : fullName);
				npc.LastName = ((num > 0) ? fullName.Substring(num + 1) : string.Empty);
			}
		}

		private static NPC FindGameNpc(string npcId)
		{
			NPC[] array = Object.FindObjectsOfType<NPC>(true);
			NPC[] array2 = array;
			foreach (NPC val in array2)
			{
				if ((Object)(object)val != (Object)null && val.ID == npcId)
				{
					return val;
				}
			}
			Melon<Core>.Logger.Warning($"[quest] NPC '{npcId}' is not among the {array.Length} NPCs in the scene");
			return null;
		}

		private static NPCEnterableBuilding FindBuilding(string name)
		{
			NPCEnterableBuilding[] array = Object.FindObjectsOfType<NPCEnterableBuilding>(true);
			foreach (NPCEnterableBuilding val in array)
			{
				if ((Object)(object)val != (Object)null && string.Equals(val.BuildingName, name, StringComparison.OrdinalIgnoreCase))
				{
					return val;
				}
			}
			return null;
		}

		public static void EnterBuilding(string npcId, string buildingName)
		{
			NPC val = FindGameNpc(npcId);
			NPCEnterableBuilding val2 = FindBuilding(buildingName);
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || val2.Doors.Length == 0)
			{
				Melon<Core>.Logger.Warning($"[quest] could not hide '{npcId}' in '{buildingName}' (npc found: {(Object)(object)val != (Object)null}, building found: {(Object)(object)val2 != (Object)null}, doors: {val2?.Doors.Length})");
			}
			else if (!((Object)(object)val.CurrentBuilding == (Object)(object)val2))
			{
				if (val.isInBuilding)
				{
					val.ExitBuilding("");
				}
				val.EnterBuilding((NetworkConnection)null, val2.GUID.ToString(), 0);
				Melon<Core>.Logger.Msg("[quest] '" + npcId + "' is now hidden in '" + buildingName + "'");
			}
		}

		public static void LeaveBuilding(string npcId)
		{
			NPC val = FindGameNpc(npcId);
			if ((Object)(object)val != (Object)null && val.isInBuilding)
			{
				val.ExitBuilding("");
			}
		}

		public static void Park(NPC npc, string npcId)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			npc.Schedule.Disable();
			LeaveBuilding(npcId);
			npc.IsInvincible = true;
			Vector3 val = npc.Position - ParkedPosition;
			if (((Vector3)(ref val)).sqrMagnitude > 1f)
			{
				npc.Position = ParkedPosition;
			}
		}

		public static void Revive(NPC npc)
		{
			if (npc.IsDead)
			{
				npc.Revive();
			}
			npc.Heal((int)npc.MaxHealth);
			npc.IsInvincible = false;
		}
	}
	internal static class AppearanceUtil
	{
		private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		public static void ClearLayers(NPCAppearance appearance)
		{
			try
			{
				object obj = typeof(NPCAppearance).GetField("_customAvatarSettings", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(appearance);
				if (obj != null)
				{
					string[] array = new string[3] { "FaceLayerSettings", "BodyLayerSettings", "AccessorySettings" };
					foreach (string name in array)
					{
						object obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) ?? obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
						obj2?.GetType().GetMethod("Clear", Type.EmptyTypes)?.Invoke(obj2, null);
					}
				}
			}
			catch (Exception ex)
			{
				Melon<Core>.Logger.Warning("[quest] clearing old layers failed: " + ex.Message);
			}
		}

		public static void ApplyNow(NPCAppearance appearance)
		{
			try
			{
				Type typeFromHandle = typeof(NPCAppearance);
				object obj = typeFromHandle.GetField("_runtimeAvatar", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(appearance);
				MethodInfo method = typeFromHandle.GetMethod("ApplyToAvatar", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (obj == null || method == null)
				{
					Melon<Core>.Logger.Warning($"[quest] could not apply the look (avatar found: {obj != null}, method found: {method != null})");
					return;
				}
				method.Invoke(appearance, new object[1] { obj });
			}
			catch (Exception ex)
			{
				Melon<Core>.Logger.Warning("[quest] applying the look failed: " + ex.Message);
			}
		}

		public static void Wear<T>(NPCAppearance appearance, Color color, params string[] candidatePaths) where T : BaseAccessoryAppearance
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			foreach (string text in candidatePaths)
			{
				bool flag = false;
				try
				{
					flag = Resources.Load(text) != (Object)null;
				}
				catch
				{
				}
				if (flag)
				{
					appearance.WithAccessoryLayer<T>(text, color);
					break;
				}
				Melon<Core>.Logger.Warning("[quest] outfit path not found: " + text);
			}
		}
	}
}
namespace BetterSpecialCustomers.Patches
{
	[HarmonyPatch(typeof(SpecialCustomerLeader), "SetCustomerData")]
	internal class BikersQuestChoicePatch
	{
		private static readonly JobDialogue Dialogue = new JobDialogue
		{
			LeaderId = "diesel_rodd",
			AskText = "You got any jobs for me?",
			HandInText = "He's dealt with.",
			Job = BikersQuest.Job,
			Lines = new JobLines
			{
				OfferLine = (string name) => "Somebody's been feedin' the cops about club business. Name's " + name + ". He's holed up at The Piss Hut. Knock on the door, and make sure he never talks again.",
				NoJobLines = new string[3] { "Not this time, prospect. Road's quiet. Come find me when it ain't.", "Nothin' right now. The club'll let you know when we need a hand.", "Ain't got nothin' for you today. Go count your money." },
				AlreadyDoneLine = "You already did right by us this run. Come back when we roll through again.",
				HandInLine = "Good. Rats don't get to walk away. The club won't forget this."
			}
		};

		public static void Postfix(SpecialCustomerLeader __instance)
		{
			Dialogue.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerLeader), "SetCustomerData")]
	internal class BusinessmenQuestChoicePatch
	{
		private static readonly JobDialogue Dialogue = new JobDialogue
		{
			LeaderId = "johnny_smith",
			AskText = "Anything I can help with?",
			HandInText = "The audit won't be a problem anymore.",
			AskSeconds = 8f,
			Job = AuditQuest.Job,
			Lines = new JobLines
			{
				OfferLine = (string name) => "As it happens, yes. An auditor, " + name + ", has been looking at the books for the wrong reasons. He keeps to a strict routine: home, the office, lunch. Make sure he doesn't finish his report.",
				NoJobLines = new string[3] { "Nothing at the moment. Business is... stable. Enjoy it.", "Not today. If something comes up, you'll be the first call.", "We're fully staffed on problems right now. Check back later." },
				AlreadyDoneLine = "You've done enough for us this time. We'll be in touch.",
				HandInLine = "Excellent. A tidy solution. Our accountants will be very pleased."
			}
		};

		public static void Postfix(SpecialCustomerLeader __instance)
		{
			Dialogue.Register(__instance);
		}
	}
	internal static class BuyLimitFieldSync
	{
		public static void Apply(SpecialCustomerManager manager, string groupId)
		{
		}
	}
	internal static class DialogueChoiceUtil
	{
		public static void Add(DialogueController controller, string text, Func<bool> shouldShow, Action onChosen)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			DialogueChoice val = new DialogueChoice();
			val.ChoiceText = text;
			val.Enabled = true;
			val.shouldShowCheck = (ShouldShowCheck)((bool enabled) => enabled && shouldShow());
			val.onChoosen.AddListener((UnityAction)delegate
			{
				onChosen();
			});
			controller.AddDialogueChoice(val, 0);
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerLeader), "SetCustomerData")]
	internal class HippiesQuestChoicePatch
	{
		private static readonly JobDialogue Dialogue = new JobDialogue
		{
			LeaderId = "krystal_bird",
			AskText = "Can I do anything for the group?",
			HandInText = "The package has been delivered.",
			AskSeconds = 8f,
			Job = HippiesQuest.Job,
			Lines = new JobLines
			{
				OfferLine = (string _) => "Oh, bless you. There's a little something that needs to get to a friend, and we can't be seen with it. I've left it at a drop. Take it to the other one, and be gentle with it.",
				NoJobLines = new string[3] { "The universe has been quiet today, friend. Come back when it speaks.", "Not right now, sweetheart. Go enjoy the sunshine.", "Nothing this time. Everything is as it should be." },
				AlreadyDoneLine = "You've done so much already. We're grateful. Rest a while.",
				HandInLine = "Wonderful. The garden will be so happy. Please, take these, with our love."
			}
		};

		public static void Postfix(SpecialCustomerLeader __instance)
		{
			Dialogue.Register(__instance);
		}
	}
	internal sealed class JobDialogue
	{
		public string LeaderId;

		public string AskText;

		public string HandInText;

		public JobController Job;

		public JobLines Lines;

		public float AskSeconds = 7f;

		public float HandInSeconds = 6f;

		private SpecialCustomerLeader _leader;

		private readonly HashSet<int> _added = new HashSet<int>();

		public void Register(SpecialCustomerLeader leader)
		{
			_leader = leader;
			DialogueController component = ((Component)((NPC)leader).DialogueHandler).GetComponent<DialogueController>();
			if (!((Object)(object)component == (Object)null) && _added.Add(((Object)component).GetInstanceID()))
			{
				DialogueChoiceUtil.Add(component, AskText, () => ((NPC)leader).ID == LeaderId && Job.CanAsk(), OnAsk);
				DialogueChoiceUtil.Add(component, HandInText, () => ((NPC)leader).ID == LeaderId && Job.IsReadyToHandIn(), OnHandIn);
				MelonLogger.Msg("[quest] added the quest choices for leader '" + LeaderId + "'");
			}
		}

		private void OnAsk()
		{
			if (!((Object)(object)_leader == (Object)null))
			{
				SpecialCustomerData currentGroupData = NetworkSingleton<SpecialCustomerManager>.Instance.CurrentGroupData;
				string text = Job.Ask((currentGroupData != null) ? currentGroupData.GroupId : null, Lines);
				if (text != null)
				{
					((NPC)_leader).DialogueHandler.ShowWorldspaceDialogue(text, AskSeconds);
				}
			}
		}

		private void OnHandIn()
		{
			if ((Object)(object)_leader != (Object)null)
			{
				((NPC)_leader).DialogueHandler.ShowWorldspaceDialogue(Lines.HandInLine, HandInSeconds);
			}
			Job.HandIn();
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerLeader), "SetCustomerData")]
	internal class PartyBusQuestChoicePatch
	{
		private static readonly JobDialogue Dialogue = new JobDialogue
		{
			LeaderId = "chad_chaddington",
			AskText = "Need a hand with anything?",
			HandInText = "The word is out.",
			AskSeconds = 8f,
			Job = PartyBusQuest.Job,
			Lines = new JobLines
			{
				OfferLine = (string _) => "Dude, yes! We need people to know the bus is in town and tonight is ON. " + $"Go tell {5} people. Some will say no. Their loss. Keep going!",
				NoJobLines = new string[3] { "We're good for now, bro. The vibe is already unreal.", "Nothing right now! Just enjoy the party.", "Not today, my guy. We got it handled." },
				AlreadyDoneLine = "You already got the word out, legend! Go have some fun.",
				HandInLine = "LEGEND! The crowd is gonna be insane. Here, you earned it!"
			}
		};

		public static void Postfix(SpecialCustomerLeader __instance)
		{
			Dialogue.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(DialogueController), "Start")]
	internal class PartyInvitePatch
	{
		private const string InviteText = "The Party Bus is in town tonight. You should come!";

		private static readonly string[] YesLines = new string[4] { "A party bus? Count me in!", "Sounds like a good time. I'll be there.", "Finally, something fun around here.", "Oh, nice. I'll swing by." };

		private static readonly string[] NoLines = new string[4] { "Not really my scene, sorry.", "I've got stuff to do tonight.", "No thanks, I'm good.", "Parties aren't for me." };

		public static void Postfix(DialogueController __instance)
		{
			NPC npc = ((Component)__instance).GetComponentInParent<NPC>();
			if (!((Object)(object)npc == (Object)null) && IsInvitable(npc))
			{
				DialogueChoiceUtil.Add(__instance, "The Party Bus is in town tonight. You should come!", () => PartyTask.CanInvite(npc.ID), delegate
				{
					OnInvite(npc);
				});
			}
		}

		private static bool IsInvitable(NPC npc)
		{
			if ((Object)(object)((Component)npc).GetComponent<SpecialCustomerLeader>() != (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)npc).GetComponent<SpecialCustomer>() != (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)npc).GetComponent<PoliceOfficer>() != (Object)null)
			{
				return false;
			}
			return npc.ID != "bikers_informant" && npc.ID != "businessmen_auditor";
		}

		private static void OnInvite(NPC npc)
		{
			string[] array = (PartyTask.Invite(npc.ID) ? YesLines : NoLines);
			npc.DialogueHandler.ShowWorldspaceDialogue(array[Random.Range(0, array.Length)], 5f);
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerData), "GetBuyQuantityForPlayerRank")]
	public class MaxBuyPatch
	{
		public const int NewMaxBuyQuantity = 200;

		private const bool DebugLogging = false;

		public static bool Prefix(SpecialCustomerData __instance, ERank rank, ref int __result)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected I4, but got Unknown
			PatchDiagnostics.Hit("MaxBuyPatch (rank-based buy limit)");
			int baseBuyQuantity = __instance.BaseBuyQuantity;
			int num = 10;
			int num2 = Math.Max(0, Math.Min((int)rank, num));
			__result = baseBuyQuantity + (200 - baseBuyQuantity) * num2 / num;
			return false;
		}

		[HarmonyPriority(0)]
		public static void Postfix(SpecialCustomerData __instance, ERank rank, int __result)
		{
			bool flag = true;
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerData), "GetBuyQuantityForPlayerRank")]
	public class RelationshipBuyQuantityPatch
	{
		public static void Postfix(SpecialCustomerData __instance, ref int __result)
		{
			PatchDiagnostics.Hit("RelationshipBuyQuantityPatch (relationship multiplier)");
			float multiplier = GroupRelationships.GetMultiplier(__instance.GroupId);
			__result = Math.Max(1, (int)Math.Round((float)__result * multiplier));
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerManager), "DealCompleted_Client")]
	public class RelationshipGainPatch
	{
		public static void Postfix(SpecialCustomerManager __instance, SpecialCustomerDealReceipt dealRecipe)
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			PatchDiagnostics.Hit("RelationshipGainPatch (sale completed)");
			SpecialCustomerData currentGroupData = __instance.CurrentGroupData;
			SpecialCustomerLeader val = dealRecipe?.Leader;
			if ((Object)(object)currentGroupData == (Object)null || (Object)(object)val == (Object)null || dealRecipe.Items == null)
			{
				return;
			}
			int num = 0;
			foreach (ItemInstance item in dealRecipe.Items)
			{
				num += ((BaseItemInstance)item).GetTotalAmount();
			}
			int num2 = val.StartingBuyQuantity;
			if (num2 <= 0)
			{
				num2 = currentGroupData.GetBuyQuantityForPlayerRank(NetworkSingleton<LevelManager>.Instance.Rank);
			}
			if (num2 > 0)
			{
				GroupRelationships.RegisterSale(currentGroupData.GroupId, num, num2);
			}
		}
	}
	[HarmonyPatch(typeof(SpecialCustomer), "SetGroupMates")]
	internal class RelationshipLossPatch
	{
		private static readonly HashSet<int> Subscribed = new HashSet<int>();

		private static float _lastPunchTime = -100f;

		private static float _lastKillTime = -100f;

		private static float _lastAimTime = -100f;

		private static float _lastPickpocketTime = -100f;

		public static void Postfix(SpecialCustomer __instance)
		{
			PatchDiagnostics.Hit("RelationshipLossPatch (subscribing to NPC reactions)");
			if (Subscribed.Add(((Object)__instance).GetInstanceID()))
			{
				NPCResponses responses = ((NPC)__instance).Responses;
				responses.OnNonLethallyAttackedByPlayer = (Action<Player>)Delegate.Combine(responses.OnNonLethallyAttackedByPlayer, new Action<Player>(OnPunched));
				responses.OnRepeatedlyNonLethallyAttackedByPlayer = (Action<Player>)Delegate.Combine(responses.OnRepeatedlyNonLethallyAttackedByPlayer, new Action<Player>(OnPunched));
				responses.OnLethallyAttackedByPlayer = (Action<Player>)Delegate.Combine(responses.OnLethallyAttackedByPlayer, new Action<Player>(OnKilledSomeone));
				responses.OnAimedAtByPlayer = (Action<Player>)Delegate.Combine(responses.OnAimedAtByPlayer, new Action<Player>(OnAimedAt));
				responses.OnPickpocketFailed = (Action<Player>)Delegate.Combine(responses.OnPickpocketFailed, new Action<Player>(OnPickpocketFailed));
			}
		}

		private static void OnPunched(Player attacker)
		{
			if (InstanceFinder.IsServer && !(Time.time - _lastPunchTime < 20f))
			{
				SpecialCustomerData currentGroupData = NetworkSingleton<SpecialCustomerManager>.Instance.CurrentGroupData;
				if (!((Object)(object)currentGroupData == (Object)null))
				{
					_lastPunchTime = Time.time;
					GroupRelationships.Penalize(currentGroupData.GroupId, 0.25f, "punched");
				}
			}
		}

		private static void OnKilledSomeone(Player attacker)
		{
			if (InstanceFinder.IsServer && !(Time.time - _lastKillTime < 20f))
			{
				SpecialCustomerData currentGroupData = NetworkSingleton<SpecialCustomerManager>.Instance.CurrentGroupData;
				if (!((Object)(object)currentGroupData == (Object)null))
				{
					_lastKillTime = Time.time;
					GroupRelationships.Penalize(currentGroupData.GroupId, 1f, "customer killed");
				}
			}
		}

		private static void OnAimedAt(Player attacker)
		{
			if (InstanceFinder.IsServer && !(Time.time - _lastAimTime < 20f))
			{
				SpecialCustomerData currentGroupData = NetworkSingleton<SpecialCustomerManager>.Instance.CurrentGroupData;
				if (!((Object)(object)currentGroupData == (Object)null))
				{
					_lastAimTime = Time.time;
					GroupRelationships.Penalize(currentGroupData.GroupId, 0.25f, "aimed weapon");
				}
			}
		}

		private static void OnPickpocketFailed(Player thief)
		{
			if (InstanceFinder.IsServer && !(Time.time - _lastPickpocketTime < 20f))
			{
				SpecialCustomerData currentGroupData = NetworkSingleton<SpecialCustomerManager>.Instance.CurrentGroupData;
				if (!((Object)(object)currentGroupData == (Object)null))
				{
					_lastPickpocketTime = Time.time;
					GroupRelationships.Penalize(currentGroupData.GroupId, 0.25f, "pickpocket failed");
				}
			}
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerManager), "GetSaveString")]
	public class RelationshipSavePatch
	{
		public static void Postfix(ref string __result)
		{
			PatchDiagnostics.Hit("RelationshipSavePatch (writing save)");
			try
			{
				int num = __result.LastIndexOf('}');
				if (num >= 0)
				{
					__result = __result.Substring(0, num).TrimEnd() + ",\n    " + GroupRelationships.ToJsonProperty() + "\n}";
				}
			}
			catch (Exception ex)
			{
				Melon<Core>.Logger.Error("Failed to add relationships to the save string: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerLoader), "Load")]
	public class RelationshipLoadPatch
	{
		public static void Prefix(string mainPath)
		{
			PatchDiagnostics.Hit("RelationshipLoadPatch (reading save)");
			try
			{
				string path = mainPath + ".json";
				GroupRelationships.LoadFromJson(File.Exists(path) ? File.ReadAllText(path) : null);
			}
			catch (Exception ex)
			{
				GroupRelationships.Clear();
				Melon<Core>.Logger.Error("Failed to load relationships: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerManager), "RunArrivalPhase")]
	public class RelationshipArrivalPatch
	{
		private static readonly FieldInfo CurrentDataField = AccessTools.Field(typeof(SpecialCustomerManager), "_currentData");

		private static readonly PropertyInfo CurrentDataProperty = AccessTools.Property(typeof(SpecialCustomerManager), "_currentData");

		private static bool _reportedMissing;

		private static string GetArrivingGroupId(SpecialCustomerManager manager)
		{
			object obj = ((CurrentDataField != null) ? CurrentDataField.GetValue(manager) : CurrentDataProperty?.GetValue(manager));
			if (obj == null && CurrentDataField == null && CurrentDataProperty == null && !_reportedMissing)
			{
				_reportedMissing = true;
				Melon<Core>.Logger.Error("RelationshipArrivalPatch: can't find SpecialCustomerManager._currentData as a field or property; visits will NOT be tracked. See the [diag] Probe lines.");
			}
			return ((SpecialCustomerSaveData)(((obj is SpecialCustomerSaveData) ? obj : null)?)).GroupId;
		}

		public static void Prefix(SpecialCustomerManager __instance)
		{
			PatchDiagnostics.Hit("RelationshipArrivalPatch (group arriving)");
			string arrivingGroupId = GetArrivingGroupId(__instance);
			if (!string.IsNullOrEmpty(arrivingGroupId))
			{
				GroupRelationships.BeginVisit(arrivingGroupId);
				BuyLimitFieldSync.Apply(__instance, arrivingGroupId);
			}
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerManager), "RemoveCustomerGroup")]
	public class RelationshipDeparturePatch
	{
		public static void Prefix(SpecialCustomerManager __instance)
		{
			PatchDiagnostics.Hit("RelationshipDeparturePatch (group leaving)");
			SpecialCustomerData currentGroupData = __instance.CurrentGroupData;
			if (!((Object)(object)currentGroupData == (Object)null))
			{
				GroupRelationships.EndVisit(currentGroupData.GroupId);
				JobController.OnGroupLeft(currentGroupData.GroupId);
			}
		}
	}
}
namespace BetterSpecialCustomers.Diagnostics
{
	public static class PatchDiagnostics
	{
		public static readonly bool Enabled = false;

		private static readonly HashSet<string> Seen = new HashSet<string>();

		public static void Hit(string patchName)
		{
			if (Enabled && Seen.Add(patchName))
			{
				Melon<Core>.Logger.Msg("[diag] first call: " + patchName + " (the game DID call this patch)");
			}
		}

		public static void PatchAllClasses(Harmony harmony)
		{
			IEnumerable<Type> enumerable = from t in typeof(Core).Assembly.GetTypes()
				where t.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0
				orderby t.Name
				select t;
			int num = 0;
			int num2 = 0;
			foreach (Type item in enumerable)
			{
				try
				{
					harmony.CreateClassProcessor(item).Patch();
					num++;
					if (Enabled)
					{
						Melon<Core>.Logger.Msg("[diag] Patch OK:     " + item.Name);
					}
				}
				catch (Exception ex)
				{
					num2++;
					Exception ex2 = ex;
					while (ex2.InnerException != null)
					{
						ex2 = ex2.InnerException;
					}
					Melon<Core>.Logger.Error("[diag] Patch FAILED: " + item.Name + " -> " + ex2.GetType().Name + ": " + ex2.Message);
				}
			}
			Melon<Core>.Logger.Msg($"[diag] Patches applied: {num} ok, {num2} failed.");
		}

		public static void Probe()
		{
			if (Enabled)
			{
				Melon<Core>.Logger.Msg(string.Format("[diag] Build: {0}. Harmony {1}", "MONO", typeof(Harmony).Assembly.GetName().Version));
				ProbeMethod(typeof(SpecialCustomerData), "GetBuyQuantityForPlayerRank");
				ProbeMethod(typeof(SpecialCustomerManager), "DealCompleted_Client");
				ProbeMethod(typeof(SpecialCustomerManager), "GetSaveString");
				ProbeMethod(typeof(SpecialCustomerManager), "RunArrivalPhase");
				ProbeMethod(typeof(SpecialCustomerManager), "RemoveCustomerGroup");
				ProbeMethod(typeof(SpecialCustomerLoader), "Load");
				ProbeMembers(typeof(SpecialCustomerManager), "currentData");
				ProbeMembers(typeof(SpecialCustomerData), "baseBuyQuantity");
				ProbeMembers(typeof(SpecialCustomerData), "additionalBuyQuantityPerRank");
				ProbeMembers(typeof(SpecialCustomerData), "maxBuyQuantity");
			}
		}

		private static void ProbeMembers(Type type, string namePart)
		{
			MemberInfo[] array = (from m in type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
				where m.Name.IndexOf(namePart, StringComparison.OrdinalIgnoreCase) >= 0
				select m).ToArray();
			if (array.Length == 0)
			{
				Melon<Core>.Logger.Warning("[diag] Probe: " + type.Name + " has NO member containing '" + namePart + "'.");
			}
			MemberInfo[] array2 = array;
			foreach (MemberInfo memberInfo in array2)
			{
				Melon<Core>.Logger.Msg($"[diag] Probe: {type.Name}.{memberInfo.Name} is a {memberInfo.MemberType}");
			}
		}

		private static void ProbeMethod(Type type, string name)
		{
			MethodBase methodBase = AccessTools.Method(type, name, (Type[])null, (Type[])null);
			if (methodBase == null)
			{
				Melon<Core>.Logger.Warning("[diag] Probe: " + type.Name + "." + name + " NOT FOUND");
			}
			else
			{
				Melon<Core>.Logger.Msg("[diag] Probe: " + type.Name + "." + name + " found (" + (methodBase.IsPublic ? "public" : "non-public") + ")");
			}
		}

		public static void ListPatchedMethods(Harmony harmony)
		{
			if (!Enabled)
			{
				return;
			}
			foreach (MethodBase patchedMethod in harmony.GetPatchedMethods())
			{
				Melon<Core>.Logger.Msg("[diag] Harmony reports patched: " + patchedMethod.DeclaringType?.Name + "." + patchedMethod.Name);
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		internal IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}

BetterSpecialCustomers_IL2Cpp.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using BetterSpecialCustomers;
using BetterSpecialCustomers.Diagnostics;
using BetterSpecialCustomers.Quests;
using BetterSpecialCustomers.Relationships;
using HarmonyLib;
using Il2CppFishNet;
using Il2CppFishNet.Connection;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne;
using Il2CppScheduleOne.Core.Items.Framework;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Dialogue;
using Il2CppScheduleOne.Doors;
using Il2CppScheduleOne.Economy;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.Levelling;
using Il2CppScheduleOne.Map;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.NPCs.Relation;
using Il2CppScheduleOne.NPCs.Responses;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.Police;
using Il2CppScheduleOne.SpecialCustomers;
using Il2CppScheduleOne.Storage;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.IO;
using MelonLoader;
using Microsoft.CodeAnalysis;
using S1API.Entities;
using S1API.Entities.Appearances.AccessoryFields;
using S1API.Entities.Appearances.Base;
using S1API.Entities.Appearances.BodyLayerFields;
using S1API.Entities.Appearances.CustomizationFields;
using S1API.Entities.Appearances.FaceLayerFields;
using S1API.Entities.Schedule;
using S1API.Internal.Abstraction;
using S1API.Leveling;
using S1API.Map;
using S1API.Map.Buildings;
using S1API.Money;
using S1API.Quests;
using S1API.Saveables;
using UnityEngine;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "BetterSpecialCustomers", "1.0.0", "Lumahex", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: HarmonyDontPatchAll]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("BetterSpecialCustomers_IL2Cpp")]
[assembly: AssemblyConfiguration("IL2CPP")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c7e4d29411e07561897b504f24ae00b062ef3bf0")]
[assembly: AssemblyProduct("BetterSpecialCustomers_IL2Cpp")]
[assembly: AssemblyTitle("BetterSpecialCustomers_IL2Cpp")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MONO_IL2CPP_Template.TemplateUtils
{
	public static class AssetBundleUtils
	{
		private static Core mod = MelonAssembly.FindMelonInstance<Core>();

		private static MelonAssembly melonAssembly = ((MelonBase)mod).MelonAssembly;

		public static Il2CppAssetBundle LoadAssetBundle(string bundleFileName)
		{
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			try
			{
				string text = typeof(Core).Namespace + ".Assets." + bundleFileName;
				Stream manifestResourceStream = melonAssembly.Assembly.GetManifestResourceStream(text ?? "");
				if (manifestResourceStream == null)
				{
					((MelonBase)mod).Unregister($"AssetBundle: '{text}' not found. \nOpen .csproj file and search for '{bundleFileName}'.\nIf it doesn't exist,\nCopy your asset to Assets/ folder then look for 'your.assetbundle' in .csproj file.", false);
					return null;
				}
				byte[] array;
				using (MemoryStream memoryStream = new MemoryStream())
				{
					manifestResourceStream.CopyTo(memoryStream);
					array = memoryStream.ToArray();
				}
				Stream val = (Stream)new MemoryStream(Il2CppStructArray<byte>.op_Implicit(array));
				return Il2CppAssetBundleManager.LoadFromStream(val);
			}
			catch (Exception value)
			{
				((MelonBase)mod).Unregister($"Failed to load AssetBundle. Please report to dev: {value}", false);
				return null;
			}
		}

		public static Il2CppAssetBundle GetLoadedAssetBundle(string asset_name_flag)
		{
			Il2CppAssetBundle[] allLoadedAssetBundles = Il2CppAssetBundleManager.GetAllLoadedAssetBundles();
			try
			{
				Il2CppAssetBundle[] array = allLoadedAssetBundles;
				foreach (Il2CppAssetBundle val in array)
				{
					if (val.Contains(asset_name_flag))
					{
						return val;
					}
				}
				string text = "";
				Il2CppAssetBundle[] array2 = allLoadedAssetBundles;
				foreach (Il2CppAssetBundle val2 in array2)
				{
					string[] array3 = Il2CppArrayBase<string>.op_Implicit((Il2CppArrayBase<string>)(object)val2.GetAllAssetNames());
					string text2 = string.Join("\n\r -", array3);
					text = text + ((object)val2)?.ToString() + $"({array3.Length} assets):" + text2;
				}
				throw new Exception($"Asset '{asset_name_flag}' not found in {allLoadedAssetBundles.Length} bundle(s).\n{text}");
			}
			catch (Exception value)
			{
				((MelonBase)mod).Unregister($"Failed to get loaded AssetBundle. Please report to dev: \n{value}", false);
				return null;
			}
		}

		public static GameObject LoadAssetFromBundle(string asset_name)
		{
			Il2CppAssetBundle loadedAssetBundle = GetLoadedAssetBundle(asset_name);
			return loadedAssetBundle.LoadAsset<GameObject>(asset_name);
		}
	}
}
namespace BetterSpecialCustomers
{
	public class Core : MelonMod
	{
		private float _nextQuestTick;

		public override void OnInitializeMelon()
		{
			((MelonBase)this).LoggerInstance.Msg("Initialized.");
			PatchDiagnostics.Probe();
			PatchDiagnostics.PatchAllClasses(((MelonBase)this).HarmonyInstance);
			PatchDiagnostics.ListPatchedMethods(((MelonBase)this).HarmonyInstance);
		}

		public override void OnUpdate()
		{
			if (Time.unscaledTime >= _nextQuestTick)
			{
				_nextQuestTick = Time.unscaledTime + 1f;
				HippiesTask.Tick();
			}
		}
	}
}
namespace BetterSpecialCustomers.Relationships
{
	public static class GroupRelationships
	{
		private class State
		{
			public float Current = 2.5f;

			public float Applied = 2.5f;

			public bool InVisit;

			public bool GainedThisVisit;

			public int SoldThisVisit;
		}

		public const float Min = 0f;

		public const float Max = 5f;

		public const float DefaultValue = 2.5f;

		public const float MultiplierAtMin = 0.5f;

		public const float MultiplierAtMax = 1.5f;

		public const float GainPerVisit = 0.5f;

		public const float MinFractionOfLimitForGain = 0.75f;

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

		public const string JsonKey = "BetterSpecialCustomers_Relationships";

		private static readonly Regex SectionRegex = new Regex("\"BetterSpecialCustomers_Relationships\"\\s*:\\s*\\[(.*?)\\]", RegexOptions.Compiled | RegexOptions.Singleline);

		private static readonly Regex EntryRegex = new Regex("\\{([^}]*)\\}", RegexOptions.Compiled);

		private static string Key(string groupId)
		{
			return (groupId ?? string.Empty).ToLowerInvariant();
		}

		private static State GetState(string groupId)
		{
			string key = Key(groupId);
			if (!States.TryGetValue(key, out var value))
			{
				value = new State();
				States[key] = value;
			}
			return value;
		}

		private static float Clamp(float value)
		{
			return Math.Max(0f, Math.Min(5f, value));
		}

		public static float GetCurrent(string groupId)
		{
			return GetState(groupId).Current;
		}

		public static float GetApplied(string groupId)
		{
			return GetState(groupId).Applied;
		}

		public static ERelationshipCategory GetCategory(float value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return RelationshipCategory.GetCategory(value);
		}

		public static float GetMultiplier(string groupId)
		{
			float num = (GetApplied(groupId) - 0f) / 5f;
			return 0.5f + 1f * num;
		}

		public static void BeginVisit(string groupId)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			State state = GetState(groupId);
			if (!state.InVisit)
			{
				state.InVisit = true;
				state.GainedThisVisit = false;
				state.SoldThisVisit = 0;
				state.Applied = state.Current;
				Melon<Core>.Logger.Msg($"[{groupId}] new visit. Relationship {state.Applied:0.00} ({GetCategory(state.Applied)}) now applies: buy multiplier x{GetMultiplier(groupId):0.00}");
			}
		}

		public static void EndVisit(string groupId)
		{
			GetState(groupId).InVisit = false;
		}

		public static void RegisterSale(string groupId, int unitsSold, int limit)
		{
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			State state = GetState(groupId);
			if (!state.GainedThisVisit && limit > 0)
			{
				state.SoldThisVisit += unitsSold;
				float num = (float)state.SoldThisVisit / (float)limit;
				if (num < 0.75f)
				{
					Melon<Core>.Logger.Msg($"[{groupId}] sold {state.SoldThisVisit} so far this visit ({num:P0} of their limit of {limit}); need {0.75f:P0} for a relationship gain");
				}
				else
				{
					state.GainedThisVisit = true;
					float current = state.Current;
					state.Current = Clamp(current + 0.5f);
					Melon<Core>.Logger.Msg($"[{groupId}] relationship {current:0.00} -> {state.Current:0.00} ({GetCategory(state.Current)}). It applies from their next visit; this visit stays at x{GetMultiplier(groupId):0.00}");
				}
			}
		}

		public static void Penalize(string groupId, float amount, string reason)
		{
			State state = GetState(groupId);
			float current = state.Current;
			state.Current = Clamp(current - amount);
			Melon<Core>.Logger.Msg($"[{groupId}] relationship {current:0.00} -> {state.Current:0.00} ({reason})");
		}

		public static void Reward(string groupId, float amount, string reason)
		{
			State state = GetState(groupId);
			float current = state.Current;
			state.Current = Clamp(current + amount);
			Melon<Core>.Logger.Msg($"[{groupId}] relationship {current:0.00} -> {state.Current:0.00} ({reason})");
		}

		private static string ReadField(string entry, string name)
		{
			Match match = Regex.Match(entry, "\"" + name + "\"\\s*:\\s*\"?([^\",\\s}]+)");
			return match.Success ? match.Groups[1].Value : null;
		}

		private static float ReadFloat(string entry, string name, float fallback)
		{
			string text = ReadField(entry, name);
			float result;
			return (text != null && float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out result)) ? Clamp(result) : fallback;
		}

		public static void Clear()
		{
			States.Clear();
		}

		public static string ToJsonProperty()
		{
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, State> state in States)
			{
				State value = state.Value;
				list.Add(string.Format(CultureInfo.InvariantCulture, "{{ \"id\": \"{0}\", \"current\": {1}, \"applied\": {2}, \"inVisit\": {3}, \"gained\": {4}, \"sold\": {5} }}", state.Key, value.Current.ToString("R", CultureInfo.InvariantCulture), value.Applied.ToString("R", CultureInfo.InvariantCulture), value.InVisit ? 1 : 0, value.GainedThisVisit ? 1 : 0, value.SoldThisVisit));
			}
			return $"\"{"BetterSpecialCustomers_Relationships"}\": [ {string.Join(", ", list)} ]";
		}

		public static void LoadFromJson(string json)
		{
			States.Clear();
			if (string.IsNullOrEmpty(json))
			{
				return;
			}
			Match match = SectionRegex.Match(json);
			if (!match.Success)
			{
				return;
			}
			foreach (Match item in EntryRegex.Matches(match.Groups[1].Value))
			{
				string value = item.Groups[1].Value;
				string text = ReadField(value, "id");
				if (!string.IsNullOrEmpty(text))
				{
					State state = GetState(text);
					state.Current = ReadFloat(value, "current", 2.5f);
					state.Applied = ReadFloat(value, "applied", state.Current);
					state.InVisit = ReadField(value, "inVisit") == "1";
					state.GainedThisVisit = ReadField(value, "gained") == "1";
					state.SoldThisVisit = (int.TryParse(ReadField(value, "sold"), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) ? result : 0);
				}
			}
		}
	}
}
namespace BetterSpecialCustomers.Quests
{
	public class BikersQuestSave : Saveable
	{
		[SaveableField("bikers_quest")]
		public JobSaveData Data = new JobSaveData();

		public static BikersQuestSave Instance { get; private set; }

		public BikersQuestSave()
		{
			Instance = this;
		}

		protected override void OnLoaded()
		{
			Instance = this;
			Informant.Refresh();
		}
	}
	public sealed class Informant : NPC
	{
		private const string NpcId = "bikers_informant";

		private const string HideoutName = "The Piss Hut";

		private static Informant _instance;

		private static int _lookAppliedForRun = -1;

		private static readonly string[] FirstNames = new string[12]
		{
			"Dale", "Rusty", "Vince", "Eddie", "Skeet", "Lenny", "Wade", "Cody", "Marv", "Gus",
			"Terry", "Bo"
		};

		private static readonly string[] LastNames = new string[12]
		{
			"Hollis", "Brandt", "Keane", "Mercer", "Voss", "Pruitt", "Lund", "Garrity", "Boone", "Sykes",
			"Dunn", "Rourke"
		};

		private static readonly Color32[] HairColors = (Color32[])(object)new Color32[5]
		{
			new Color32((byte)30, (byte)22, (byte)18, byte.MaxValue),
			new Color32((byte)60, (byte)40, (byte)28, byte.MaxValue),
			new Color32((byte)95, (byte)65, (byte)40, byte.MaxValue),
			new Color32((byte)150, (byte)120, (byte)80, byte.MaxValue),
			new Color32((byte)120, (byte)120, (byte)120, byte.MaxValue)
		};

		private static readonly string[] HairStyles = new string[7] { "Avatar/Hair/buzzcut/BuzzCut", "Avatar/Hair/closebuzzcut/CloseBuzzCut", "Avatar/Hair/balding/Balding", "Avatar/Hair/receding/Receding", "Avatar/Hair/longslicked/LongSlicked", "Avatar/Hair/shoulderlength/ShoulderLength", "Avatar/Hair/peaked/Peaked" };

		private static readonly Color32[] ShirtColors = (Color32[])(object)new Color32[4]
		{
			new Color32((byte)25, (byte)25, (byte)25, byte.MaxValue),
			new Color32((byte)60, (byte)60, (byte)60, byte.MaxValue),
			new Color32((byte)110, (byte)25, (byte)25, byte.MaxValue),
			new Color32((byte)90, (byte)90, (byte)90, byte.MaxValue)
		};

		private static readonly Color32[] JeansColors = (Color32[])(object)new Color32[3]
		{
			new Color32((byte)30, (byte)38, (byte)60, byte.MaxValue),
			new Color32((byte)20, (byte)20, (byte)24, byte.MaxValue),
			new Color32((byte)55, (byte)65, (byte)90, byte.MaxValue)
		};

		public override bool IsPhysical => true;

		public static string PickName()
		{
			return QuestNpcUtil.RandomName(FirstNames, LastNames);
		}

		protected override void ConfigurePrefab(NPCPrefabBuilder builder)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			builder.WithIdentity("bikers_informant", "Informant", string.Empty).WithSpawnPosition(QuestNpcUtil.ParkedPosition).WithSchedule((Action<PrefabScheduleBuilder>)delegate(PrefabScheduleBuilder plan)
			{
				plan.StayInBuilding(Building.Get<ThePissHut>(), 0, 1439, (int?)null, (string)null);
			});
		}

		protected override void OnCreated()
		{
			((NPC)this).OnCreated();
			((NPC)this).Appearance.Build();
			_instance = this;
			Melon<Core>.Logger.Msg("[quest] the informant NPC was created");
			((NPC)this).OnDeath += BikersQuest.Job.OnObjectiveDone;
			Refresh();
		}

		public static void Refresh()
		{
			if (_instance == null)
			{
				return;
			}
			JobSaveData jobSaveData = BikersQuestSave.Instance?.Data;
			bool flag = jobSaveData != null && jobSaveData.Accepted && !jobSaveData.TargetKilled;
			try
			{
				if (flag)
				{
					_instance.Deploy(jobSaveData);
				}
				else
				{
					QuestNpcUtil.Park((NPC)(object)_instance, "bikers_informant");
				}
			}
			catch (Exception value)
			{
				Melon<Core>.Logger.Error($"[quest] informant {(flag ? "deploy" : "park")} failed: {value}");
			}
		}

		private void Deploy(JobSaveData save)
		{
			QuestNpcUtil.ApplyName((NPC)(object)this, save.TargetName);
			if (_lookAppliedForRun != save.Runs)
			{
				_lookAppliedForRun = save.Runs;
				ApplyBikerLook();
			}
			QuestNpcUtil.Revive((NPC)(object)this);
			QuestNpcUtil.EnterBuilding("bikers_informant", "The Piss Hut");
			((NPC)this).Schedule.Enable();
		}

		private void ApplyBikerLook()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_041b: Unknown result type (might be due to invalid IL or missing references)
			AppearanceUtil.ClearLayers(((NPC)this).Appearance);
			Color val = Color32.op_Implicit(QuestNpcUtil.Pick(HairColors));
			((NPC)this).Appearance.Set<Gender>((object)0f);
			((NPC)this).Appearance.Set<Height>((object)Random.Range(0.95f, 1.05f));
			((NPC)this).Appearance.Set<Weight>((object)Random.Range(0.45f, 0.75f));
			((NPC)this).Appearance.Set<SkinColor>((object)Color32.op_Implicit(QuestNpcUtil.Pick(QuestNpcUtil.SkinTones)));
			((NPC)this).Appearance.Set<EyeBallTint>((object)Color.white);
			((NPC)this).Appearance.Set<HairColor>((object)val);
			((NPC)this).Appearance.Set<HairStyle>((object)QuestNpcUtil.Pick(HairStyles));
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(0.15f, 0.1f, 0.1f);
			((NPC)this).Appearance.WithFaceLayer<Face>((Random.value < 0.5f) ? "Avatar/Layers/Face/Face_Neutral" : "Avatar/Layers/Face/Face_SlightFrown", val2);
			if (Random.value < 0.6f)
			{
				((NPC)this).Appearance.WithFaceLayer<FacialHair>((Random.value < 0.5f) ? "Avatar/Layers/Face/FacialHair_Stubble" : "Avatar/Layers/Face/FacialHair_Goatee", val);
			}
			string[] items = new string[5] { "Avatar/Layers/Top/T-Shirt", "Avatar/Layers/Top/T-Shirt", "Avatar/Layers/Top/V-Neck", "Avatar/Layers/Top/FlannelButtonUp", "Avatar/Layers/Top/RolledButtonUp" };
			((NPC)this).Appearance.WithBodyLayer<Shirts>(QuestNpcUtil.Pick(items), Color32.op_Implicit(QuestNpcUtil.Pick(ShirtColors)));
			((NPC)this).Appearance.WithBodyLayer<Pants>((Random.value < 0.7f) ? "Avatar/Layers/Bottom/Jeans" : "Avatar/Layers/Bottom/CargoPants", Color32.op_Implicit(QuestNpcUtil.Pick(JeansColors)));
			Color color = ((Random.value < 0.7f) ? new Color(0.08f, 0.08f, 0.08f) : new Color(0.2f, 0.12f, 0.07f));
			float value = Random.value;
			if (value < 0.6f)
			{
				AppearanceUtil.Wear<Chest>(((NPC)this).Appearance, color, new string[2] { "Avatar/Accessories/Chest/BikerJacket/BikerJacket", "Avatar/Accessories/Chest/BikerJacket/Biker Jacket" });
			}
			else if (value < 0.85f)
			{
				AppearanceUtil.Wear<Chest>(((NPC)this).Appearance, color, new string[2] { "Avatar/Accessories/Chest/LeatherJacket/LeatherJacket", "Avatar/Accessories/Chest/LeatherJacket/leatherJacket" });
			}
			else
			{
				AppearanceUtil.Wear<Chest>(((NPC)this).Appearance, color, new string[1] { "Avatar/Accessories/Chest/OpenVest/OpenVest" });
			}
			AppearanceUtil.Wear<Feet>(((NPC)this).Appearance, new Color(0.1f, 0.1f, 0.1f), new string[1] { (Random.value < 0.85f) ? "Avatar/Accessories/Feet/CombatBoots/CombatBoots" : "Avatar/Accessories/Feet/Sneakers/Sneakers" });
			if (Random.value < 0.5f)
			{
				AppearanceUtil.Wear<Waist>(((NPC)this).Appearance, new Color(0.1f, 0.07f, 0.05f), new string[1] { "Avatar/Accessories/Waist/Belt/Belt" });
			}
			float value2 = Random.value;
			if (value2 < 0.3f)
			{
				AppearanceUtil.Wear<Head>(((NPC)this).Appearance, new Color(0.1f, 0.1f, 0.1f), new string[2] { "Avatar/Accessories/Head/Bandana/Bandana", "Avatar/Accessories/Head/PaisleyBandana/PaisleyBandana" });
			}
			else if (value2 < 0.5f)
			{
				AppearanceUtil.Wear<Head>(((NPC)this).Appearance, new Color(0.12f, 0.12f, 0.12f), new string[1] { "Avatar/Accessories/Head/Beanie/Beanie" });
			}
			else if (value2 < 0.7f)
			{
				AppearanceUtil.Wear<Head>(((NPC)this).Appearance, Color.white, new string[1] { "Avatar/Accessories/Head/Oakleys/Oakleys" });
			}
			if (Random.value < 0.4f)
			{
				AppearanceUtil.Wear<Hands>(((NPC)this).Appearance, new Color(0.1f, 0.1f, 0.1f), new string[1] { "Avatar/Accessories/Hands/FingerlessGloves/FingerlessGloves" });
			}
			if (Random.value < 0.4f)
			{
				AppearanceUtil.Wear<Neck>(((NPC)this).Appearance, Color.white, new string[2] { "Avatar/Accessories/Neck/GoldChain/GoldChain", "Avatar/Accessories/Neck/SilverChain/SilverChain" });
			}
			AppearanceUtil.ApplyNow(((NPC)this).Appearance);
		}
	}
	public class BikersQuest : Quest
	{
		internal static readonly JobController Job = new JobController(new JobDefinition
		{
			QuestIdPrefix = "bikers_informant_quest",
			RewardLogLabel = "Diesel's informant job",
			GetSave = () => BikersQuestSave.Instance?.Data,
			PickName = Informant.PickName,
			RefreshTarget = Informant.Refresh,
			CreateQuest = (string id) => QuestManager.CreateQuest<BikersQuest>(id)
		});

		protected override string Title => "Loose Lips";

		protected override string Description => "Someone in the club has been talking to the cops. Diesel Rodd wants the informant dealt with.";

		protected override bool AutoBegin => true;

		protected override void OnCreated()
		{
			((Registerable)this).OnCreated();
			Job.Attach((Quest)(object)this);
			string text = Job.TargetNameOr("the informant");
			QuestEntry val = ((Quest)this).AddEntry("Knock on the door of The Piss Hut and kill " + text, (Vector3?)null);
			val.SetPOIToNPC<Informant>();
			((Quest)this).AddEntry("Tell Diesel " + text + " is dealt with", (Vector3?)null);
		}

		protected override void OnLoaded()
		{
			((Saveable)this).OnLoaded();
			Job.Attach((Quest)(object)this);
		}
	}
	public class BusinessmenQuestSave : Saveable
	{
		[SaveableField("businessmen_quest")]
		public JobSaveData Data = new JobSaveData();

		public static BusinessmenQuestSave Instance { get; private set; }

		public BusinessmenQuestSave()
		{
			Instance = this;
		}

		protected override void OnLoaded()
		{
			Instance = this;
			Auditor.Refresh();
		}
	}
	public sealed class Auditor : NPC
	{
		private const string NpcId = "businessmen_auditor";

		private const string HomeName = "Upscale Apartments";

		private static Auditor _instance;

		private static int _lookAppliedForRun = -1;

		private static readonly string[] FirstNames = new string[10] { "Gordon", "Nigel", "Howard", "Clive", "Warren", "Preston", "Desmond", "Edwin", "Leonard", "Franklin" };

		private static readonly string[] LastNames = new string[10] { "Whitlock", "Ashcroft", "Pembroke", "Thorne", "Hargrove", "Fairbanks", "Lockhart", "Sterling", "Winslow", "Crane" };

		private static readonly Color32[] HairColors = (Color32[])(object)new Color32[5]
		{
			new Color32((byte)30, (byte)22, (byte)18, byte.MaxValue),
			new Color32((byte)60, (byte)40, (byte)28, byte.MaxValue),
			new Color32((byte)95, (byte)65, (byte)40, byte.MaxValue),
			new Color32((byte)120, (byte)120, (byte)120, byte.MaxValue),
			new Color32((byte)190, (byte)190, (byte)190, byte.MaxValue)
		};

		private static readonly string[] HairStyles = new string[6] { "Avatar/Hair/closebuzzcut/CloseBuzzCut", "Avatar/Hair/peaked/Peaked", "Avatar/Hair/receding/Receding", "Avatar/Hair/balding/Balding", "Avatar/Hair/midfringe/MidFringe", "Avatar/Hair/sidepartbob/SidePartBob" };

		private static readonly Color32[] ShirtColors = (Color32[])(object)new Color32[3]
		{
			new Color32((byte)235, (byte)235, (byte)235, byte.MaxValue),
			new Color32((byte)180, (byte)200, (byte)225, byte.MaxValue),
			new Color32((byte)210, (byte)210, (byte)200, byte.MaxValue)
		};

		private static readonly Color32[] SuitColors = (Color32[])(object)new Color32[4]
		{
			new Color32((byte)25, (byte)28, (byte)40, byte.MaxValue),
			new Color32((byte)45, (byte)45, (byte)50, byte.MaxValue),
			new Color32((byte)20, (byte)20, (byte)20, byte.MaxValue),
			new Color32((byte)55, (byte)45, (byte)38, byte.MaxValue)
		};

		public override bool IsPhysical => true;

		public static string PickName()
		{
			return QuestNpcUtil.RandomName(FirstNames, LastNames);
		}

		protected override void ConfigurePrefab(NPCPrefabBuilder builder)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			builder.WithIdentity("businessmen_auditor", "Auditor", string.Empty).WithSpawnPosition(QuestNpcUtil.ParkedPosition).WithSchedule((Action<PrefabScheduleBuilder>)delegate(PrefabScheduleBuilder plan)
			{
				Building val = Building.Get<UpscaleApartments>();
				Building val2 = Building.Get<TallTower>();
				Building val3 = Building.Get<Cafe>();
				Building val4 = Building.Get<ChineseRestaurant>();
				plan.StayInBuilding(val, 0, 450, (int?)null, (string)null);
				plan.StayInBuilding(val2, 800, 240, (int?)null, (string)null);
				plan.StayInBuilding(val3, 1215, 60, (int?)null, (string)null);
				plan.StayInBuilding(val2, 1330, 210, (int?)null, (string)null);
				plan.StayInBuilding(val4, 1800, 120, (int?)null, (string)null);
				plan.StayInBuilding(val, 2100, 179, (int?)null, (string)null);
			});
		}

		protected override void OnCreated()
		{
			((NPC)this).OnCreated();
			((NPC)this).Appearance.Build();
			_instance = this;
			Melon<Core>.Logger.Msg("[quest] the auditor NPC was created");
			((NPC)this).OnDeath += AuditQuest.Job.OnObjectiveDone;
			Refresh();
		}

		public static void Refresh()
		{
			if (_instance == null)
			{
				return;
			}
			JobSaveData jobSaveData = BusinessmenQuestSave.Instance?.Data;
			bool flag = jobSaveData != null && jobSaveData.Accepted && !jobSaveData.TargetKilled;
			try
			{
				if (flag)
				{
					_instance.Deploy(jobSaveData);
				}
				else
				{
					QuestNpcUtil.Park((NPC)(object)_instance, "businessmen_auditor");
				}
			}
			catch (Exception value)
			{
				Melon<Core>.Logger.Error($"[quest] auditor {(flag ? "deploy" : "park")} failed: {value}");
			}
		}

		private void Deploy(JobSaveData save)
		{
			QuestNpcUtil.ApplyName((NPC)(object)this, save.TargetName);
			if (_lookAppliedForRun != save.Runs)
			{
				_lookAppliedForRun = save.Runs;
				ApplyBusinessLook();
			}
			QuestNpcUtil.Revive((NPC)(object)this);
			QuestNpcUtil.EnterBuilding("businessmen_auditor", "Upscale Apartments");
			((NPC)this).Schedule.Enable();
			((NPC)this).Schedule.EnforceState();
		}

		private void ApplyBusinessLook()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			AppearanceUtil.ClearLayers(((NPC)this).Appearance);
			Color val = Color32.op_Implicit(QuestNpcUtil.Pick(HairColors));
			((NPC)this).Appearance.Set<Gender>((object)0f);
			((NPC)this).Appearance.Set<Height>((object)Random.Range(0.95f, 1.05f));
			((NPC)this).Appearance.Set<Weight>((object)Random.Range(0.35f, 0.65f));
			((NPC)this).Appearance.Set<SkinColor>((object)Color32.op_Implicit(QuestNpcUtil.Pick(QuestNpcUtil.SkinTones)));
			((NPC)this).Appearance.Set<EyeBallTint>((object)Color.white);
			((NPC)this).Appearance.Set<HairColor>((object)val);
			((NPC)this).Appearance.Set<HairStyle>((object)QuestNpcUtil.Pick(HairStyles));
			((NPC)this).Appearance.WithFaceLayer<Face>((Random.value < 0.6f) ? "Avatar/Layers/Face/Face_Neutral" : "Avatar/Layers/Face/Face_SlightFrown", new Color(0.15f, 0.1f, 0.1f));
			if (Random.value < 0.25f)
			{
				((NPC)this).Appearance.WithFaceLayer<FacialHair>("Avatar/Layers/Face/FacialHair_Stubble", val);
			}
			Color val2 = Color32.op_Implicit(QuestNpcUtil.Pick(SuitColors));
			((NPC)this).Appearance.WithBodyLayer<Shirts>((Random.value < 0.8f) ? "Avatar/Layers/Top/Buttonup" : "Avatar/Layers/Top/RolledButtonUp", Color32.op_Implicit(QuestNpcUtil.Pick(ShirtColors)));
			((NPC)this).Appearance.WithBodyLayer<Pants>("Avatar/Layers/Bottom/Jeans", val2);
			AppearanceUtil.Wear<Chest>(((NPC)this).Appearance, val2, new string[1] { "Avatar/Accessories/Chest/Blazer/Blazer" });
			AppearanceUtil.Wear<Feet>(((NPC)this).Appearance, new Color(0.08f, 0.06f, 0.05f), new string[1] { "Avatar/Accessories/Feet/DressShoes/DressShoes" });
			string[] items = new string[4] { "Avatar/Accessories/Neck/StripedTie/StripedTie", "Avatar/Accessories/Neck/CheckedTie/CheckedTie", "Avatar/Accessories/Neck/DottedTie/DottedTie", "Avatar/Accessories/Neck/CrossTie/CrossTie" };
			AppearanceUtil.Wear<Neck>(((NPC)this).Appearance, new Color(0.35f, 0.08f, 0.1f), new string[1] { QuestNpcUtil.Pick(items) });
			float value = Random.value;
			if (value < 0.35f)
			{
				AppearanceUtil.Wear<Head>(((NPC)this).Appearance, Color.white, new string[1] { "Avatar/Accessories/Head/RectangleFrameGlasses/RectangleFrameGlasses" });
			}
			else if (value < 0.5f)
			{
				AppearanceUtil.Wear<Head>(((NPC)this).Appearance, Color.white, new string[1] { "Avatar/Accessories/Head/SmallRoundGlasses/SmallRoundGlasses" });
			}
			if (Random.value < 0.5f)
			{
				AppearanceUtil.Wear<Hands>(((NPC)this).Appearance, Color.white, new string[1] { "Avatar/Accessories/Hands/Polex/Polex" });
			}
			AppearanceUtil.ApplyNow(((NPC)this).Appearance);
		}
	}
	public class AuditQuest : Quest
	{
		internal static readonly JobController Job = new JobController(new JobDefinition
		{
			QuestIdPrefix = "businessmen_audit_quest",
			RewardLogLabel = "Johnny's audit job",
			GetSave = () => BusinessmenQuestSave.Instance?.Data,
			PickName = Auditor.PickName,
			RefreshTarget = Auditor.Refresh,
			CreateQuest = (string id) => QuestManager.CreateQuest<AuditQuest>(id)
		});

		protected override string Title => "The Audit";

		protected override string Description => "An auditor is getting too close to the books. Johnny Smith wants him dealt with.";

		protected override bool AutoBegin => true;

		protected override void OnCreated()
		{
			((Registerable)this).OnCreated();
			Job.Attach((Quest)(object)this);
			string text = Job.TargetNameOr("the auditor");
			QuestEntry val = ((Quest)this).AddEntry("Find and kill " + text + ". He keeps a strict daily routine", (Vector3?)null);
			val.SetPOIToNPC<Auditor>();
			((Quest)this).AddEntry("Tell Johnny Smith " + text + " is dealt with", (Vector3?)null);
		}

		protected override void OnLoaded()
		{
			((Saveable)this).OnLoaded();
			Job.Attach((Quest)(object)this);
		}
	}
	public class DropRun
	{
		public string FromGuid = string.Empty;

		public string ToGuid = string.Empty;

		public string FromName = string.Empty;

		public string ToName = string.Empty;

		public bool Placed;

		public bool PickedUp;
	}
	public class HippiesQuestSave : Saveable
	{
		[SaveableField("hippies_quest")]
		public JobSaveData Data = new JobSaveData();

		[SaveableField("hippies_quest_drops")]
		public DropRun Drops = new DropRun();

		public static HippiesQuestSave Instance { get; private set; }

		public HippiesQuestSave()
		{
			Instance = this;
		}

		protected override void OnLoaded()
		{
			Instance = this;
		}
	}
	internal static class HippiesTask
	{
		private const string PackageItemId = "paisleybandana";

		private static readonly KeyValuePair<string, int>[] Supplies = new KeyValuePair<string, int>[4]
		{
			new KeyValuePair<string, int>("extralonglifesoil", 3),
			new KeyValuePair<string, int>("speedgrow", 3),
			new KeyValuePair<string, int>("pgr", 1),
			new KeyValuePair<string, int>("fertilizer", 1)
		};

		private const float MinDropDistance = 80f;

		private static HippiesQuestSave Save => HippiesQuestSave.Instance;

		private static DeadDrop FindDrop(string guid)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(guid))
			{
				return null;
			}
			Enumerator<DeadDrop> enumerator = DeadDrop.DeadDrops.GetEnumerator();
			while (enumerator.MoveNext())
			{
				DeadDrop current = enumerator.Current;
				if ((Object)(object)current != (Object)null && string.Equals(((object)current.GUID/*cast due to .constrained prefix*/).ToString(), guid, StringComparison.OrdinalIgnoreCase))
				{
					return current;
				}
			}
			return null;
		}

		public static Vector3? PositionOf(string guid)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			DeadDrop val = FindDrop(guid);
			return ((Object)(object)val != (Object)null) ? new Vector3?(((Component)val).transform.position) : ((Vector3?)null);
		}

		private static int Count(DeadDrop drop, string itemId)
		{
			int num = 0;
			Enumerator<ItemSlot> enumerator = ((StorageEntity)drop.Storage).ItemSlots.GetEnumerator();
			while (enumerator.MoveNext())
			{
				ItemSlot current = enumerator.Current;
				ItemInstance itemInstance = current.ItemInstance;
				if (itemInstance != null && ((BaseItemDefinition)itemInstance.Definition).ID == itemId)
				{
					num += ((BaseItemInstance)itemInstance).Quantity;
				}
			}
			return num;
		}

		private static void RemoveAll(DeadDrop drop, string itemId)
		{
			Enumerator<ItemSlot> enumerator = ((StorageEntity)drop.Storage).ItemSlots.GetEnumerator();
			while (enumerator.MoveNext())
			{
				ItemSlot current = enumerator.Current;
				ItemInstance itemInstance = current.ItemInstance;
				if (itemInstance != null && ((BaseItemDefinition)itemInstance.Definition).ID == itemId)
				{
					current.ClearStoredInstance(false);
				}
			}
		}

		public static void Refresh()
		{
			HippiesQuestSave save = Save;
			if (save == null)
			{
				return;
			}
			if (save.Data.Accepted && !save.Data.TargetKilled)
			{
				if (!save.Drops.Placed)
				{
					ChooseAndPlace(save.Drops);
				}
			}
			else
			{
				Cleanup(save);
			}
		}

		private static void ChooseAndPlace(DropRun run)
		{
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			List<DeadDrop> list = new List<DeadDrop>();
			Enumerator<DeadDrop> enumerator = DeadDrop.DeadDrops.GetEnumerator();
			while (enumerator.MoveNext())
			{
				DeadDrop current = enumerator.Current;
				if ((Object)(object)current != (Object)null && ((StorageEntity)current.Storage).ItemCount == 0)
				{
					list.Add(current);
				}
			}
			if (list.Count < 2)
			{
				Melon<Core>.Logger.Warning("[quest] not enough empty dead drops for the hippies' job");
				return;
			}
			DeadDrop from = list[Random.Range(0, list.Count)];
			List<DeadDrop> list2 = list.FindAll(delegate(DeadDrop d)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val3 = ((Component)d).transform.position - ((Component)from).transform.position;
				return ((Vector3)(ref val3)).magnitude >= 80f;
			});
			DeadDrop val = (DeadDrop)((list2.Count > 0) ? ((object)list2[Random.Range(0, list2.Count)]) : ((object)list.Find((DeadDrop d) => (Object)(object)d != (Object)(object)from)));
			ItemInstance val2 = RewardUtil.CreateItem("paisleybandana", 1);
			if (val2 != null)
			{
				((StorageEntity)from.Storage).InsertItem(val2, true);
				run.FromGuid = ((object)from.GUID/*cast due to .constrained prefix*/).ToString();
				run.ToGuid = ((object)val.GUID/*cast due to .constrained prefix*/).ToString();
				run.FromName = from.DeadDropName;
				run.ToName = val.DeadDropName;
				run.Placed = true;
				run.PickedUp = false;
				Melon<Core>.Logger.Msg($"[quest] package placed at '{run.FromName}', to be delivered to '{run.ToName}'");
			}
		}

		private static void Cleanup(HippiesQuestSave save)
		{
			DropRun drops = save.Drops;
			if (drops.Placed)
			{
				DeadDrop val = FindDrop(drops.FromGuid);
				DeadDrop val2 = FindDrop(drops.ToGuid);
				if ((Object)(object)val != (Object)null)
				{
					RemoveAll(val, "paisleybandana");
				}
				if ((Object)(object)val2 != (Object)null)
				{
					RemoveAll(val2, "paisleybandana");
				}
				save.Drops = new DropRun();
			}
		}

		public static void Tick()
		{
			try
			{
				HippiesQuestSave save = Save;
				if (save == null || !save.Data.Accepted || save.Data.TargetKilled || save.Data.Completed)
				{
					return;
				}
				DropRun drops = save.Drops;
				if (!drops.Placed)
				{
					return;
				}
				JobController job = HippiesQuest.Job;
				if (!drops.PickedUp)
				{
					DeadDrop val = FindDrop(drops.FromGuid);
					if ((Object)(object)val == (Object)null || Count(val, "paisleybandana") > 0)
					{
						return;
					}
					drops.PickedUp = true;
					List<QuestEntry> list = job.Quest?.QuestEntries;
					if (list != null && list.Count > 1)
					{
						list[0].Complete();
						list[1].Begin();
					}
					Melon<Core>.Logger.Msg("[quest] package picked up");
				}
				DeadDrop val2 = FindDrop(drops.ToGuid);
				if ((Object)(object)val2 != (Object)null && Count(val2, "paisleybandana") > 0)
				{
					job.OnObjectiveDone();
				}
			}
			catch (Exception ex)
			{
				Melon<Core>.Logger.Warning("[quest] hippies tick failed: " + ex.Message);
			}
		}

		public static void GiveSupplies()
		{
			RewardUtil.GiveItems(Supplies);
		}
	}
	public class HippiesQuest : Quest
	{
		internal static readonly JobController Job = new JobController(new JobDefinition
		{
			QuestIdPrefix = "hippies_package_quest",
			RewardLogLabel = "Krystal's package job",
			CashReward = 0f,
			GetSave = () => HippiesQuestSave.Instance?.Data,
			PickName = () => string.Empty,
			RefreshTarget = HippiesTask.Refresh,
			GrantExtraReward = HippiesTask.GiveSupplies,
			CreateQuest = (string id) => QuestManager.CreateQuest<HippiesQuest>(id)
		});

		protected override string Title => "Special Delivery";

		protected override string Description => "Krystal Bird needs a package carried between two dead drops.";

		protected override bool AutoBegin => true;

		protected override void OnCreated()
		{
			((Registerable)this).OnCreated();
			Job.Attach((Quest)(object)this);
			DropRun dropRun = HippiesQuestSave.Instance?.Drops ?? new DropRun();
			((Quest)this).AddEntry("Pick up the package from the dead drop at " + dropRun.FromName, HippiesTask.PositionOf(dropRun.FromGuid));
			((Quest)this).AddEntry("Deliver it to the dead drop at " + dropRun.ToName, HippiesTask.PositionOf(dropRun.ToGuid));
			((Quest)this).AddEntry("Tell Krystal Bird the package has been delivered", (Vector3?)null);
		}

		protected override void OnLoaded()
		{
			((Saveable)this).OnLoaded();
			Job.Attach((Quest)(object)this);
		}
	}
	public class PartyRun
	{
		public List<string> Asked = new List<string>();

		public int Accepted;
	}
	public class PartyBusQuestSave : Saveable
	{
		[SaveableField("partybus_quest")]
		public JobSaveData Data = new JobSaveData();

		[SaveableField("partybus_quest_run")]
		public PartyRun Run = new PartyRun();

		public static PartyBusQuestSave Instance { get; private set; }

		public PartyBusQuestSave()
		{
			Instance = this;
		}

		protected override void OnLoaded()
		{
			Instance = this;
		}
	}
	internal static class PartyTask
	{
		public const int PeopleNeeded = 5;

		public const float RejectChance = 0.3f;

		private static readonly KeyValuePair<string, int>[] PartySupplies = new KeyValuePair<string, int>[1]
		{
			new KeyValuePair<string, int>("energydrink", 5)
		};

		private static PartyBusQuestSave Save => PartyBusQuestSave.Instance;

		public static void GiveSupplies()
		{
			RewardUtil.GiveItems(PartySupplies);
		}

		public static bool CanInvite(string npcId)
		{
			PartyBusQuestSave save = Save;
			if (save == null || string.IsNullOrEmpty(npcId))
			{
				return false;
			}
			JobSaveData data = save.Data;
			return data.Accepted && !data.TargetKilled && !data.Completed && !save.Run.Asked.Contains(npcId);
		}

		public static bool Invite(string npcId)
		{
			PartyBusQuestSave save = Save;
			if (save == null || !CanInvite(npcId))
			{
				return false;
			}
			save.Run.Asked.Add(npcId);
			bool flag = Random.value >= 0.3f;
			if (flag)
			{
				save.Run.Accepted++;
			}
			UpdateObjectiveTitle();
			Melon<Core>.Logger.Msg($"[quest] {npcId} {(flag ? "accepted" : "turned down")} the invite ({save.Run.Accepted}/{5})");
			if (save.Run.Accepted >= 5)
			{
				PartyBusQuest.Job.OnObjectiveDone();
			}
			return flag;
		}

		public static string ObjectiveText(int accepted)
		{
			return $"Tell people the Party Bus is in town ({Math.Min(accepted, 5)}/{5})";
		}

		private static void UpdateObjectiveTitle()
		{
			List<QuestEntry> list = PartyBusQuest.Job.Quest?.QuestEntries;
			if (list != null && list.Count > 0)
			{
				list[0].Title = ObjectiveText(Save.Run.Accepted);
			}
		}

		public static void Refresh()
		{
			PartyBusQuestSave save = Save;
			if (save != null && (!save.Data.Accepted || save.Data.TargetKilled) && (save.Run.Asked.Count > 0 || save.Run.Accepted > 0))
			{
				save.Run = new PartyRun();
			}
		}
	}
	public class PartyBusQuest : Quest
	{
		internal static readonly JobController Job = new JobController(new JobDefinition
		{
			QuestIdPrefix = "partybus_word_quest",
			RewardLogLabel = "Chad's spread-the-word job",
			CashReward = 2500f,
			GrantExtraReward = PartyTask.GiveSupplies,
			GetSave = () => PartyBusQuestSave.Instance?.Data,
			PickName = () => string.Empty,
			RefreshTarget = PartyTask.Refresh,
			CreateQuest = (string id) => QuestManager.CreateQuest<PartyBusQuest>(id)
		});

		protected override string Title => "Spread the Word";

		protected override string Description => "Chad Chaddington wants the whole town to know the Party Bus is here.";

		protected override bool AutoBegin => true;

		protected override void OnCreated()
		{
			((Registerable)this).OnCreated();
			Job.Attach((Quest)(object)this);
			int accepted = PartyBusQuestSave.Instance?.Run.Accepted ?? 0;
			((Quest)this).AddEntry(PartyTask.ObjectiveText(accepted), (Vector3?)null);
			((Quest)this).AddEntry("Tell Chad Chaddington the word is out", (Vector3?)null);
		}

		protected override void OnLoaded()
		{
			((Saveable)this).OnLoaded();
			Job.Attach((Quest)(object)this);
		}
	}
	public class JobSaveData
	{
		public bool Accepted;

		public string GroupId = string.Empty;

		public bool TargetKilled;

		public bool Completed;

		public int OfferState;

		public string TargetName = string.Empty;

		public int Runs;
	}
	internal sealed class JobDefinition
	{
		public string QuestIdPrefix;

		public string RewardLogLabel;

		public float RelationshipReward = 0.5f;

		public float CashReward = 5000f;

		public int XpReward = 100;

		public Action GrantExtraReward;

		public Func<JobSaveData> GetSave;

		public Func<string> PickName;

		public Action RefreshTarget;

		public Func<string, Quest> CreateQuest;
	}
	internal sealed class JobLines
	{
		public Func<string, string> OfferLine;

		public string[] NoJobLines;

		public string AlreadyDoneLine;

		public string HandInLine;
	}
	internal sealed class JobController
	{
		public static readonly bool DebugMode = false;

		private static readonly List<JobController> All = new List<JobController>();

		private readonly JobDefinition _def;

		private Quest _quest;

		public static float OfferChance => DebugMode ? 1f : 0.5f;

		public JobSaveData Save => _def.GetSave();

		public Quest Quest => _quest;

		public JobController(JobDefinition def)
		{
			_def = def;
			All.Add(this);
		}

		public void Attach(Quest quest)
		{
			_quest = quest;
			quest.OnComplete -= HandleComplete;
			quest.OnComplete += HandleComplete;
		}

		public string TargetNameOr(string fallback)
		{
			string text = Save?.TargetName;
			return string.IsNullOrEmpty(text) ? fallback : text;
		}

		public bool CanAsk()
		{
			JobSaveData save = Save;
			return save == null || !save.Accepted || save.Completed;
		}

		public bool IsReadyToHandIn()
		{
			JobSaveData save = Save;
			return save != null && save.TargetKilled && !save.Completed;
		}

		public string Ask(string groupId, JobLines lines)
		{
			JobSaveData save = Save;
			if (save == null)
			{
				return null;
			}
			if (!string.IsNullOrEmpty(groupId))
			{
				save.GroupId = groupId;
			}
			if (save.Completed && !DebugMode)
			{
				return lines.AlreadyDoneLine;
			}
			if (save.Completed)
			{
				ResetRun();
			}
			if (save.OfferState == 0 || DebugMode)
			{
				save.OfferState = ((Random.value < OfferChance) ? 1 : 2);
			}
			if (save.OfferState == 1)
			{
				Start(groupId);
				return lines.OfferLine(save.TargetName);
			}
			return lines.NoJobLines[Random.Range(0, lines.NoJobLines.Length)];
		}

		public void Start(string groupId)
		{
			JobSaveData save = Save;
			if (save != null)
			{
				save.Accepted = true;
				save.GroupId = groupId ?? string.Empty;
				save.Runs++;
				save.TargetName = _def.PickName();
			}
			if (_quest == null)
			{
				_quest = _def.CreateQuest($"{_def.QuestIdPrefix}_{save?.Runs ?? 0}");
			}
			if (_quest == null)
			{
				Melon<Core>.Logger.Error("[quest] could not create the quest");
				return;
			}
			_def.RefreshTarget();
			Melon<Core>.Logger.Msg("[quest] created; S1API sets up its entries and begins it on the next frame");
		}

		public void OnObjectiveDone()
		{
			JobSaveData save = Save;
			if (_quest != null && save != null && save.Accepted && !save.TargetKilled)
			{
				save.TargetKilled = true;
				int num = _quest.QuestEntries.Count - 1;
				for (int i = 0; i < num; i++)
				{
					_quest.QuestEntries[i].Complete();
				}
				if (num > 0)
				{
					_quest.QuestEntries[num].Begin();
				}
				Melon<Core>.Logger.Msg("[quest] task done, report back to the leader");
			}
		}

		public void HandIn()
		{
			JobSaveData save = Save;
			if (_quest != null && save != null && save.TargetKilled && !save.Completed)
			{
				save.Completed = true;
				int num = _quest.QuestEntries.Count - 1;
				if (num > 0)
				{
					_quest.QuestEntries[num].Complete();
				}
				_quest.Complete();
				_def.RefreshTarget();
			}
		}

		private void HandleComplete()
		{
			if (_def.CashReward > 0f)
			{
				Money.ChangeCashBalance(_def.CashReward, true, true);
			}
			LevelManager.AddXP(_def.XpReward);
			_def.GrantExtraReward?.Invoke();
			Melon<Core>.Logger.Msg($"[quest] paid ${_def.CashReward}, {_def.XpReward} XP");
			string text = Save?.GroupId;
			if (!string.IsNullOrEmpty(text))
			{
				GroupRelationships.Reward(text, _def.RelationshipReward, "completed " + _def.RewardLogLabel);
			}
			else
			{
				Melon<Core>.Logger.Warning("[quest] no saved group id, relationship reward skipped");
			}
		}

		public void ResetRun()
		{
			JobSaveData save = Save;
			if (save != null)
			{
				if (_quest != null && save.Accepted && !save.Completed)
				{
					_quest.Cancel();
				}
				_quest = null;
				save.OfferState = 0;
				save.TargetName = string.Empty;
				save.Accepted = false;
				save.TargetKilled = false;
				save.Completed = false;
				_def.RefreshTarget();
			}
		}

		public static void OnGroupLeft(string groupId)
		{
			foreach (JobController item in All)
			{
				JobSaveData save = item.Save;
				if (save != null && string.Equals(save.GroupId, groupId, StringComparison.OrdinalIgnoreCase))
				{
					item.ResetRun();
					Melon<Core>.Logger.Msg("[quest] " + groupId + " left, their job can be taken again next visit");
				}
			}
		}
	}
	internal static class RewardUtil
	{
		public static ItemInstance CreateItem(string itemId, int quantity)
		{
			ItemDefinition item = Registry.GetItem(itemId);
			if ((Object)(object)item == (Object)null)
			{
				Melon<Core>.Logger.Warning("[quest] item '" + itemId + "' does not exist");
				return null;
			}
			return item.GetDefaultInstance(quantity);
		}

		public static void GiveItems(params KeyValuePair<string, int>[] items)
		{
			PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				Melon<Core>.Logger.Warning("[quest] no inventory to give the reward to");
				return;
			}
			for (int i = 0; i < items.Length; i++)
			{
				KeyValuePair<string, int> keyValuePair = items[i];
				ItemInstance val = CreateItem(keyValuePair.Key, keyValuePair.Value);
				if (val != null)
				{
					instance.AddItemToInventory(val);
				}
			}
		}
	}
	internal static class QuestNpcUtil
	{
		public static readonly Vector3 ParkedPosition = new Vector3(0f, -200f, 0f);

		public static readonly Color32[] SkinTones = (Color32[])(object)new Color32[5]
		{
			new Color32((byte)236, (byte)200, (byte)170, byte.MaxValue),
			new Color32((byte)224, (byte)172, (byte)138, byte.MaxValue),
			new Color32((byte)198, (byte)140, (byte)100, byte.MaxValue),
			new Color32((byte)168, (byte)112, (byte)78, byte.MaxValue),
			new Color32((byte)130, (byte)84, (byte)58, byte.MaxValue)
		};

		public static T Pick<T>(T[] items)
		{
			return items[Random.Range(0, items.Length)];
		}

		public static string RandomName(string[] firstNames, string[] lastNames)
		{
			return Pick(firstNames) + " " + Pick(lastNames);
		}

		public static void ApplyName(NPC npc, string fullName)
		{
			if (!string.IsNullOrEmpty(fullName))
			{
				int num = fullName.IndexOf(' ');
				npc.FirstName = ((num > 0) ? fullName.Substring(0, num) : fullName);
				npc.LastName = ((num > 0) ? fullName.Substring(num + 1) : string.Empty);
			}
		}

		private static NPC FindGameNpc(string npcId)
		{
			Il2CppArrayBase<NPC> val = Object.FindObjectsOfType<NPC>(true);
			foreach (NPC item in val)
			{
				if ((Object)(object)item != (Object)null && item.ID == npcId)
				{
					return item;
				}
			}
			Melon<Core>.Logger.Warning($"[quest] NPC '{npcId}' is not among the {val.Length} NPCs in the scene");
			return null;
		}

		private static NPCEnterableBuilding FindBuilding(string name)
		{
			foreach (NPCEnterableBuilding item in Object.FindObjectsOfType<NPCEnterableBuilding>(true))
			{
				if ((Object)(object)item != (Object)null && string.Equals(item.BuildingName, name, StringComparison.OrdinalIgnoreCase))
				{
					return item;
				}
			}
			return null;
		}

		public static void EnterBuilding(string npcId, string buildingName)
		{
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			NPC val = FindGameNpc(npcId);
			NPCEnterableBuilding val2 = FindBuilding(buildingName);
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || ((Il2CppArrayBase<StaticDoor>)(object)val2.Doors).Length == 0)
			{
				Melon<Core>.Logger.Warning($"[quest] could not hide '{npcId}' in '{buildingName}' (npc found: {(Object)(object)val != (Object)null}, building found: {(Object)(object)val2 != (Object)null}, doors: {((val2 != null) ? new int?(((Il2CppArrayBase<StaticDoor>)(object)val2.Doors).Length) : ((int?)null))})");
			}
			else if (!((Object)(object)val.CurrentBuilding == (Object)(object)val2))
			{
				if (val.isInBuilding)
				{
					val.ExitBuilding("");
				}
				val.EnterBuilding((NetworkConnection)null, ((object)val2.GUID/*cast due to .constrained prefix*/).ToString(), 0);
				Melon<Core>.Logger.Msg($"[quest] '{npcId}' is now hidden in '{buildingName}'");
			}
		}

		public static void LeaveBuilding(string npcId)
		{
			NPC val = FindGameNpc(npcId);
			if ((Object)(object)val != (Object)null && val.isInBuilding)
			{
				val.ExitBuilding("");
			}
		}

		public static void Park(NPC npc, string npcId)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			npc.Schedule.Disable();
			LeaveBuilding(npcId);
			npc.IsInvincible = true;
			Vector3 val = npc.Position - ParkedPosition;
			if (((Vector3)(ref val)).sqrMagnitude > 1f)
			{
				npc.Position = ParkedPosition;
			}
		}

		public static void Revive(NPC npc)
		{
			if (npc.IsDead)
			{
				npc.Revive();
			}
			npc.Heal((int)npc.MaxHealth);
			npc.IsInvincible = false;
		}
	}
	internal static class AppearanceUtil
	{
		private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		public static void ClearLayers(NPCAppearance appearance)
		{
			try
			{
				object obj = typeof(NPCAppearance).GetField("_customAvatarSettings", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(appearance);
				if (obj != null)
				{
					string[] array = new string[3] { "FaceLayerSettings", "BodyLayerSettings", "AccessorySettings" };
					foreach (string name in array)
					{
						object obj2 = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) ?? obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
						obj2?.GetType().GetMethod("Clear", Type.EmptyTypes)?.Invoke(obj2, null);
					}
				}
			}
			catch (Exception ex)
			{
				Melon<Core>.Logger.Warning("[quest] clearing old layers failed: " + ex.Message);
			}
		}

		public static void ApplyNow(NPCAppearance appearance)
		{
			try
			{
				Type typeFromHandle = typeof(NPCAppearance);
				object obj = typeFromHandle.GetField("_runtimeAvatar", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(appearance);
				MethodInfo method = typeFromHandle.GetMethod("ApplyToAvatar", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (obj == null || method == null)
				{
					Melon<Core>.Logger.Warning($"[quest] could not apply the look (avatar found: {obj != null}, method found: {method != null})");
				}
				else
				{
					method.Invoke(appearance, new object[1] { obj });
				}
			}
			catch (Exception ex)
			{
				Melon<Core>.Logger.Warning("[quest] applying the look failed: " + ex.Message);
			}
		}

		public static void Wear<T>(NPCAppearance appearance, Color color, params string[] candidatePaths) where T : BaseAccessoryAppearance
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			foreach (string text in candidatePaths)
			{
				bool flag = false;
				try
				{
					flag = Resources.Load(text) != (Object)null;
				}
				catch
				{
				}
				if (flag)
				{
					appearance.WithAccessoryLayer<T>(text, color);
					break;
				}
				Melon<Core>.Logger.Warning("[quest] outfit path not found: " + text);
			}
		}
	}
}
namespace BetterSpecialCustomers.Patches
{
	[HarmonyPatch(typeof(SpecialCustomerLeader), "SetCustomerData")]
	internal class BikersQuestChoicePatch
	{
		private static readonly JobDialogue Dialogue = new JobDialogue
		{
			LeaderId = "diesel_rodd",
			AskText = "You got any jobs for me?",
			HandInText = "He's dealt with.",
			Job = BikersQuest.Job,
			Lines = new JobLines
			{
				OfferLine = (string name) => "Somebody's been feedin' the cops about club business. Name's " + name + ". He's holed up at The Piss Hut. Knock on the door, and make sure he never talks again.",
				NoJobLines = new string[3] { "Not this time, prospect. Road's quiet. Come find me when it ain't.", "Nothin' right now. The club'll let you know when we need a hand.", "Ain't got nothin' for you today. Go count your money." },
				AlreadyDoneLine = "You already did right by us this run. Come back when we roll through again.",
				HandInLine = "Good. Rats don't get to walk away. The club won't forget this."
			}
		};

		public static void Postfix(SpecialCustomerLeader __instance)
		{
			Dialogue.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerLeader), "SetCustomerData")]
	internal class BusinessmenQuestChoicePatch
	{
		private static readonly JobDialogue Dialogue = new JobDialogue
		{
			LeaderId = "johnny_smith",
			AskText = "Anything I can help with?",
			HandInText = "The audit won't be a problem anymore.",
			AskSeconds = 8f,
			Job = AuditQuest.Job,
			Lines = new JobLines
			{
				OfferLine = (string name) => "As it happens, yes. An auditor, " + name + ", has been looking at the books for the wrong reasons. He keeps to a strict routine: home, the office, lunch. Make sure he doesn't finish his report.",
				NoJobLines = new string[3] { "Nothing at the moment. Business is... stable. Enjoy it.", "Not today. If something comes up, you'll be the first call.", "We're fully staffed on problems right now. Check back later." },
				AlreadyDoneLine = "You've done enough for us this time. We'll be in touch.",
				HandInLine = "Excellent. A tidy solution. Our accountants will be very pleased."
			}
		};

		public static void Postfix(SpecialCustomerLeader __instance)
		{
			Dialogue.Register(__instance);
		}
	}
	internal static class BuyLimitFieldSync
	{
		private sealed class IntMember
		{
			private readonly FieldInfo _field;

			private readonly PropertyInfo _property;

			public bool Exists => _field != null || _property != null;

			public IntMember(Type type, string name)
			{
				_field = AccessTools.Field(type, name);
				_property = ((_field == null) ? AccessTools.Property(type, name) : null);
			}

			public int Get(object target)
			{
				return Convert.ToInt32((_field != null) ? _field.GetValue(target) : _property.GetValue(target));
			}

			public void Set(object target, int value)
			{
				if (_field != null)
				{
					_field.SetValue(target, value);
				}
				else
				{
					_property.SetValue(target, value);
				}
			}
		}

		private static readonly IntMember BaseQuantity = new IntMember(typeof(SpecialCustomerData), "_baseBuyQuantity");

		private static readonly IntMember PerRank = new IntMember(typeof(SpecialCustomerData), "_additionalBuyQuantityPerRank");

		private static readonly IntMember MaxQuantity = new IntMember(typeof(SpecialCustomerData), "_maxBuyQuantity");

		private static bool _reportedMissing;

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

		public static void Apply(SpecialCustomerManager manager, string groupId)
		{
			try
			{
				if (!PerRank.Exists || !MaxQuantity.Exists)
				{
					if (!_reportedMissing)
					{
						_reportedMissing = true;
						Melon<Core>.Logger.Error($"BuyLimitFieldSync: can't find the game's per-rank / max buy quantity values (perRank found: {PerRank.Exists}, max found: {MaxQuantity.Exists}). See the [diag] Probe lines. The buy limit will stay vanilla.");
					}
					return;
				}
				SpecialCustomerData specialCustomerData = manager.GetSpecialCustomerData(groupId);
				if (!((Object)(object)specialCustomerData == (Object)null))
				{
					int num = 10;
					double num2 = GroupRelationships.GetMultiplier(groupId);
					string key = groupId.ToLowerInvariant();
					if (!OriginalBase.TryGetValue(key, out var value))
					{
						value = specialCustomerData.BaseBuyQuantity;
						OriginalBase[key] = value;
					}
					int num3 = (BaseQuantity.Exists ? ((int)Math.Round((double)value * num2)) : value);
					int num4 = (int)Math.Round(200.0 * num2);
					int num5 = Math.Max(0, (int)Math.Ceiling((double)(num4 - num3) / (double)num));
					int value2 = PerRank.Get(specialCustomerData);
					int value3 = MaxQuantity.Get(specialCustomerData);
					if (BaseQuantity.Exists)
					{
						BaseQuantity.Set(specialCustomerData, num3);
					}
					PerRank.Set(specialCustomerData, num5);
					MaxQuantity.Set(specialCustomerData, num4);
					if (PatchDiagnostics.Enabled)
					{
						Melon<Core>.Logger.Msg($"[diag] BuyLimitFieldSync [{groupId}]: base {value} -> {num3}, perRank {value2} -> {num5}, max {value3} -> {num4} (relationship x{GroupRelationships.GetMultiplier(groupId):0.00}; Kingpin limit = {Math.Min(num3 + num5 * num, num4)})");
					}
				}
			}
			catch (Exception ex)
			{
				Melon<Core>.Logger.Error("BuyLimitFieldSync failed: " + ex);
			}
		}
	}
	internal static class DialogueChoiceUtil
	{
		public static void Add(DialogueController controller, string text, Func<bool> shouldShow, Action onChosen)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			DialogueChoice val = new DialogueChoice();
			val.ChoiceText = text;
			val.Enabled = true;
			Func<bool, bool> func = (bool enabled) => enabled && shouldShow();
			val.shouldShowCheck = DelegateSupport.ConvertDelegate<ShouldShowCheck>((Delegate)func);
			val.onChoosen.AddListener(DelegateSupport.ConvertDelegate<UnityAction>((Delegate)onChosen));
			controller.AddDialogueChoice(val, 0);
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerLeader), "SetCustomerData")]
	internal class HippiesQuestChoicePatch
	{
		private static readonly JobDialogue Dialogue = new JobDialogue
		{
			LeaderId = "krystal_bird",
			AskText = "Can I do anything for the group?",
			HandInText = "The package has been delivered.",
			AskSeconds = 8f,
			Job = HippiesQuest.Job,
			Lines = new JobLines
			{
				OfferLine = (string _) => "Oh, bless you. There's a little something that needs to get to a friend, and we can't be seen with it. I've left it at a drop. Take it to the other one, and be gentle with it.",
				NoJobLines = new string[3] { "The universe has been quiet today, friend. Come back when it speaks.", "Not right now, sweetheart. Go enjoy the sunshine.", "Nothing this time. Everything is as it should be." },
				AlreadyDoneLine = "You've done so much already. We're grateful. Rest a while.",
				HandInLine = "Wonderful. The garden will be so happy. Please, take these, with our love."
			}
		};

		public static void Postfix(SpecialCustomerLeader __instance)
		{
			Dialogue.Register(__instance);
		}
	}
	internal sealed class JobDialogue
	{
		public string LeaderId;

		public string AskText;

		public string HandInText;

		public JobController Job;

		public JobLines Lines;

		public float AskSeconds = 7f;

		public float HandInSeconds = 6f;

		private SpecialCustomerLeader _leader;

		private readonly HashSet<int> _added = new HashSet<int>();

		public void Register(SpecialCustomerLeader leader)
		{
			_leader = leader;
			DialogueController component = ((Component)((NPC)leader).DialogueHandler).GetComponent<DialogueController>();
			if (!((Object)(object)component == (Object)null) && _added.Add(((Object)component).GetInstanceID()))
			{
				DialogueChoiceUtil.Add(component, AskText, () => ((NPC)leader).ID == LeaderId && Job.CanAsk(), OnAsk);
				DialogueChoiceUtil.Add(component, HandInText, () => ((NPC)leader).ID == LeaderId && Job.IsReadyToHandIn(), OnHandIn);
				MelonLogger.Msg("[quest] added the quest choices for leader '" + LeaderId + "'");
			}
		}

		private void OnAsk()
		{
			if (!((Object)(object)_leader == (Object)null))
			{
				SpecialCustomerData currentGroupData = NetworkSingleton<SpecialCustomerManager>.Instance.CurrentGroupData;
				string text = Job.Ask((currentGroupData != null) ? currentGroupData.GroupId : null, Lines);
				if (text != null)
				{
					((NPC)_leader).DialogueHandler.ShowWorldspaceDialogue(text, AskSeconds);
				}
			}
		}

		private void OnHandIn()
		{
			if ((Object)(object)_leader != (Object)null)
			{
				((NPC)_leader).DialogueHandler.ShowWorldspaceDialogue(Lines.HandInLine, HandInSeconds);
			}
			Job.HandIn();
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerLeader), "SetCustomerData")]
	internal class PartyBusQuestChoicePatch
	{
		private static readonly JobDialogue Dialogue = new JobDialogue
		{
			LeaderId = "chad_chaddington",
			AskText = "Need a hand with anything?",
			HandInText = "The word is out.",
			AskSeconds = 8f,
			Job = PartyBusQuest.Job,
			Lines = new JobLines
			{
				OfferLine = (string _) => $"Dude, yes! We need people to know the bus is in town and tonight is ON. Go tell {5} people. Some will say no. Their loss. Keep going!",
				NoJobLines = new string[3] { "We're good for now, bro. The vibe is already unreal.", "Nothing right now! Just enjoy the party.", "Not today, my guy. We got it handled." },
				AlreadyDoneLine = "You already got the word out, legend! Go have some fun.",
				HandInLine = "LEGEND! The crowd is gonna be insane. Here, you earned it!"
			}
		};

		public static void Postfix(SpecialCustomerLeader __instance)
		{
			Dialogue.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(DialogueController), "Start")]
	internal class PartyInvitePatch
	{
		private const string InviteText = "The Party Bus is in town tonight. You should come!";

		private static readonly string[] YesLines = new string[4] { "A party bus? Count me in!", "Sounds like a good time. I'll be there.", "Finally, something fun around here.", "Oh, nice. I'll swing by." };

		private static readonly string[] NoLines = new string[4] { "Not really my scene, sorry.", "I've got stuff to do tonight.", "No thanks, I'm good.", "Parties aren't for me." };

		public static void Postfix(DialogueController __instance)
		{
			NPC npc = ((Component)__instance).GetComponentInParent<NPC>();
			if (!((Object)(object)npc == (Object)null) && IsInvitable(npc))
			{
				DialogueChoiceUtil.Add(__instance, "The Party Bus is in town tonight. You should come!", () => PartyTask.CanInvite(npc.ID), delegate
				{
					OnInvite(npc);
				});
			}
		}

		private static bool IsInvitable(NPC npc)
		{
			if ((Object)(object)((Component)npc).GetComponent<SpecialCustomerLeader>() != (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)npc).GetComponent<SpecialCustomer>() != (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)npc).GetComponent<PoliceOfficer>() != (Object)null)
			{
				return false;
			}
			return npc.ID != "bikers_informant" && npc.ID != "businessmen_auditor";
		}

		private static void OnInvite(NPC npc)
		{
			string[] array = (PartyTask.Invite(npc.ID) ? YesLines : NoLines);
			npc.DialogueHandler.ShowWorldspaceDialogue(array[Random.Range(0, array.Length)], 5f);
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerData), "GetBuyQuantityForPlayerRank")]
	public class MaxBuyPatch
	{
		public const int NewMaxBuyQuantity = 200;

		private const bool DebugLogging = false;

		public static bool Prefix(SpecialCustomerData __instance, ERank rank, ref int __result)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected I4, but got Unknown
			PatchDiagnostics.Hit("MaxBuyPatch (rank-based buy limit)");
			int baseBuyQuantity = __instance.BaseBuyQuantity;
			int num = 10;
			int num2 = Math.Max(0, Math.Min((int)rank, num));
			__result = baseBuyQuantity + (200 - baseBuyQuantity) * num2 / num;
			return false;
		}

		[HarmonyPriority(0)]
		public static void Postfix(SpecialCustomerData __instance, ERank rank, int __result)
		{
			bool flag = true;
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerData), "GetBuyQuantityForPlayerRank")]
	public class RelationshipBuyQuantityPatch
	{
		public static void Postfix(SpecialCustomerData __instance, ref int __result)
		{
			PatchDiagnostics.Hit("RelationshipBuyQuantityPatch (relationship multiplier)");
			float multiplier = GroupRelationships.GetMultiplier(__instance.GroupId);
			__result = Math.Max(1, (int)Math.Round((float)__result * multiplier));
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerManager), "DealCompleted_Client")]
	public class RelationshipGainPatch
	{
		public static void Postfix(SpecialCustomerManager __instance, SpecialCustomerDealReceipt dealRecipe)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			PatchDiagnostics.Hit("RelationshipGainPatch (sale completed)");
			SpecialCustomerData currentGroupData = __instance.CurrentGroupData;
			SpecialCustomerLeader val = ((dealRecipe != null) ? dealRecipe.Leader : null);
			if (!((Object)(object)currentGroupData == (Object)null) && !((Object)(object)val == (Object)null) && dealRecipe.Items != null)
			{
				int num = 0;
				Enumerator<ItemInstance> enumerator = dealRecipe.Items.GetEnumerator();
				while (enumerator.MoveNext())
				{
					ItemInstance current = enumerator.Current;
					num += ((BaseItemInstance)current).GetTotalAmount();
				}
				int num2 = val.StartingBuyQuantity;
				if (num2 <= 0)
				{
					num2 = currentGroupData.GetBuyQuantityForPlayerRank(NetworkSingleton<LevelManager>.Instance.Rank);
				}
				if (num2 > 0)
				{
					GroupRelationships.RegisterSale(currentGroupData.GroupId, num, num2);
				}
			}
		}
	}
	[HarmonyPatch(typeof(SpecialCustomer), "SetGroupMates")]
	internal class RelationshipLossPatch
	{
		private static readonly HashSet<int> Subscribed = new HashSet<int>();

		private static float _lastPunchTime = -100f;

		private static float _lastKillTime = -100f;

		private static float _lastAimTime = -100f;

		private static float _lastPickpocketTime = -100f;

		public static void Postfix(SpecialCustomer __instance)
		{
			PatchDiagnostics.Hit("RelationshipLossPatch (subscribing to NPC reactions)");
			if (Subscribed.Add(((Object)__instance).GetInstanceID()))
			{
				NPCResponses responses = ((NPC)__instance).Responses;
				responses.OnNonLethallyAttackedByPlayer = AddHandler(responses.OnNonLethallyAttackedByPlayer, OnPunched);
				responses.OnRepeatedlyNonLethallyAttackedByPlayer = AddHandler(responses.OnRepeatedlyNonLethallyAttackedByPlayer, OnPunched);
				responses.OnLethallyAttackedByPlayer = AddHandler(responses.OnLethallyAttackedByPlayer, OnKilledSomeone);
				responses.OnAimedAtByPlayer = AddHandler(responses.OnAimedAtByPlayer, OnAimedAt);
				responses.OnPickpocketFailed = AddHandler(responses.OnPickpocketFailed, OnPickpocketFailed);
			}
		}

		private static Action<Player> AddHandler(Action<Player> existing, Action<Player> handler)
		{
			Action<Player> val = DelegateSupport.ConvertDelegate<Action<Player>>((Delegate)handler);
			return ((Il2CppObjectBase)Delegate.Combine((Delegate)(object)existing, (Delegate)(object)val)).Cast<Action<Player>>();
		}

		private static void OnPunched(Player attacker)
		{
			if (InstanceFinder.IsServer && !(Time.time - _lastPunchTime < 20f))
			{
				SpecialCustomerData currentGroupData = NetworkSingleton<SpecialCustomerManager>.Instance.CurrentGroupData;
				if (!((Object)(object)currentGroupData == (Object)null))
				{
					_lastPunchTime = Time.time;
					GroupRelationships.Penalize(currentGroupData.GroupId, 0.25f, "punched");
				}
			}
		}

		private static void OnKilledSomeone(Player attacker)
		{
			if (InstanceFinder.IsServer && !(Time.time - _lastKillTime < 20f))
			{
				SpecialCustomerData currentGroupData = NetworkSingleton<SpecialCustomerManager>.Instance.CurrentGroupData;
				if (!((Object)(object)currentGroupData == (Object)null))
				{
					_lastKillTime = Time.time;
					GroupRelationships.Penalize(currentGroupData.GroupId, 1f, "customer killed");
				}
			}
		}

		private static void OnAimedAt(Player attacker)
		{
			if (InstanceFinder.IsServer && !(Time.time - _lastAimTime < 20f))
			{
				SpecialCustomerData currentGroupData = NetworkSingleton<SpecialCustomerManager>.Instance.CurrentGroupData;
				if (!((Object)(object)currentGroupData == (Object)null))
				{
					_lastAimTime = Time.time;
					GroupRelationships.Penalize(currentGroupData.GroupId, 0.25f, "aimed weapon");
				}
			}
		}

		private static void OnPickpocketFailed(Player thief)
		{
			if (InstanceFinder.IsServer && !(Time.time - _lastPickpocketTime < 20f))
			{
				SpecialCustomerData currentGroupData = NetworkSingleton<SpecialCustomerManager>.Instance.CurrentGroupData;
				if (!((Object)(object)currentGroupData == (Object)null))
				{
					_lastPickpocketTime = Time.time;
					GroupRelationships.Penalize(currentGroupData.GroupId, 0.25f, "pickpocket failed");
				}
			}
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerManager), "GetSaveString")]
	public class RelationshipSavePatch
	{
		public static void Postfix(ref string __result)
		{
			PatchDiagnostics.Hit("RelationshipSavePatch (writing save)");
			try
			{
				int num = __result.LastIndexOf('}');
				if (num >= 0)
				{
					__result = __result.Substring(0, num).TrimEnd() + ",\n    " + GroupRelationships.ToJsonProperty() + "\n}";
				}
			}
			catch (Exception ex)
			{
				Melon<Core>.Logger.Error("Failed to add relationships to the save string: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerLoader), "Load")]
	public class RelationshipLoadPatch
	{
		public static void Prefix(string mainPath)
		{
			PatchDiagnostics.Hit("RelationshipLoadPatch (reading save)");
			try
			{
				string path = mainPath + ".json";
				GroupRelationships.LoadFromJson(File.Exists(path) ? File.ReadAllText(path) : null);
			}
			catch (Exception ex)
			{
				GroupRelationships.Clear();
				Melon<Core>.Logger.Error("Failed to load relationships: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerManager), "RunArrivalPhase")]
	public class RelationshipArrivalPatch
	{
		private static readonly FieldInfo CurrentDataField = AccessTools.Field(typeof(SpecialCustomerManager), "_currentData");

		private static readonly PropertyInfo CurrentDataProperty = AccessTools.Property(typeof(SpecialCustomerManager), "_currentData");

		private static bool _reportedMissing;

		private static string GetArrivingGroupId(SpecialCustomerManager manager)
		{
			object obj = ((CurrentDataField != null) ? CurrentDataField.GetValue(manager) : CurrentDataProperty?.GetValue(manager));
			if (obj == null && CurrentDataField == null && CurrentDataProperty == null && !_reportedMissing)
			{
				_reportedMissing = true;
				Melon<Core>.Logger.Error("RelationshipArrivalPatch: can't find SpecialCustomerManager._currentData as a field or property; visits will NOT be tracked. See the [diag] Probe lines.");
			}
			object obj2 = ((obj is SpecialCustomerSaveData) ? obj : null);
			return (obj2 != null) ? ((SpecialCustomerSaveData)obj2).GroupId : null;
		}

		public static void Prefix(SpecialCustomerManager __instance)
		{
			PatchDiagnostics.Hit("RelationshipArrivalPatch (group arriving)");
			string arrivingGroupId = GetArrivingGroupId(__instance);
			if (!string.IsNullOrEmpty(arrivingGroupId))
			{
				GroupRelationships.BeginVisit(arrivingGroupId);
				BuyLimitFieldSync.Apply(__instance, arrivingGroupId);
			}
		}
	}
	[HarmonyPatch(typeof(SpecialCustomerManager), "RemoveCustomerGroup")]
	public class RelationshipDeparturePatch
	{
		public static void Prefix(SpecialCustomerManager __instance)
		{
			PatchDiagnostics.Hit("RelationshipDeparturePatch (group leaving)");
			SpecialCustomerData currentGroupData = __instance.CurrentGroupData;
			if (!((Object)(object)currentGroupData == (Object)null))
			{
				GroupRelationships.EndVisit(currentGroupData.GroupId);
				JobController.OnGroupLeft(currentGroupData.GroupId);
			}
		}
	}
}
namespace BetterSpecialCustomers.Diagnostics
{
	public static class PatchDiagnostics
	{
		public static readonly bool Enabled = false;

		private static readonly HashSet<string> Seen = new HashSet<string>();

		public static void Hit(string patchName)
		{
			if (Enabled && Seen.Add(patchName))
			{
				Melon<Core>.Logger.Msg("[diag] first call: " + patchName + " (the game DID call this patch)");
			}
		}

		public static void PatchAllClasses(Harmony harmony)
		{
			IEnumerable<Type> enumerable = from t in typeof(Core).Assembly.GetTypes()
				where t.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0
				orderby t.Name
				select t;
			int num = 0;
			int num2 = 0;
			foreach (Type item in enumerable)
			{
				try
				{
					harmony.CreateClassProcessor(item).Patch();
					num++;
					if (Enabled)
					{
						Melon<Core>.Logger.Msg("[diag] Patch OK:     " + item.Name);
					}
				}
				catch (Exception ex)
				{
					num2++;
					Exception ex2 = ex;
					while (ex2.InnerException != null)
					{
						ex2 = ex2.InnerException;
					}
					Melon<Core>.Logger.Error($"[diag] Patch FAILED: {item.Name} -> {ex2.GetType().Name}: {ex2.Message}");
				}
			}
			Melon<Core>.Logger.Msg($"[diag] Patches applied: {num} ok, {num2} failed.");
		}

		public static void Probe()
		{
			if (Enabled)
			{
				Melon<Core>.Logger.Msg($"[diag] Build: {"IL2CPP"}. Harmony {typeof(Harmony).Assembly.GetName().Version}");
				ProbeMethod(typeof(SpecialCustomerData), "GetBuyQuantityForPlayerRank");
				ProbeMethod(typeof(SpecialCustomerManager), "DealCompleted_Client");
				ProbeMethod(typeof(SpecialCustomerManager), "GetSaveString");
				ProbeMethod(typeof(SpecialCustomerManager), "RunArrivalPhase");
				ProbeMethod(typeof(SpecialCustomerManager), "RemoveCustomerGroup");
				ProbeMethod(typeof(SpecialCustomerLoader), "Load");
				ProbeMembers(typeof(SpecialCustomerManager), "currentData");
				ProbeMembers(typeof(SpecialCustomerData), "baseBuyQuantity");
				ProbeMembers(typeof(SpecialCustomerData), "additionalBuyQuantityPerRank");
				ProbeMembers(typeof(SpecialCustomerData), "maxBuyQuantity");
			}
		}

		private static void ProbeMembers(Type type, string namePart)
		{
			MemberInfo[] array = (from m in type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
				where m.Name.IndexOf(namePart, StringComparison.OrdinalIgnoreCase) >= 0
				select m).ToArray();
			if (array.Length == 0)
			{
				Melon<Core>.Logger.Warning($"[diag] Probe: {type.Name} has NO member containing '{namePart}'.");
			}
			MemberInfo[] array2 = array;
			foreach (MemberInfo memberInfo in array2)
			{
				Melon<Core>.Logger.Msg($"[diag] Probe: {type.Name}.{memberInfo.Name} is a {memberInfo.MemberType}");
			}
		}

		private static void ProbeMethod(Type type, string name)
		{
			MethodBase methodBase = AccessTools.Method(type, name, (Type[])null, (Type[])null);
			if (methodBase == null)
			{
				Melon<Core>.Logger.Warning($"[diag] Probe: {type.Name}.{name} NOT FOUND");
			}
			else
			{
				Melon<Core>.Logger.Msg($"[diag] Probe: {type.Name}.{name} found ({(methodBase.IsPublic ? "public" : "non-public")})");
			}
		}

		public static void ListPatchedMethods(Harmony harmony)
		{
			if (!Enabled)
			{
				return;
			}
			foreach (MethodBase patchedMethod in harmony.GetPatchedMethods())
			{
				Melon<Core>.Logger.Msg("[diag] Harmony reports patched: " + patchedMethod.DeclaringType?.Name + "." + patchedMethod.Name);
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		internal IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}