Decompiled source of RVRepairVan v2.6.2

RVRepairVan.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
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;
using DooDesch.Localization;
using DooDesch.Transition;
using HarmonyLib;
using Il2CppFishNet;
using Il2CppFishNet.Connection;
using Il2CppFishNet.Managing;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne.Audio;
using Il2CppScheduleOne.Core.Items.Framework;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Dialogue;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.Money;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.NPCs.CharacterClasses;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.Product;
using Il2CppScheduleOne.Property;
using Il2CppScheduleOne.Quests;
using Il2CppScheduleOne.UI;
using Il2CppScheduleOne.VoiceOver;
using Il2CppSystem.Collections.Generic;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using RVRepairVan;
using RVRepairVan.Config;
using RVRepairVan.Dialogue;
using RVRepairVan.Effects;
using RVRepairVan.Localization;
using RVRepairVan.Managers;
using RVRepairVan.Net;
using RVRepairVan.Net.Patches;
using RVRepairVan.Persistence;
using RVRepairVan.Quests;
using S1API.DeadDrops;
using S1API.Entities;
using S1API.Entities.Dialogue;
using S1API.Internal.Abstraction;
using S1API.Items;
using S1API.Items.Storable;
using S1API.Leveling;
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.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "RVRepairVan", "2.6.2", "DooDesch", "https://github.com/DooDesch-Mods/RVRepairVan")]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("DooDesch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © DooDesch")]
[assembly: AssemblyFileVersion("2.6.2.0")]
[assembly: AssemblyInformationalVersion("2.6.2+15f23afa4cee5b0c11188c792d68d9756cbf5378")]
[assembly: AssemblyProduct("RVRepairVan")]
[assembly: AssemblyTitle("RVRepairVan")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.6.2.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 DooDesch.Localization
{
	internal static class L10n
	{
		private static readonly Dictionary<string, Dictionary<string, string>> _tables = new Dictionary<string, Dictionary<string, string>>();

		private static Dictionary<string, string> _active;

		private static string _lang;

		internal static string Language => _lang ?? (_lang = Detect());

		private static string ModName => typeof(L10n).Assembly.GetName().Name;

		internal static void Register(string lang, Dictionary<string, string> table)
		{
			_tables[lang] = table;
			_active = null;
		}

		internal static string T(string en)
		{
			if (_active == null)
			{
				_active = BuildActiveTable();
			}
			if (!_active.TryGetValue(en, out var value))
			{
				return en;
			}
			return value;
		}

		internal static string T(string en, params object[] args)
		{
			return string.Format(T(en), args);
		}

		private static Dictionary<string, string> BuildActiveTable()
		{
			Dictionary<string, string> value;
			Dictionary<string, string> dictionary = (_tables.TryGetValue(Language, out value) ? new Dictionary<string, string>(value) : new Dictionary<string, string>());
			try
			{
				ExportTemplate();
				Overlay(dictionary, Path.Combine(PackDir(), Language + ".json"));
				Overlay(dictionary, Path.Combine(UserDir(), Language + ".json"));
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[L10n] user translations failed: " + ex.Message);
			}
			return dictionary;
		}

		private static void Overlay(Dictionary<string, string> table, string file)
		{
			if (!File.Exists(file))
			{
				return;
			}
			Dictionary<string, string> dictionary = ParseJsonObject(File.ReadAllText(file));
			if (dictionary == null)
			{
				MelonLogger.Warning("[L10n] ignoring invalid translation file (flat JSON object of \"english\": \"translated\" strings expected): " + file);
				return;
			}
			foreach (KeyValuePair<string, string> item in dictionary)
			{
				table[item.Key] = item.Value;
			}
		}

		private static string PackDir()
		{
			return Path.Combine(MelonEnvironment.ModsDirectory, "Localization", ModName);
		}

		private static string UserDir()
		{
			return Path.Combine(MelonEnvironment.UserDataDirectory, "DooDesch", "Localization", ModName);
		}

		private static void ExportTemplate()
		{
			if (_tables.Count == 0)
			{
				return;
			}
			SortedSet<string> sortedSet = new SortedSet<string>(StringComparer.Ordinal);
			foreach (Dictionary<string, string> value in _tables.Values)
			{
				foreach (string key in value.Keys)
				{
					sortedSet.Add(key);
				}
			}
			if (sortedSet.Count == 0)
			{
				return;
			}
			Directory.CreateDirectory(UserDir());
			WriteTemplate("en", sortedSet, null);
			foreach (string key2 in _tables.Keys)
			{
				if (key2 != "en")
				{
					WriteTemplate(key2, sortedSet, _tables[key2]);
				}
			}
		}

		private static void WriteTemplate(string lang, SortedSet<string> keys, Dictionary<string, string> table)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{\n  ").Append(Quote("_readme")).Append(": ")
				.Append(Quote("This file is regenerated on every game start - do not edit it. To change or add a translation, copy it to <language code>.json (for example " + lang + ".json), keep only the lines you want to change, and translate the VALUES only - the keys are the mod's English source strings and must stay unchanged. Keep placeholders like {0}. Set Language in MelonPreferences.cfg under [DooDesch] to force a language (auto = OS language). To share your translation as an installable mod, see https://github.com/DooDesch-Mods/ScheduleOne-L10n/wiki"))
				.Append(",\n");
			foreach (string key in keys)
			{
				string value;
				string s = ((table != null && table.TryGetValue(key, out value)) ? value : key);
				stringBuilder.Append("  ").Append(Quote(key)).Append(": ")
					.Append(Quote(s))
					.Append(",\n");
			}
			stringBuilder.Length -= 2;
			stringBuilder.Append("\n}\n");
			File.WriteAllText(Path.Combine(UserDir(), "_template." + lang + ".json"), stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		private static string Detect()
		{
			//IL_0079: 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_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Invalid comparison between Unknown and I4
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Invalid comparison between Unknown and I4
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Invalid comparison between Unknown and I4
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Invalid comparison between Unknown and I4
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Expected I4, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Invalid comparison between Unknown and I4
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Invalid comparison between Unknown and I4
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Invalid comparison between Unknown and I4
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Invalid comparison between Unknown and I4
			string text = "auto";
			try
			{
				MelonPreferences_Category val = MelonPreferences.CreateCategory("DooDesch", "DooDesch Mods");
				text = ((val.GetEntry<string>("Language") ?? val.CreateEntry<string>("Language", "auto", "Language", "Language for all DooDesch mods: auto (= OS language), en, de, ...", false, false, (ValueValidator)null, (string)null)).Value ?? "auto").Trim().ToLowerInvariant();
			}
			catch
			{
			}
			if (text.Length > 0 && text != "auto")
			{
				return text;
			}
			SystemLanguage systemLanguage = Application.systemLanguage;
			if ((int)systemLanguage <= 15)
			{
				if ((int)systemLanguage == 6)
				{
					goto IL_0121;
				}
				if ((int)systemLanguage == 14)
				{
					return "fr";
				}
				if ((int)systemLanguage == 15)
				{
					return "de";
				}
			}
			else if ((int)systemLanguage <= 34)
			{
				switch (systemLanguage - 21)
				{
				default:
					if ((int)systemLanguage != 34)
					{
						break;
					}
					return "es";
				case 0:
					return "it";
				case 7:
					return "pt";
				case 6:
					return "pl";
				case 9:
					return "ru";
				case 1:
					return "ja";
				case 2:
					return "ko";
				case 3:
				case 4:
				case 5:
				case 8:
					break;
				}
			}
			else
			{
				if ((int)systemLanguage == 37)
				{
					return "tr";
				}
				if (systemLanguage - 40 <= 1)
				{
					goto IL_0121;
				}
			}
			return "en";
			IL_0121:
			return "zh";
		}

		private static Dictionary<string, string> ParseJsonObject(string json)
		{
			if (json == null)
			{
				return null;
			}
			int i = 0;
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			SkipWs(json, ref i);
			if (i >= json.Length || json[i] != '{')
			{
				return null;
			}
			i++;
			SkipWs(json, ref i);
			if (i < json.Length && json[i] == '}')
			{
				return dictionary;
			}
			while (true)
			{
				string text = ParseString(json, ref i);
				if (text == null)
				{
					return null;
				}
				SkipWs(json, ref i);
				if (i >= json.Length || json[i] != ':')
				{
					return null;
				}
				i++;
				SkipWs(json, ref i);
				string text2 = ParseString(json, ref i);
				if (text2 == null)
				{
					return null;
				}
				dictionary[text] = text2;
				SkipWs(json, ref i);
				if (i >= json.Length)
				{
					return null;
				}
				if (json[i] != ',')
				{
					break;
				}
				i++;
				SkipWs(json, ref i);
				if (i < json.Length && json[i] == '}')
				{
					return dictionary;
				}
			}
			if (json[i] != '}')
			{
				return null;
			}
			return dictionary;
		}

		private static void SkipWs(string s, ref int i)
		{
			while (i < s.Length && (char.IsWhiteSpace(s[i]) || s[i] == '\ufeff'))
			{
				i++;
			}
		}

		private static string ParseString(string s, ref int i)
		{
			SkipWs(s, ref i);
			if (i >= s.Length || s[i] != '"')
			{
				return null;
			}
			i++;
			StringBuilder stringBuilder = new StringBuilder();
			while (i < s.Length)
			{
				char c = s[i++];
				switch (c)
				{
				case '"':
					return stringBuilder.ToString();
				default:
					stringBuilder.Append(c);
					break;
				case '\\':
					if (i >= s.Length)
					{
						return null;
					}
					switch (s[i++])
					{
					case '"':
						stringBuilder.Append('"');
						break;
					case '\\':
						stringBuilder.Append('\\');
						break;
					case '/':
						stringBuilder.Append('/');
						break;
					case 'b':
						stringBuilder.Append('\b');
						break;
					case 'f':
						stringBuilder.Append('\f');
						break;
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'u':
						if (i + 4 > s.Length)
						{
							return null;
						}
						try
						{
							stringBuilder.Append((char)Convert.ToInt32(s.Substring(i, 4), 16));
						}
						catch
						{
							return null;
						}
						i += 4;
						break;
					default:
						return null;
					}
					break;
				}
			}
			return null;
		}

		private static string Quote(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length + 2);
			stringBuilder.Append('"');
			foreach (char c in s)
			{
				switch (c)
				{
				case '"':
					stringBuilder.Append("\\\"");
					continue;
				case '\\':
					stringBuilder.Append("\\\\");
					continue;
				case '\n':
					stringBuilder.Append("\\n");
					continue;
				case '\r':
					stringBuilder.Append("\\r");
					continue;
				case '\t':
					stringBuilder.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder2 = stringBuilder.Append("\\u");
					int num = c;
					stringBuilder2.Append(num.ToString("x4"));
				}
				else
				{
					stringBuilder.Append(c);
				}
			}
			stringBuilder.Append('"');
			return stringBuilder.ToString();
		}
	}
}
namespace DooDesch.Transition
{
	internal static class Veil
	{
		internal static class Ease
		{
			public static float In(float t)
			{
				t = Mathf.Clamp01(t);
				return t * t * t;
			}

			public static float Out(float t)
			{
				t = Mathf.Clamp01(t);
				float num = 1f - t;
				return 1f - num * num * num;
			}

			public static float InOut(float t)
			{
				t = Mathf.Clamp01(t);
				return t * t * (3f - 2f * t);
			}
		}

		internal static bool EyelidsAvailable
		{
			get
			{
				try
				{
					return Singleton<EyelidOverlay>.InstanceExists;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static bool BlackAvailable
		{
			get
			{
				try
				{
					return Singleton<BlackOverlay>.InstanceExists || Singleton<HUD>.InstanceExists;
				}
				catch
				{
					return false;
				}
			}
		}

		private static EyelidOverlay Eyes()
		{
			try
			{
				return Singleton<EyelidOverlay>.InstanceExists ? Singleton<EyelidOverlay>.Instance : null;
			}
			catch
			{
				return null;
			}
		}

		internal static void SetEyelids(float openness)
		{
			EyelidOverlay val = Eyes();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				val.AutoUpdate = false;
				val.SetOpen(Mathf.Clamp01(openness));
			}
			catch
			{
			}
		}

		internal static float CurrentEyelidOpen(float fallback = 1f)
		{
			EyelidOverlay val = Eyes();
			if ((Object)(object)val == (Object)null)
			{
				return fallback;
			}
			try
			{
				return val.CurrentOpen;
			}
			catch
			{
				return fallback;
			}
		}

		internal static void ReleaseEyelids()
		{
			EyelidOverlay val = Eyes();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				val.AutoUpdate = true;
			}
			catch
			{
			}
		}

		internal static float EyelidTargetOpen()
		{
			return 1f;
		}

		internal static IEnumerator CloseEyelids(float dur)
		{
			float start = CurrentEyelidOpen();
			if (dur <= 0f)
			{
				SetEyelids(0f);
				yield break;
			}
			for (float t = 0f; t < dur; t += Time.deltaTime)
			{
				SetEyelids(Mathf.Lerp(start, 0f, Ease.InOut(t / dur)));
				yield return null;
			}
			SetEyelids(0f);
		}

		internal static IEnumerator OpenEyelidsToTarget(float dur)
		{
			float target = EyelidTargetOpen();
			if (dur <= 0f)
			{
				SetEyelids(target);
				ReleaseEyelids();
				yield break;
			}
			for (float t = 0f; t < dur; t += Time.deltaTime)
			{
				SetEyelids(Mathf.Lerp(0f, target, Ease.Out(t / dur)));
				yield return null;
			}
			SetEyelids(target);
			ReleaseEyelids();
		}

		internal static IEnumerator FadeEyelids(float target, float dur, bool releaseToGame)
		{
			if ((Object)(object)Eyes() == (Object)null)
			{
				yield break;
			}
			float start = CurrentEyelidOpen(target);
			if (dur > 0f)
			{
				for (float t = 0f; t < dur; t += Time.deltaTime)
				{
					SetEyelids(Mathf.Lerp(start, target, Ease.InOut(t / dur)));
					yield return null;
				}
			}
			SetEyelids(target);
			if (releaseToGame)
			{
				ReleaseEyelids();
			}
		}

		internal static void BlackOpen(float t)
		{
			Black(toBlack: true, t);
		}

		internal static void BlackClose(float t)
		{
			Black(toBlack: false, t);
		}

		private static void Black(bool toBlack, float t)
		{
			try
			{
				if (Singleton<BlackOverlay>.InstanceExists)
				{
					BlackOverlay instance = Singleton<BlackOverlay>.Instance;
					if (toBlack)
					{
						instance.Open(t);
					}
					else
					{
						instance.Close(t);
					}
				}
				else if (Singleton<HUD>.InstanceExists)
				{
					Singleton<HUD>.Instance.SetBlackOverlayVisible(toBlack, t);
				}
			}
			catch (Exception ex)
			{
				ScreenTransition.Warn?.Invoke("Veil.Black failed: " + ex.Message);
			}
		}

		internal static void SetCanMove(bool canMove)
		{
			try
			{
				PlayerMovement instance = PlayerSingleton<PlayerMovement>.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.CanMove = canMove;
				}
			}
			catch
			{
			}
		}

		internal static void SetCanLook(bool canLook)
		{
			try
			{
				PlayerCamera instance = PlayerSingleton<PlayerCamera>.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.SetCanLook(canLook);
				}
			}
			catch
			{
			}
		}
	}
	internal enum VeilMode
	{
		Eyelids,
		Black,
		EyelidsAndBlack
	}
	internal struct TransitionRequest
	{
		public VeilMode Mechanism;

		public float CloseSeconds;

		public float OpenSeconds;

		public float HoldSeconds;

		public bool LockMovement;

		public bool LockCamera;

		public Action DuringBlack;

		public Action OnMidHold;

		public Action OnDone;

		public Func<bool> WaitUntilReady;

		public float WaitDeadlineSeconds;

		public Action WhileWaiting;

		public Func<bool> KeepClosedAfter;

		public string Key;
	}
	internal static class ScreenTransition
	{
		public static Action<string> Warn = delegate(string msg)
		{
			try
			{
				MelonLogger.Warning(msg);
			}
			catch
			{
			}
		};

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

		public static bool IsRunning(string key)
		{
			return Active.Contains(Bucket(key));
		}

		public static bool Play(TransitionRequest req)
		{
			string text = Bucket(req.Key);
			if (Active.Contains(text))
			{
				return false;
			}
			bool flag = req.Mechanism == VeilMode.Eyelids || req.Mechanism == VeilMode.EyelidsAndBlack;
			bool flag2 = req.Mechanism == VeilMode.Black || req.Mechanism == VeilMode.EyelidsAndBlack;
			if ((!flag || !Veil.EyelidsAvailable) && (!flag2 || !Veil.BlackAvailable))
			{
				Safe(req.DuringBlack);
				Safe(req.OnDone);
				return true;
			}
			Active.Add(text);
			try
			{
				MelonCoroutines.Start(Run(req, text, flag, flag2));
			}
			catch (Exception ex)
			{
				Warn?.Invoke("ScreenTransition.Play start failed: " + ex.Message);
				Active.Remove(text);
				Safe(req.DuringBlack);
				Safe(req.OnDone);
			}
			return true;
		}

		public static void ForceReset(string key = null)
		{
			if (key == null)
			{
				Active.Clear();
			}
			else
			{
				Active.Remove(Bucket(key));
			}
			try
			{
				Veil.BlackClose(0f);
				Veil.SetEyelids(1f);
				Veil.ReleaseEyelids();
				Veil.SetCanMove(canMove: true);
				Veil.SetCanLook(canLook: true);
			}
			catch (Exception ex)
			{
				Warn?.Invoke("ScreenTransition.ForceReset failed: " + ex.Message);
			}
		}

		private static IEnumerator Run(TransitionRequest req, string key, bool eye, bool black)
		{
			bool lockedMove = false;
			bool lockedLook = false;
			try
			{
				if (req.LockMovement)
				{
					Veil.SetCanMove(canMove: false);
					lockedMove = true;
				}
				if (req.LockCamera)
				{
					Veil.SetCanLook(canLook: false);
					lockedLook = true;
				}
				if (black)
				{
					Veil.BlackOpen(req.CloseSeconds);
				}
				if (eye)
				{
					yield return Veil.CloseEyelids(req.CloseSeconds);
				}
				else
				{
					yield return Wait(req.CloseSeconds);
				}
				Safe(req.DuringBlack);
				yield return null;
				yield return null;
				if (req.WaitUntilReady != null)
				{
					float deadline = Time.time + Mathf.Max(0f, req.WaitDeadlineSeconds);
					while (Time.time < deadline && !SafeReady(req.WaitUntilReady))
					{
						Safe(req.WhileWaiting);
						yield return null;
					}
					Safe(req.WhileWaiting);
				}
				if (req.HoldSeconds > 0f)
				{
					float deadline = req.HoldSeconds * 0.5f;
					yield return Wait(deadline);
					Safe(req.OnMidHold);
					yield return Wait(req.HoldSeconds - deadline);
				}
				else
				{
					Safe(req.OnMidHold);
				}
				if (req.KeepClosedAfter == null || !SafeReady(req.KeepClosedAfter))
				{
					if (black)
					{
						Veil.BlackClose(req.OpenSeconds);
					}
					if (eye)
					{
						yield return Veil.OpenEyelidsToTarget(req.OpenSeconds);
					}
					else
					{
						yield return Wait(req.OpenSeconds);
					}
				}
				Safe(req.OnDone);
			}
			finally
			{
				bool flag = req.KeepClosedAfter != null && SafeReady(req.KeepClosedAfter);
				if (eye)
				{
					if (flag)
					{
						Veil.SetEyelids(0f);
					}
					else
					{
						Veil.SetEyelids(Veil.EyelidTargetOpen());
						Veil.ReleaseEyelids();
					}
				}
				if (black && !flag)
				{
					Veil.BlackClose(0.25f);
				}
				if (lockedMove)
				{
					Veil.SetCanMove(canMove: true);
				}
				if (lockedLook)
				{
					Veil.SetCanLook(canLook: true);
				}
				Active.Remove(key);
			}
		}

		private static string Bucket(string key)
		{
			if (!string.IsNullOrEmpty(key))
			{
				return key;
			}
			return "default";
		}

		private static IEnumerator Wait(float sec)
		{
			if (!(sec <= 0f))
			{
				for (float t = 0f; t < sec; t += Time.deltaTime)
				{
					yield return null;
				}
			}
		}

		private static bool SafeReady(Func<bool> f)
		{
			try
			{
				return f();
			}
			catch
			{
				return false;
			}
		}

		private static void Safe(Action a)
		{
			if (a == null)
			{
				return;
			}
			try
			{
				a();
			}
			catch (Exception ex)
			{
				Warn?.Invoke("ScreenTransition callback failed: " + ex.Message);
			}
		}
	}
}
namespace RVRepairVan
{
	public sealed class Core : MelonMod
	{
		public static Core Instance { get; private set; }

		public static Instance Log { get; private set; }

		[Conditional("DEBUG")]
		public static void LogDebug(string msg)
		{
			Instance log = Log;
			if (log != null)
			{
				log.Msg(msg);
			}
		}

		public override void OnInitializeMelon()
		{
			Instance = this;
			Log = ((MelonBase)this).LoggerInstance;
			RVRepairVanPreferences.Initialize();
			German.Register();
			((MelonBase)this).HarmonyInstance.PatchAll();
			NetworkBus.Init(((MelonBase)this).HarmonyInstance);
			Log.Msg($"RVRepairVan initialized. Enabled={RVRepairVanPreferences.Enabled}, Questline={RVRepairVanPreferences.QuestlineEnabled}, RepairPrice={RVRepairVanPreferences.RepairPrice}");
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			if (!(sceneName != "Main"))
			{
				RepairSave.BeginLoad();
				RVManager.Reset();
				RepairCinematic.ForceReset();
				Questline.InitNet();
				if (RVRepairVanPreferences.QuestlineEnabled)
				{
					Questline.Reset();
					Questline.Start();
				}
				else
				{
					MarcoRepairDialogue.Reset();
					MelonCoroutines.Start(MarcoRepairDialogue.SetupCoroutine());
				}
				MelonCoroutines.Start(RestoreRepairCoroutine());
			}
		}

		public override void OnPreferencesSaved()
		{
			try
			{
				MarcoRepairDialogue.RefreshPrice();
				Questline.RefreshPrice();
			}
			catch (Exception ex)
			{
				Log.Warning("[Prefs] OnPreferencesSaved failed: " + ex.Message);
			}
		}

		private static IEnumerator RestoreRepairCoroutine()
		{
			float waited = 0f;
			while (!RepairSave.Loaded && waited < 10f)
			{
				yield return (object)new WaitForSeconds(0.5f);
				waited += 0.5f;
			}
			yield return (object)new WaitForSeconds(2f);
			if (NetworkBus.Online && !NetworkBus.IsServer)
			{
				Log.Msg("[Restore] co-op client - deferring RV state to the host (no local restore).");
				yield break;
			}
			bool flag = false;
			try
			{
				flag = RVManager.TryLocate() && RepairStateStore.GetRepaired() && RVManager.IsDestroyed();
			}
			catch (Exception ex)
			{
				Log.Warning("[Restore] check failed: " + ex.Message);
			}
			if (flag)
			{
				Log.Msg("[Restore] RV was previously repaired - restoring without charge.");
				RVManager.Repair();
			}
		}
	}
}
namespace RVRepairVan.Quests
{
	internal static class Questline
	{
		private const int None = 0;

		private const int Started = 1;

		private const int AskedDonna = 2;

		private const int MingErrand = 3;

		private const int MingCrate = 4;

		private const int Referred = 5;

		private const int MarcoMet = 6;

		private const int ReadyToPay = 7;

		private const int Trusted = 8;

		private const int Paid = 9;

		private const int Done = 10;

		private const string DonnaId = "donna_martin";

		private const string MingId = "ming";

		private const string MarcoId = "marco_baron";

		private const string CrateId = "rv_ming_crate";

		private const string PackageId = "rv_marco_package";

		private const int LostPackageFee = 500;

		private const string MingAngry = "You lost it? I don't lose things, and people who lose my things lose teeth. Five hundred buys you both back. Now.";

		private const string MingPaid = "Smart. We're square. Now go see Marco at the body shop down by the docks, and tell him Mrs. Ming sent you.";

		private const string MingShort = "Then don't come back until your hands are full.";

		private const string MarcoAngry = "You did what? You walk in here empty-handed and waste my time. Five hundred, or the next thing that goes missing is you.";

		private const string MarcoPaid = "Good. Mess like that gets forgotten when the cash shows up. Bring me some of that good stuff now and then, and I'll keep shaving down the bill.";

		private const string MarcoShort = "Clock's running. Come back with it.";

		private const int DEGRADED_AFTER_ATTEMPTS = 15;

		private static bool _donnaDone;

		private static bool _mingDone;

		private static bool _marcoDone;

		private static bool _pickupActive;

		private static bool _hasPackage;

		private static Vector3 _dropPoint;

		private static DeadDropInstance _drop;

		private static Vector3 _cratePoint;

		private static DeadDropInstance _crateDrop;

		private static bool _cratePlaced;

		private static bool _pkgPlaced;

		private static bool _itemsRegistered;

		private static bool _clientCheckedRv;

		private static int _gen;

		private static Transform _donnaT;

		private static Transform _mingT;

		private static Transform _marcoT;

		private static DialogueController _donnaDC;

		private static DialogueController _mingDC;

		private static DialogueController _marcoDC;

		private static int _injectedChoices;

		private static int _skippedChoices;

		private static DialogueChoice _marcoRepairChoice;

		private const int MAX_SAMPLE_CHOICES = 8;

		private static readonly DialogueChoice[] _sampleChoices = (DialogueChoice[])(object)new DialogueChoice[8];

		private static readonly List<ItemSlot> _sampleSlots = new List<ItemSlot>();

		private static bool _itemsDumped;

		private static bool _netWired;

		private static int Stage
		{
			get
			{
				return RepairStateStore.GetStage();
			}
			set
			{
				RepairStateStore.SetStage(value);
				if (NetworkBus.Online && NetworkBus.IsServer)
				{
					NetworkBus.BroadcastToAll(RvOp.StageSync, value, DiscountTotal);
				}
			}
		}

		private static int Samples
		{
			get
			{
				return RepairStateStore.GetSamples();
			}
			set
			{
				RepairStateStore.SetSamples(value);
			}
		}

		private static int DiscountTotal
		{
			get
			{
				return RepairStateStore.GetDiscountTotal();
			}
			set
			{
				RepairStateStore.SetDiscountTotal(value);
				if (NetworkBus.Online && NetworkBus.IsServer)
				{
					NetworkBus.BroadcastToAll(RvOp.StageSync, Stage, value);
				}
			}
		}

		private static bool MarcoGreeted => Stage >= 6;

		private static bool ReferralUsed => Stage >= 7;

		private static bool Trusted_ => Stage >= 8;

		private static bool Active
		{
			get
			{
				if (RVRepairVanPreferences.Enabled)
				{
					if (!RVManager.IsDestroyed())
					{
						if (NetworkBus.Online && !NetworkBus.IsServer && Stage >= 1)
						{
							return Stage < 9;
						}
						return false;
					}
					return true;
				}
				return false;
			}
		}

		internal static int CurrentPrice()
		{
			int num = (ReferralUsed ? RVRepairVanPreferences.BasePriceWithReferral : RVRepairVanPreferences.BasePriceNoReferral);
			return Mathf.Max(RVRepairVanPreferences.RepairPrice, num - DiscountTotal);
		}

		private static bool ExplosionBeatPassed()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Invalid comparison between Unknown and I4
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			try
			{
				Quest quest = Quest.GetQuest("Getting Started");
				if ((Object)(object)quest != (Object)null && ((int)quest.State == 1 || (int)quest.State == 2))
				{
					return true;
				}
				Quest quest2 = Quest.GetQuest("Welcome to Hyland Point");
				if ((Object)(object)quest2 != (Object)null && (int)quest2.State == 2)
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static void Reset()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			_gen++;
			ResetDiag();
			_donnaDone = (_mingDone = (_marcoDone = false));
			_pickupActive = false;
			_hasPackage = false;
			_clientCheckedRv = false;
			_drop = null;
			_crateDrop = null;
			_cratePlaced = (_pkgPlaced = false);
			_dropPoint = Vector3.zero;
			_cratePoint = Vector3.zero;
			_donnaT = (_mingT = (_marcoT = null));
			_donnaDC = (_mingDC = (_marcoDC = null));
			_injectedChoices = (_skippedChoices = 0);
			_marcoRepairChoice = null;
			Array.Clear(_sampleChoices, 0, _sampleChoices.Length);
			_sampleSlots.Clear();
			RepairStateStore.ResetClient();
		}

		internal static void Start()
		{
			InitNet();
			MelonCoroutines.Start(EnsureItemsCoroutine());
			MelonCoroutines.Start(SetupCoroutine());
			MelonCoroutines.Start(ProximityCoroutine());
			MelonCoroutines.Start(RestoreCoroutine());
			MelonCoroutines.Start(NetJoinCoroutine());
		}

		private static IEnumerator RestoreCoroutine()
		{
			yield return (object)new WaitForSeconds(4f);
			if (NetworkBus.Online && !NetworkBus.IsServer)
			{
				yield break;
			}
			try
			{
				if (Active && Stage >= 1 && Stage < 10)
				{
					EnsureQuest();
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Questline] restore failed: " + ex.Message);
			}
		}

		private static IEnumerator SetupCoroutine()
		{
			int myGen = _gen;
			int attempt = 0;
			bool reported = false;
			while (myGen == _gen && (!_donnaDone || !_mingDone || !_marcoDone))
			{
				yield return (object)new WaitForSeconds((attempt < 20) ? 2f : 10f);
				attempt++;
				TryInject(attempt >= 15);
				if (!reported && attempt >= 20)
				{
					reported = true;
				}
			}
			if (myGen != _gen)
			{
				yield break;
			}
			try
			{
				if ((!NetworkBus.Online || NetworkBus.IsServer) && Active && Stage >= 1 && Stage < 10)
				{
					EnsureQuest();
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Questline] post-inject POI sync failed: " + ex.Message);
			}
		}

		private static void TryInject(bool degraded = false)
		{
			try
			{
				if (!_donnaDone)
				{
					_donnaDone = TryOne("donna_martin", out _donnaT, ref _donnaDC, InjectDonna, degraded);
				}
				if (!_mingDone)
				{
					_mingDone = TryOne("ming", out _mingT, ref _mingDC, InjectMing, degraded);
				}
				if (!_marcoDone)
				{
					_marcoDone = TryOne("marco_baron", out _marcoT, ref _marcoDC, InjectMarco, degraded);
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Questline] inject attempt failed: " + ex.Message);
			}
		}

		private static bool TryOne(string id, out Transform t, ref DialogueController held, Func<DialogueController, NPC, bool, bool> inject, bool degraded)
		{
			NPC val = FindNpc(id);
			DialogueController val2 = ControllerOf(val, out t);
			if ((Object)(object)val2 != (Object)null && inject(val2, val, degraded))
			{
				held = val2;
				if (degraded)
				{
					Core.Log.Warning("[Questline] '" + id + "' injected WITHOUT its dialogue containers - the questline works but replies show as floating lines instead of in-conversation menus.");
				}
				return true;
			}
			return false;
		}

		private static bool InjectDonna(DialogueController donna, NPC npc, bool degraded)
		{
			DialogueContainer val = S1Container(npc, "rv_donna", delegate(DialogueContainerBuilder b)
			{
				b.AddNode("ENTRY", L10n.T("Do I look like a mechanic, sweetheart? Go ask Mrs. Ming over at the Chinese place. She knows people."), (Action<ChoiceList>)null);
			});
			if ((Object)(object)val == (Object)null && !degraded)
			{
				return false;
			}
			AddChoice(donna, L10n.T("My RV got blown up. Know anyone who can fix it?"), 90, () => Active && Stage == 1, OnAskDonna, val);
			return true;
		}

		private static bool InjectMing(DialogueController ming, NPC npc, bool degraded)
		{
			DialogueContainer val = S1Container(npc, "rv_ming_offer", delegate(DialogueContainerBuilder b)
			{
				b.AddNode("ENTRY", L10n.T("Marco at the docks can fix almost anything. But favors move both ways. I have a crate waiting at a dead drop nearby. Bring it back, and I'll put in a word."), (Action<ChoiceList>)delegate(ChoiceList c)
				{
					c.Add("MING_ACCEPT", L10n.T("I'll grab it."), "MING_ACCEPTED").Add("MING_DEFER", L10n.T("Not right now."), "MING_DEFERRED");
				}).AddNode("MING_ACCEPTED", L10n.T("Good. Pick it up, bring it here, and don't open it."), (Action<ChoiceList>)null).AddNode("MING_DEFERRED", L10n.T("Then your RV can stay where it is."), (Action<ChoiceList>)null);
			});
			DialogueContainer val2 = S1Container(npc, "rv_ming_deliver", delegate(DialogueContainerBuilder b)
			{
				b.AddNode("ENTRY", L10n.T("Good. Go see Marco at the body shop down by the docks. Tell him Mrs. Ming sent you."), (Action<ChoiceList>)null);
			});
			DialogueContainer val3 = S1Container(npc, "rv_ming_lost", delegate(DialogueContainerBuilder b)
			{
				b.AddNode("ENTRY", L10n.T("You lost it? I don't lose things, and people who lose my things lose teeth. Five hundred buys you both back. Now."), (Action<ChoiceList>)delegate(ChoiceList c)
				{
					c.Add("MING_PAY", L10n.T("Pay ${0}", 500), (string)null).Add("MING_DEFER_LOSS", L10n.T("I'll get the money."), "MING_LOSS_DEFER");
				}).AddNode("MING_LOSS_DEFER", L10n.T("Then don't come back until your hands are full."), (Action<ChoiceList>)null);
			});
			if (!degraded && ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null))
			{
				return false;
			}
			OnPick(npc, "MING_ACCEPT", OnAcceptErrand);
			AddChoice(ming, L10n.T("Donna said you might know someone who can fix my RV."), 92, () => Active && Stage == 2, ((Object)(object)val != (Object)null) ? null : ((Action)delegate
			{
				WorldSay(_mingT, L10n.T("Marco at the docks can fix almost anything. But favors move both ways. I have a crate waiting at a dead drop nearby. Bring it back, and I'll put in a word."));
				OnAcceptErrand();
			}), val);
			AddChoice(ming, L10n.T("Here's your crate."), 91, () => Active && Stage == 4 && PlayerHasItem("rv_ming_crate"), OnDeliverCrate, val2);
			OnPick(npc, "MING_PAY", OnMingPayLoss);
			AddChoice(ming, ((Object)(object)val3 != (Object)null) ? L10n.T("I lost your crate.") : L10n.T("I lost your crate. (Pay ${0})", 500), 90, () => Active && Stage == 4 && !PlayerHasItem("rv_ming_crate"), ((Object)(object)val3 != (Object)null) ? null : ((Action)delegate
			{
				WorldSay(_mingT, L10n.T("You lost it? I don't lose things, and people who lose my things lose teeth. Five hundred buys you both back. Now."));
				OnMingPayLoss();
			}), val3);
			return true;
		}

		private static bool InjectMarco(DialogueController marco, NPC npc, bool degraded)
		{
			DialogueContainer val = S1Container(npc, "rv_marco_favour", delegate(DialogueContainerBuilder b)
			{
				b.AddNode("ENTRY", L10n.T("Maybe. I left a package at a dead drop nearby. Pick it up, bring it back, and don't make it weird."), (Action<ChoiceList>)null);
			});
			DialogueContainer val2 = S1Container(npc, "rv_marco_gotpkg", delegate(DialogueContainerBuilder b)
			{
				b.AddNode("ENTRY", L10n.T("Good. You can follow instructions. Bring me some of that good stuff now and then, and I'll keep shaving down the bill."), (Action<ChoiceList>)null);
			});
			DialogueContainer val3 = S1Container(npc, "rv_marco_lost", delegate(DialogueContainerBuilder b)
			{
				b.AddNode("ENTRY", L10n.T("You did what? You walk in here empty-handed and waste my time. Five hundred, or the next thing that goes missing is you."), (Action<ChoiceList>)delegate(ChoiceList c)
				{
					c.Add("MARCO_PAY", L10n.T("Pay ${0}", 500), (string)null).Add("MARCO_DEFER_LOSS", L10n.T("I'll get the money."), "MARCO_LOSS_DEFER");
				}).AddNode("MARCO_LOSS_DEFER", L10n.T("Clock's running. Come back with it."), (Action<ChoiceList>)null);
			});
			DialogueContainer val4 = S1Container(npc, "rv_marco_bring", delegate(DialogueContainerBuilder b)
			{
				b.AddNode("ENTRY", L10n.T("Bring me packaged product - sealed stuff, not raw. Every piece I take knocks its value off the bill, up to five hundred a pop, right down to my floor."), (Action<ChoiceList>)null);
			});
			if (!degraded && ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null))
			{
				return false;
			}
			AddChoice(marco, L10n.T("Can you fix my RV?"), 100, () => Active && Stage == 5, OnMarcoGreet);
			AddChoice(marco, L10n.T("Fifty grand?"), 99, () => Active && Stage == 6, OnMarcoFifty);
			AddChoice(marco, L10n.T("Mrs. Ming sent me."), 98, () => Active && Stage == 6, OnMarcoReferral);
			_marcoRepairChoice = AddChoice(marco, RepairChoiceText(), 97, () => Active && MarcoGreeted && Stage < 9, OnMarcoRepair);
			AddChoice(marco, L10n.T("Anything I can do to bring the price down?"), 96, () => Active && Stage == 7 && !_pickupActive, OnMarcoFavour, val);
			AddChoice(marco, L10n.T("Got your package."), 96, () => Active && _pickupActive && _hasPackage && PlayerHasItem("rv_marco_package"), OnGotPackage, val2);
			OnPick(npc, "MARCO_PAY", OnMarcoPayLoss);
			AddChoice(marco, ((Object)(object)val3 != (Object)null) ? L10n.T("I lost your package.") : L10n.T("I lost your package. (Pay ${0})", 500), 96, () => Active && _pickupActive && _hasPackage && !PlayerHasItem("rv_marco_package"), ((Object)(object)val3 != (Object)null) ? null : ((Action)delegate
			{
				WorldSay(_marcoT, L10n.T("You did what? You walk in here empty-handed and waste my time. Five hundred, or the next thing that goes missing is you."));
				OnMarcoPayLoss();
			}), val3);
			for (int num = 0; num < 8; num++)
			{
				int idx = num;
				_sampleChoices[idx] = AddChoice(marco, L10n.T("Give Marco a packaged sample"), 95 - idx, () => SampleChoiceVisible(idx), delegate
				{
					OnGiveSample(idx);
				});
			}
			AddChoice(marco, L10n.T("What can I bring to lower the price?"), 94, () => Active && Trusted_ && Stage < 9 && !HoldingPackaged() && CurrentPrice() > RVRepairVanPreferences.RepairPrice, ((Object)(object)val4 != (Object)null) ? null : ((Action)delegate
			{
				WorldSay(_marcoT, L10n.T("Bring me packaged product - sealed stuff, not raw. Every piece I take knocks its value off the bill, up to five hundred a pop, right down to my floor."));
			}), val4);
			return true;
		}

		private static NPC FindNpc(string id)
		{
			try
			{
				List<NPC> nPCRegistry = NPCManager.NPCRegistry;
				if (nPCRegistry == null)
				{
					return null;
				}
				for (int i = 0; i < nPCRegistry.Count; i++)
				{
					NPC val = nPCRegistry[i];
					if ((Object)(object)val != (Object)null && string.Equals(val.ID, id, StringComparison.OrdinalIgnoreCase))
					{
						return val;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		internal static void GruntNpc(NPC npc)
		{
			try
			{
				if ((Object)(object)npc != (Object)null && (Object)(object)npc.VoiceOverEmitter != (Object)null)
				{
					npc.VoiceOverEmitter.Play((EVOLineType)9);
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Questline] grunt failed: " + ex.Message);
			}
		}

		private static DialogueContainer S1Container(NPC npc, string name, Action<DialogueContainerBuilder> build)
		{
			try
			{
				if ((Object)(object)npc == (Object)null)
				{
					return null;
				}
				NPC val = NPC.Get(npc.ID);
				if (val == null)
				{
					return null;
				}
				val.Dialogue.BuildAndRegisterContainer(name, build);
				DialogueHandler dialogueHandler = npc.DialogueHandler;
				List<DialogueContainer> val2 = (((Object)(object)dialogueHandler != (Object)null) ? dialogueHandler.dialogueContainers : null);
				if (val2 == null)
				{
					return null;
				}
				for (int i = 0; i < val2.Count; i++)
				{
					if ((Object)(object)val2[i] != (Object)null && ((Object)val2[i]).name == name)
					{
						return val2[i];
					}
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Questline] S1 container '" + name + "' failed: " + ex.Message);
			}
			return null;
		}

		private static void OnPick(NPC npc, string label, Action cb)
		{
			try
			{
				if (!((Object)(object)npc == (Object)null))
				{
					NPC obj = NPC.Get(npc.ID);
					if (obj != null)
					{
						obj.Dialogue.OnChoiceSelected(label, cb);
					}
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Questline] OnPick '" + label + "' failed: " + ex.Message);
			}
		}

		internal static void DumpNpcDiagnostics()
		{
			DumpState();
			try
			{
				List<NPC> nPCRegistry = NPCManager.NPCRegistry;
				if (nPCRegistry == null)
				{
					Core.Log.Msg("[NPC-DIAG] NPCRegistry == null");
					return;
				}
				string text = "";
				for (int i = 0; i < nPCRegistry.Count; i++)
				{
					NPC val = nPCRegistry[i];
					if (!((Object)(object)val == (Object)null))
					{
						try
						{
							text = text + val.ID + " ";
						}
						catch
						{
							text += "(err) ";
						}
					}
				}
				Core.Log.Msg("[NPC-DIAG] registry(" + nPCRegistry.Count + "): " + text);
				DiagTarget("donna_martin");
				DiagTarget("ming");
				DiagTarget("marco_baron");
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[NPC-DIAG] dump failed: " + ex.Message);
			}
		}

		internal static void DumpState()
		{
			try
			{
				Core.Log.Msg("[STATE] Enabled=" + RVRepairVanPreferences.Enabled + " Questline=" + RVRepairVanPreferences.QuestlineEnabled + " Stage=" + Stage + " Samples=" + Samples + " Discount=" + DiscountTotal + " Active(IsDestroyed)=" + Active + " ExplosionBeatPassed=" + ExplosionBeatPassed());
				LogQuest("Getting Started");
				LogQuest("Welcome to Hyland Point");
				try
				{
					Core.Log.Msg("[STATE] S1API NPC.All count = " + NPC.All.Count);
				}
				catch (Exception ex)
				{
					Core.Log.Msg("[STATE] S1API NPC.All threw: " + ex.Message);
				}
			}
			catch (Exception ex2)
			{
				Core.Log.Warning("[STATE] dump failed: " + ex2.Message);
			}
		}

		private static void LogQuest(string name)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Quest quest = Quest.GetQuest(name);
				Core.Log.Msg("[STATE] Quest '" + name + "': " + (((Object)(object)quest == (Object)null) ? "NOT FOUND" : ("state=" + ((object)quest.State/*cast due to .constrained prefix*/).ToString())));
			}
			catch (Exception ex)
			{
				Core.Log.Msg("[STATE] Quest '" + name + "' lookup threw: " + ex.Message);
			}
		}

		private static void DiagTarget(string id)
		{
			NPC val = FindNpc(id);
			if ((Object)(object)val == (Object)null)
			{
				Core.Log.Msg("[NPC-DIAG] " + id + ": NOT in registry");
				return;
			}
			string text = "?";
			bool flag = false;
			bool flag2 = false;
			try
			{
				text = ((Object)((Component)val).gameObject).name;
				flag = ((Component)val).gameObject.activeInHierarchy;
			}
			catch
			{
			}
			try
			{
				flag2 = (Object)(object)ControllerOf(val, out var _) != (Object)null;
			}
			catch
			{
			}
			Core.Log.Msg("[NPC-DIAG] " + id + ": IN registry, go='" + text + "' active=" + flag + " controllerFound=" + flag2);
		}

		private static void ResetDiag()
		{
		}

		private static DialogueController ControllerOf(NPC npc, out Transform t)
		{
			t = null;
			if ((Object)(object)npc == (Object)null)
			{
				return null;
			}
			try
			{
				t = ((Component)npc).transform;
				DialogueHandler dialogueHandler = npc.DialogueHandler;
				DialogueController val = null;
				if ((Object)(object)dialogueHandler != (Object)null)
				{
					val = ((Component)dialogueHandler).GetComponentInChildren<DialogueController>(true);
				}
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)npc).GetComponentInChildren<DialogueController>(true);
				}
				return val;
			}
			catch
			{
				return null;
			}
		}

		private static void OnAskDonna()
		{
			if (!RouteIntent(RvOp.AskDonna) && Stage == 1)
			{
				Stage = 2;
				SyncEntry();
			}
		}

		private static void OnAcceptErrand()
		{
			//IL_0033: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if (!RouteIntent(RvOp.AcceptErrand) && Stage == 2)
			{
				Stage = 3;
				_crateDrop = ReserveDeadDrop(((Object)(object)_mingT != (Object)null) ? _mingT.position : RvPos());
				_cratePoint = ((_crateDrop != null) ? _crateDrop.Position : (((Object)(object)_mingT != (Object)null) ? (_mingT.position + new Vector3(8f, 0f, 8f)) : RvPos()));
				_cratePlaced = PlaceItem(_crateDrop, "rv_ming_crate");
				SyncEntry();
			}
		}

		private static void OnDeliverCrate()
		{
			RemovePlayerItem("rv_ming_crate");
			if (!RouteIntent(RvOp.DeliverCrate))
			{
				HostDeliverCrate();
			}
		}

		private static void HostDeliverCrate()
		{
			if (Stage == 4)
			{
				Stage = 5;
				SyncEntry();
			}
		}

		private static void OnMingPayLoss()
		{
			if (!RouteIntent(RvOp.MingPayLoss) && Stage == 4)
			{
				if (Money.GetCashBalance() < 500f)
				{
					WorldSay(_mingT, L10n.T("Then don't come back until your hands are full."));
					return;
				}
				Money.ChangeCashBalance(-500f, true, true);
				Stage = 5;
				WorldSay(_mingT, L10n.T("Smart. We're square. Now go see Marco at the body shop down by the docks, and tell him Mrs. Ming sent you."));
				SyncEntry();
			}
		}

		private static void OnMarcoGreet()
		{
			if (!RouteIntent(RvOp.MarcoGreet) && Stage == 5)
			{
				Stage = 6;
				WorldSay(_marcoT, L10n.T("Yeah, I can fix it. Fifty grand."));
				SyncEntry();
			}
		}

		private static void OnMarcoFifty()
		{
			WorldSay(_marcoT, L10n.T("You brought me a burnt-out shell. That's not a repair, that's a resurrection."));
		}

		private static void OnMarcoReferral()
		{
			if (!RouteIntent(RvOp.MarcoReferral) && Stage == 6)
			{
				Stage = 7;
				RefreshRepairChoice();
				WorldSay(_marcoT, L10n.T("Mrs. Ming sent you? Yeah, alright. Should've opened with that. Ten grand."));
				SyncEntry();
			}
		}

		private static void OnMarcoRepair()
		{
			try
			{
				if (!RVManager.IsDestroyed())
				{
					WorldSay(_marcoT, L10n.T("Your RV looks fine to me."));
					return;
				}
				int num = CurrentPrice();
				if (Money.GetCashBalance() < (float)num)
				{
					WorldSay(_marcoT, L10n.T("You're short. Come back when you've got the cash."));
					return;
				}
				WorldSay(_marcoT, L10n.T("Alright. Hold still, this won't take long."));
				if (NetworkBus.Online && !NetworkBus.IsServer)
				{
					NetworkBus.SendToHost(RvOp.PayRepair);
					RepairCinematic.Play(null, delegate
					{
						WorldSay(_marcoT, L10n.T("There she is - back from the dead. Go take a look, and try not to total her again."));
					}, delegate
					{
						GruntNpc(FindNpc("marco_baron"));
					});
					return;
				}
				if (NetworkBus.IsServer)
				{
					HostPayRepair(withCinematic: true);
					return;
				}
				Money.ChangeCashBalance((float)(-num), true, true);
				int paid = num;
				RepairCinematic.Play(delegate
				{
					if (RVManager.Repair())
					{
						RepairStateStore.SetRepaired(repaired: true);
						Stage = 9;
						SyncEntry();
						Core.Log.Msg("[Questline] RV repaired for " + MoneyManager.FormatAmount((float)paid, false, false) + ".");
					}
				}, delegate
				{
					WorldSay(_marcoT, L10n.T("There she is - back from the dead. Go take a look, and try not to total her again."));
				}, delegate
				{
					GruntNpc(FindNpc("marco_baron"));
				});
			}
			catch (Exception ex)
			{
				Core.Log.Error("[Questline] repair failed: " + ex);
			}
		}

		private static void OnMarcoFavour()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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_006f: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if (!RouteIntent(RvOp.MarcoFavour) && Stage == 7 && !_pickupActive)
			{
				_pickupActive = true;
				_hasPackage = false;
				_drop = ReserveDeadDrop(((Object)(object)_marcoT != (Object)null) ? _marcoT.position : RvPos());
				_dropPoint = ((_drop != null) ? _drop.Position : (((Object)(object)_marcoT != (Object)null) ? (_marcoT.position + new Vector3(10f, 0f, 10f)) : RvPos()));
				_pkgPlaced = PlaceItem(_drop, "rv_marco_package");
				SyncEntry();
			}
		}

		private static void OnGotPackage()
		{
			RemovePlayerItem("rv_marco_package");
			if (!RouteIntent(RvOp.GotPackage))
			{
				HostGotPackage();
			}
		}

		private static void HostGotPackage()
		{
			if (_pickupActive && _hasPackage)
			{
				_pickupActive = false;
				_hasPackage = false;
				_drop = null;
				Stage = 8;
				SyncEntry();
			}
		}

		private static void OnMarcoPayLoss()
		{
			if (!RouteIntent(RvOp.MarcoPayLoss) && _pickupActive && _hasPackage)
			{
				if (Money.GetCashBalance() < 500f)
				{
					WorldSay(_marcoT, L10n.T("Clock's running. Come back with it."));
					return;
				}
				Money.ChangeCashBalance(-500f, true, true);
				_pickupActive = false;
				_hasPackage = false;
				_drop = null;
				Stage = 8;
				WorldSay(_marcoT, L10n.T("Good. Mess like that gets forgotten when the cash shows up. Bring me some of that good stuff now and then, and I'll keep shaving down the bill."));
				SyncEntry();
			}
		}

		private static void OnGiveSample(int i)
		{
			try
			{
				RefreshSampleSlots();
				ItemSlot val = ((i >= 0 && i < _sampleSlots.Count) ? _sampleSlots[i] : FindPackagedProductSlot());
				object obj;
				if (val == null)
				{
					obj = null;
				}
				else
				{
					ItemInstance itemInstance = val.ItemInstance;
					obj = ((itemInstance != null) ? ((Il2CppObjectBase)itemInstance).TryCast<ProductItemInstance>() : null);
				}
				ProductItemInstance val2 = (ProductItemInstance)obj;
				if (val2 == null || (Object)(object)val2.AppliedPackaging == (Object)null)
				{
					WorldSay(_marcoT, L10n.T("That ain't packaged. Hand me something sealed."));
					return;
				}
				int num = SampleUnitDiscount(val2);
				NPC val3 = FindNpc("marco_baron");
				if (val != null)
				{
					_ = val.Quantity;
				}
				if ((Object)(object)val3 != (Object)null && (Object)(object)val3.Behaviour != (Object)null)
				{
					val3.Behaviour.ConsumeProduct(val2, false);
				}
				RemoveOneFromSlot(val);
				if (val != null)
				{
					_ = val.Quantity;
				}
				if (RouteIntent(RvOp.GiveSample, num))
				{
					WorldSay(_marcoT, L10n.T("Appreciate it. Knocked {0} off the bill.", MoneyManager.FormatAmount((float)num, false, false)));
				}
				else
				{
					HostGiveSample(num);
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Questline] give sample failed: " + ex.Message);
			}
		}

		private static int SampleUnitDiscount(ProductItemInstance p)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.Max(1, ((BaseItemInstance)p).Quantity);
			return Mathf.Clamp(Mathf.RoundToInt(((BaseItemInstance)p).GetMonetaryValue() / (float)num * QualityMultiplier(((QualityItemInstance)p).Quality)), RVRepairVanPreferences.MinSampleDiscount, RVRepairVanPreferences.MaxSampleDiscount);
		}

		private static float QualityMultiplier(EQuality q)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected I4, but got Unknown
			return (int)q switch
			{
				0 => 0.6f, 
				1 => 0.8f, 
				3 => 1.5f, 
				4 => 2f, 
				_ => 1f, 
			};
		}

		private static void RefreshSampleSlots()
		{
			_sampleSlots.Clear();
			PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance;
			List<ItemSlot> val = ((instance != null) ? instance.GetAllInventorySlots() : null);
			if (val == null)
			{
				return;
			}
			for (int i = 0; i < val.Count; i++)
			{
				if (_sampleSlots.Count >= 8)
				{
					break;
				}
				ItemSlot val2 = val[i];
				object obj;
				if (val2 == null)
				{
					obj = null;
				}
				else
				{
					ItemInstance itemInstance = val2.ItemInstance;
					obj = ((itemInstance != null) ? ((Il2CppObjectBase)itemInstance).TryCast<ProductItemInstance>() : null);
				}
				ProductItemInstance val3 = (ProductItemInstance)obj;
				if (val3 != null && (Object)(object)val3.AppliedPackaging != (Object)null)
				{
					_sampleSlots.Add(val2);
				}
			}
		}

		private static bool SampleChoiceVisible(int i)
		{
			if (!Active || !Trusted_ || Stage >= 9 || CurrentPrice() <= RVRepairVanPreferences.RepairPrice)
			{
				return false;
			}
			RefreshSampleSlots();
			if (i >= _sampleSlots.Count)
			{
				return false;
			}
			ItemSlot obj = _sampleSlots[i];
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				ItemInstance itemInstance = obj.ItemInstance;
				obj2 = ((itemInstance != null) ? ((Il2CppObjectBase)itemInstance).TryCast<ProductItemInstance>() : null);
			}
			ProductItemInstance val = (ProductItemInstance)obj2;
			if (val == null)
			{
				return false;
			}
			if (_sampleChoices[i] != null)
			{
				_sampleChoices[i].ChoiceText = SampleChoiceText(val);
			}
			return true;
		}

		private static string SampleChoiceText(ProductItemInstance p)
		{
			string text = L10n.T("product");
			try
			{
				ItemDefinition definition = ((ItemInstance)p).Definition;
				if ((Object)(object)definition != (Object)null)
				{
					text = ((BaseItemDefinition)definition).Name;
				}
			}
			catch
			{
			}
			return L10n.T("Give Marco: {0} (-{1})", text, MoneyManager.FormatAmount((float)SampleUnitDiscount(p), false, false));
		}

		private static void HostGiveSample(int discount)
		{
			Samples++;
			DiscountTotal += discount;
			RefreshRepairChoice();
			WorldSay(_marcoT, L10n.T("Appreciate it. Knocked {0} off the bill.", MoneyManager.FormatAmount((float)discount, false, false)));
		}

		private static DeadDropInstance ReserveDeadDrop(Vector3 origin)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				DeadDropInstance[] array = DeadDropManager.Empty;
				if (array == null || array.Length == 0)
				{
					array = DeadDropManager.All;
				}
				if (array == null || array.Length == 0)
				{
					return null;
				}
				DeadDropInstance result = null;
				float num = -1f;
				foreach (DeadDropInstance val in array)
				{
					if (val != null)
					{
						float num2 = Vector3.Distance(origin, val.Position);
						if (num2 > num)
						{
							num = num2;
							result = val;
						}
					}
				}
				return result;
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Questline] reserve dead drop failed: " + ex.Message);
				return null;
			}
		}

		private static void EnsureItems()
		{
			if (_itemsRegistered)
			{
				return;
			}
			try
			{
				if (!(ItemManager.GetDefinition("grainbag") == (ItemDefinition)null))
				{
					RegisterItem("rv_ming_crate", L10n.T("Ming's Crate"), L10n.T("A sealed crate for Mrs. Ming. She said not to open it."), new string[3] { "grainbag", "trashbag", "flashlight" });
					RegisterItem("rv_marco_package", L10n.T("Marco's Package"), L10n.T("A package Marco left at a drop. Don't make it weird."), new string[3] { "trashbag", "grainbag", "flashlight" });
					_itemsRegistered = true;
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Questline] item register failed: " + ex.Message);
			}
		}

		private static IEnumerator EnsureItemsCoroutine()
		{
			for (int i = 0; i < 30; i++)
			{
				if (_itemsRegistered)
				{
					break;
				}
				EnsureItems();
				if (_itemsRegistered)
				{
					break;
				}
				yield return (object)new WaitForSeconds(1f);
			}
		}

		private static void RegisterItem(string id, string name, string desc, string[] baseIds)
		{
			if (ItemManager.GetDefinition(id) != (ItemDefinition)null)
			{
				return;
			}
			StorableItemDefinition val = null;
			foreach (string text in baseIds)
			{
				try
				{
					val = ((StorableItemDefinitionBuilderBase<StorableItemDefinitionBuilder>)(object)((StorableItemDefinitionBuilderBase<StorableItemDefinitionBuilder>)(object)ItemCreator.CloneFrom(text)).WithBasicInfo(id, name, desc, (ItemCategory)3)).WithStackLimit(1).Build();
				}
				catch (Exception)
				{
					continue;
				}
				break;
			}
			if ((ItemDefinition)(object)val == (ItemDefinition)null)
			{
				val = ItemCreator.CreateItem(id, name, desc, (ItemCategory)3, 1, 10f, 0.5f, (LegalStatus)0, (FullRank?)null, (Sprite)null, (Equippable)null);
				Core.Log.Warning("[Questline] quest item '" + id + "' registered WITHOUT a model/icon (no clone base usable).");
			}
			try
			{
				ItemManager.PreserveRuntimeItem((ItemDefinition)(object)val);
			}
			catch
			{
			}
		}

		[Conditional("DEBUG")]
		private static void DumpItemsOnce()
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (_itemsDumped)
			{
				return;
			}
			_itemsDumped = true;
			try
			{
				List<ItemDefinition> allItemDefinitions = ItemManager.GetAllItemDefinitions();
				Core.Log.Msg("[ITEMDUMP] " + (allItemDefinitions?.Count ?? 0) + " registered items:");
				if (allItemDefinitions == null)
				{
					return;
				}
				for (int i = 0; i < allItemDefinitions.Count; i++)
				{
					ItemDefinition val = allItemDefinitions[i];
					if (!(val == (ItemDefinition)null))
					{
						try
						{
							Core.Log.Msg("[ITEMDUMP] id='" + val.ID + "' name='" + val.Name + "' cat=" + ((object)val.Category/*cast due to .constrained prefix*/).ToString() + " icon=" + ((Object)(object)val.Icon != (Object)null));
						}
						catch
						{
						}
					}
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[ITEMDUMP] failed: " + ex.Message);
			}
		}

		private static bool PlaceItem(DeadDropInstance drop, string id)
		{
			try
			{
				if (drop == null)
				{
					return false;
				}
				EnsureItems();
				ItemDefinition definition = ItemManager.GetDefinition(id);
				if (definition == (ItemDefinition)null)
				{
					return false;
				}
				drop.Storage.AddItem(definition.CreateInstance(1));
				return true;
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Questline] place item failed: " + ex.Message);
				return false;
			}
		}

		private static bool PlayerHasItem(string id)
		{
			try
			{
				PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance;
				List<HotbarSlot> val = (((Object)(object)instance != (Object)null) ? instance.hotbarSlots : null);
				if (val == null)
				{
					return false;
				}
				for (int i = 0; i < val.Count; i++)
				{
					ItemSlot val2 = (ItemSlot)(object)val[i];
					ItemInstance val3 = ((val2 != null) ? val2.ItemInstance : null);
					if (val3 != null && string.Equals(((BaseItemInstance)val3).ID, id, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static void RemovePlayerItem(string id)
		{
			try
			{
				PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance;
				List<HotbarSlot> val = (((Object)(object)instance != (Object)null) ? instance.hotbarSlots : null);
				if (val == null)
				{
					Core.Log.Warning("[Questline] remove '" + id + "': no hotbar slots.");
					return;
				}
				for (int i = 0; i < val.Count; i++)
				{
					ItemSlot val2 = (ItemSlot)(object)val[i];
					ItemInstance val3 = ((val2 != null) ? val2.ItemInstance : null);
					if (val3 != null && string.Equals(((BaseItemInstance)val3).ID, id, StringComparison.OrdinalIgnoreCase))
					{
						RemoveOneFromSlot(val2);
						return;
					}
				}
				Core.Log.Warning("[Questline] remove '" + id + "': not found in hotbar.");
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Questline] remove item failed: " + ex.Message);
			}
		}

		private static void RemoveOneFromSlot(ItemSlot slot)
		{
			if (slot != null)
			{
				ItemInstance itemInstance = slot.ItemInstance;
				if (((itemInstance != null) ? ((BaseItemInstance)itemInstance).Quantity : 0) > 1)
				{
					slot.ChangeQuantity(-1, false);
				}
				else
				{
					slot.ClearStoredInstance(false);
				}
			}
		}

		private static IEnumerator ProximityCoroutine()
		{
			int myGen = _gen;
			int waitedForLoad = 0;
			while (myGen == _gen)
			{
				yield return (object)new WaitForSeconds(1f);
				if (!RepairSave.Loaded)
				{
					int num = waitedForLoad + 1;
					waitedForLoad = num;
					if (num < 10)
					{
						continue;
					}
					if (waitedForLoad == 10)
					{
						Core.Log.Warning("[Questline] save state not loaded after 10s - proceeding with current values.");
					}
				}
				if (NetworkBus.Online && !NetworkBus.IsServer)
				{
					if (Stage == 9 && !_clientCheckedRv && RVManager.TryGetPosition(out var position) && Dist(PlayerPos(), position) < 14f)
					{
						_clientCheckedRv = true;
						NetworkBus.SendToHost(RvOp.CheckedRv);
					}
					else if ((Stage == 3 && PlayerHasItem("rv_ming_crate")) || (_pickupActive && !_hasPackage && PlayerHasItem("rv_marco_package")))
					{
						NetworkBus.SendToHost(RvOp.ErrandItemPicked);
					}
					continue;
				}
				if (RVRepairVanPreferences.Enabled && Stage == 0 && !RepairStateStore.GetRepaired() && RVManager.IsDestroyed() && ExplosionBeatPassed())
				{
					Stage = 1;
					EnsureQuest();
					Core.Log.Msg("[Questline] quest started (wrecked RV + explosion beat passed).");
				}
				if (Stage == 9 && RVManager.TryGetPosition(out var position2) && Dist(PlayerPos(), position2) < 14f)
				{
					HostCheckedRv();
				}
				if (!Active)
				{
					continue;
				}
				try
				{
					Vector3 a = PlayerPos();
					if (Stage == 3)
					{
						if (_cratePoint == Vector3.zero && (Object)(object)_mingT != (Object)null)
						{
							_crateDrop = ReserveDeadDrop(_mingT.position);
							_cratePoint = ((_crateDrop != null) ? _crateDrop.Position : (_mingT.position + new Vector3(8f, 0f, 8f)));
							_cratePlaced = PlaceItem(_crateDrop, "rv_ming_crate");
							SyncEntry();
						}
						if (_cratePlaced ? PlayerHasItem("rv_ming_crate") : (_cratePoint != Vector3.zero && Dist(a, _cratePoint) < 5f))
						{
							Stage = 4;
							SyncEntry();
						}
					}
					if (_pickupActive && !_hasPackage && (_pkgPlaced ? PlayerHasItem("rv_marco_package") : (Dist(a, _dropPoint) < 5f)))
					{
						_hasPackage = true;
						SyncEntry();
					}
				}
				catch
				{
				}
			}
		}

		private static void EnsureQuest()
		{
			RepairQuest.StartIfNeeded();
			SyncEntry();
		}

		private static void SyncEntry()
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: 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_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			string text = null;
			string title;
			Vector3 val;
			if (_pickupActive)
			{
				title = (_hasPackage ? L10n.T("Bring Marco's package back") : L10n.T("Pick up Marco's package from the dead drop"));
				if (_hasPackage)
				{
					text = "marco_baron";
					val = MarcoPos();
				}
				else
				{
					val = _dropPoint;
				}
			}
			else
			{
				switch (Stage)
				{
				case 1:
					title = L10n.T("Ask the motel manager about the RV");
					text = "donna_martin";
					val = DonnaPos();
					break;
				case 2:
					title = L10n.T("Talk to Mrs. Ming at the Chinese restaurant");
					text = "ming";
					val = MingPos();
					break;
				case 3:
					title = L10n.T("Pick up Ming's crate from the dead drop");
					val = _cratePoint;
					break;
				case 4:
					title = L10n.T("Bring Ming's crate back to Mrs. Ming");
					text = "ming";
					val = MingPos();
					break;
				case 5:
					title = L10n.T("Talk to Marco at the body shop");
					text = "marco_baron";
					val = MarcoPos();
					break;
				case 6:
					title = L10n.T("Tell Marco Mrs. Ming sent you");
					text = "marco_baron";
					val = MarcoPos();
					break;
				case 7:
				case 8:
					title = L10n.T("Pay Marco for the repair");
					text = "marco_baron";
					val = MarcoPos();
					break;
				case 9:
					title = L10n.T("Check on the RV");
					val = RvPos();
					break;
				default:
					title = L10n.T("Find a way to repair your RV");
					val = RvPos();
					break;
				}
			}
			if (text != null)
			{
				RepairQuest.UpdateEntry(title, FindNpc(text), val);
			}
			else
			{
				RepairQuest.UpdateEntry(title, val);
			}
			if (NetworkBus.Online && NetworkBus.IsServer)
			{
				HostBroadcastTransient();
			}
		}

		private static DialogueChoice AddChoice(DialogueController dc, string text, int prio, Func<bool> show, Action onChosen, DialogueContainer conv = null)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			if ((Object)(object)conv == (Object)null && onChosen == null)
			{
				_skippedChoices++;
				Core.Log.Warning("[Questline] skipped a choice with neither reply nor action: '" + text + "'");
				return null;
			}
			_injectedChoices++;
			DialogueChoice val = new DialogueChoice
			{
				Enabled = true,
				ChoiceText = text,
				Conversation = conv,
				Priority = prio
			};
			Func<bool, bool> func = delegate
			{
				try
				{
					return show();
				}
				catch
				{
					return false;
				}
			};
			val.shouldShowCheck = DelegateSupport.ConvertDelegate<ShouldShowCheck>((Delegate)func);
			val.onChoosen = new UnityEvent();
			if (onChosen != null)
			{
				val.onChoosen.AddListener(UnityAction.op_Implicit(onChosen));
			}
			dc.AddDialogueChoice(val, prio);
			return val;
		}

		private static bool HoldingPackaged()
		{
			return FindPackagedProductSlot() != null;
		}

		private static ItemSlot FindPackagedProductSlot()
		{
			try
			{
				PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance;
				List<ItemSlot> val = ((instance != null) ? instance.GetAllInventorySlots() : null);
				if (val == null)
				{
					return null;
				}
				for (int i = 0; i < val.Count; i++)
				{
					ItemSlot val2 = val[i];
					object obj;
					if (val2 == null)
					{
						obj = null;
					}
					else
					{
						ItemInstance itemInstance = val2.ItemInstance;
						obj = ((itemInstance != null) ? ((Il2CppObjectBase)itemInstance).TryCast<ProductItemInstance>() : null);
					}
					ProductItemInstance val3 = (ProductItemInstance)obj;
					if (val3 != null && (Object)(object)val3.AppliedPackaging != (Object)null)
					{
						return val2;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static void RefreshRepairChoice()
		{
			try
			{
				if (_marcoRepairChoice != null)
				{
					_marcoRepairChoice.ChoiceText = RepairChoiceText();
				}
			}
			catch
			{
			}
		}

		internal static void RefreshPrice()
		{
			RefreshRepairChoice();
		}

		private static string RepairChoiceText()
		{
			return L10n.T("Repair my RV ({0})", MoneyManager.FormatAmount((float)CurrentPrice(), false, false));
		}

		private static void WorldSay(Transform npc, string line)
		{
			try
			{
				if (!((Object)(object)npc == (Object)null))
				{
					NPC componentInParent = ((Component)npc).GetComponentInParent<NPC>();
					if ((Object)(object)componentInParent != (Object)null)
					{
						componentInParent.SendWorldSpaceDialogue(line, 5f);
					}
				}
			}
			catch
			{
			}
		}

		private static Vector3 PlayerPos()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Player local = Player.Local;
				if ((Object)(object)local != (Object)null)
				{
					return ((Component)local).transform.position;
				}
			}
			catch
			{
			}
			return Vector3.zero;
		}

		private static float Dist(Vector3 a, Vector3 b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return Vector3.Distance(a, b);
		}

		private static Vector3 RvPos()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (!RVManager.TryGetPosition(out var position))
			{
				return Vector3.zero;
			}
			return position;
		}

		private static Vector3 DonnaPos()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_donnaT != (Object)null))
			{
				return RvPos();
			}
			return _donnaT.position;
		}

		private static Vector3 MingPos()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_mingT != (Object)null))
			{
				return RvPos();
			}
			return _mingT.position;
		}

		private static Vector3 MarcoPos()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_marcoT != (Object)null))
			{
				return RvPos();
			}
			return _marcoT.position;
		}

		internal static void InitNet()
		{
			if (!_netWired)
			{
				_netWired = true;
				NetworkBus.OnServerIntent = OnServerIntent;
				NetworkBus.OnClientState = OnClientState;
			}
		}

		private static bool RouteIntent(RvOp op, int a = 0, int b = 0)
		{
			if (NetworkBus.Online && !NetworkBus.IsServer)
			{
				NetworkBus.SendToHost(op, a, b);
				return true;
			}
			return false;
		}

		private static IEnumerator NetJoinCoroutine()
		{
			for (int i = 0; i < 60; i++)
			{
				yield return (object)new WaitForSeconds(0.5f);
				if (NetworkBus.Online)
				{
					break;
				}
			}
			if (NetworkBus.Online && !NetworkBus.IsServer)
			{
				NetworkBus.SendToHost(RvOp.RequestSnapshot);
				yield return (object)new WaitForSeconds(2f);
				NetworkBus.SendToHost(RvOp.RequestSnapshot);
			}
		}

		private static void OnServerIntent(RvMsg m)
		{
			if (!NetworkBus.IsServer)
			{
				return;
			}
			try
			{
				switch (m.Op)
				{
				case RvOp.AskDonna:
					OnAskDonna();
					break;
				case RvOp.AcceptErrand:
					OnAcceptErrand();
					break;
				case RvOp.DeliverCrate:
					HostDeliverCrate();
					break;
				case RvOp.MingPayLoss:
					OnMingPayLoss();
					break;
				case RvOp.MarcoGreet:
					OnMarcoGreet();
					break;
				case RvOp.MarcoReferral:
					OnMarcoReferral();
					break;
				case RvOp.MarcoFavour:
					OnMarcoFavour();
					break;
				case RvOp.GotPackage:
					HostGotPackage();
					break;
				case RvOp.MarcoPayLoss:
					OnMarcoPayLoss();
					break;
				case RvOp.GiveSample:
					HostGiveSample(m.A);
					break;
				case RvOp.PayRepair:
					HostPayRepair(withCinematic: false);
					break;
				case RvOp.CheckedRv:
					HostCheckedRv();
					break;
				case RvOp.ErrandItemPicked:
					HostErrandItemPicked();
					break;
				case RvOp.RequestSnapshot:
					HostSendSnapshot();
					break;
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Net] server intent " + m.Op.ToString() + " failed: " + ex.Message);
			}
		}

		private static void OnClientState(RvMsg m)
		{
			try
			{
				switch (m.Op)
				{
				case RvOp.StageSync:
					ApplyStageSync(m.A, m.B);
					break;
				case RvOp.TransientSync:
					ApplyTransient(m.A, m.B, m.C);
					break;
				case RvOp.RepairApplied:
					ApplyRepair();
					break;
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Net] client state " + m.Op.ToString() + " failed: " + ex.Message);
			}
		}

		private static void HostSendSnapshot()
		{
			NetworkBus.BroadcastToAll(RvOp.StageSync, Stage, DiscountTotal);
			HostBroadcastTransient();
			if (RepairStateStore.GetRepaired())
			{
				NetworkBus.BroadcastToAll(RvOp.RepairApplied);
			}
			HostResyncErrandItem();
		}

		private static void HostResyncErrandItem()
		{
			try
			{
				if (Stage == 3 && _crateDrop != null && _cratePlaced)
				{
					_crateDrop.Storage.RemoveAllOfDefinition("rv_ming_crate");
					PlaceItem(_crateDrop, "rv_ming_crate");
				}
				if (_pickupActive && !_hasPackage && _drop != null && _pkgPlaced)
				{
					_drop.Storage.RemoveAllOfDefinition("rv_marco_package");
					PlaceItem(_drop, "rv_marco_package");
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Net] errand item re-sync failed: " + ex.Message);
			}
		}

		private static void ApplyStageSync(int stage, int discount)
		{
			if (!NetworkBus.IsServer)
			{
				RepairStateStore.SetStage(stage);
				RepairStateStore.SetDiscountTotal(discount);
				if (stage >= 10)
				{
					RepairQuest.CompleteIfActive();
				}
				else if (Active && stage >= 1)
				{
					EnsureQuest();
				}
				SyncEntry();
				RefreshRepairChoice();
			}
		}

		private static void HostBroadcastTransient()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			int a = (_pickupActive ? 1 : 0) | (_hasPackage ? 2 : 0);
			Vector3 val = Vector3.zero;
			if (_pickupActive)
			{
				val = ((_drop != null) ? _drop.Position : _dropPoint);
			}
			else if (Stage == 3)
			{
				val = ((_crateDrop != null) ? _crateDrop.Position : _cratePoint);
			}
			NetworkBus.BroadcastToAll(RvOp.TransientSync, a, Mathf.RoundToInt(val.x), Mathf.RoundToInt(val.z));
		}

		private static void ApplyTransient(int flags, int x, int z)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkBus.IsServer)
			{
				_pickupActive = (flags & 1) != 0;
				_hasPackage = (flags & 2) != 0;
				DeadDropInstance val = ResolveDropByPos(x, z);
				Vector3 val2 = (Vector3)((val != null) ? val.Position : ((x == 0 && z == 0) ? Vector3.zero : new Vector3((float)x, 0f, (float)z)));
				if (_pickupActive)
				{
					_drop = val;
					_pkgPlaced = val != null;
					_dropPoint = val2;
				}
				else
				{
					_crateDrop = val;
					_cratePlaced = val != null;
					_cratePoint = val2;
				}
				SyncEntry();
			}
		}

		private static DeadDropInstance ResolveDropByPos(int x, int z)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (x == 0 && z == 0)
				{
					return null;
				}
				DeadDropInstance[] all = DeadDropManager.All;
				if (all == null)
				{
					return null;
				}
				DeadDropInstance val = null;
				float num = float.MaxValue;
				for (int i = 0; i < all.Length; i++)
				{
					if (all[i] != null)
					{
						float num2 = all[i].Position.x - (float)x;
						float num3 = all[i].Position.z - (float)z;
						float num4 = num2 * num2 + num3 * num3;
						if (num4 < num)
						{
							num = num4;
							val = all[i];
						}
					}
				}
				return (num <= 9f) ? val : null;
			}
			catch
			{
				return null;
			}
		}

		private static void HostErrandItemPicked()
		{
			if (Stage == 3)
			{
				Stage = 4;
				SyncEntry();
			}
			else if (_pickupActive && !_hasPackage)
			{
				_hasPackage = true;
				SyncEntry();
			}
		}

		private static void HostCheckedRv()
		{
			if (Stage == 9)
			{
				Stage = 10;
				RepairQuest.CompleteIfActive();
				WorldSay(_marcoT, L10n.T("There she is. Standing again. Interior's your problem. Try not to piss off whoever torched it the first time."));
				Core.Log.Msg("[Questline] quest complete (RV checked).");
			}
		}

		private static void ApplyRepair()
		{
			if (!NetworkBus.IsServer)
			{
				RVManager.RepairVisualOnly();
				RepairStateStore.SetRepaired(repaired: true);
				RepairQuest.CompleteIfActive();
			}
		}

		internal static void HostPayRepair(bool withCinematic)
		{
			try
			{
				if (!RVManager.IsDestroyed())
				{
					return;
				}
				int num = (RVRepairVanPreferences.QuestlineEnabled ? CurrentPrice() : RVRepairVanPreferences.RepairPrice);
				if (Money.GetCashBalance() < (float)num)
				{
					Core.Log.Msg("[Net] host PayRepair rejected - shared pool short (" + MoneyManager.FormatAmount((float)num, false, false) + ").");
					return;
				}
				Money.ChangeCashBalance((float)(-num), true, true);
				int paid = num;
				Action commit = delegate
				{
					if (RVManager.Repair())
					{
						RepairStateStore.SetRepaired(repaired: true);
						NetworkBus.BroadcastToAll(RvOp.RepairApplied);
						if (RVRepairVanPreferences.QuestlineEnabled)
						{
							Stage = 9;
							SyncEntry();
						}
						else
						{
							RepairQuest.CompleteIfActive();
						}
						Core.Log.Msg("[Net] host repaired the RV for " + MoneyManager.FormatAmount((float)paid, false, false) + " (broadcast to clients).");
					}
				};
				if (withCinematic)
				{
					RepairCinematic.Play(delegate
					{
						commit();
					}, delegate
					{
						WorldSay(_marcoT, L10n.T("There she is - back from the dead. Go take a look, and try not to total her again."));
					}, delegate
					{
						GruntNpc(FindNpc("marco_baron"));
					});
				}
				else
				{
					commit();
				}
			}
			catch (Exception ex)
			{
				Core.Log.Error("[Net] host repair failed: " + ex);
			}
		}
	}
	internal static class RepairQuest
	{
		internal static readonly string Title = L10n.T("Back on the Road");

		internal static bool IsActive()
		{
			try
			{
				return QuestManager.GetQuestByName(Title) != null;
			}
			catch
			{
				return false;
			}
		}

		internal static bool HasEntry()
		{
			try
			{
				Quest questByName = QuestManager.GetQuestByName(Title);
				return questByName?.QuestEntries != null && questByName.QuestEntries.Count > 0;
			}
			catch
			{
				return false;
			}
		}

		internal static void StartIfNeeded()
		{
			try
			{
				if (IsActive())
				{
					Core.Log.Msg("[Quest] '" + Title + "' already active.");
					return;
				}
				QuestManager.CreateQuest<RepairRVQuest>((string)null);
				Core.Log.Msg("[Quest] '" + Title + "' started.");
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Quest] start failed: " + ex.Message);
			}
		}

		internal static void UpdateEntry(string title, Vector3 poi)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Quest questByName = QuestManager.GetQuestByName(Title);
				if (questByName?.QuestEntries != null && questByName.QuestEntries.Count > 0)
				{
					QuestEntry obj = questByName.QuestEntries[0];
					obj.Title = title;
					obj.POIPosition = poi;
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Quest] update entry failed: " + ex.Message);
			}
		}

		internal static void UpdateEntry(string title, NPC npc, Vector3 fallback)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Quest questByName = QuestManager.GetQuestByName(Title);
				if (questByName?.QuestEntries != null && questByName.QuestEntries.Count != 0)
				{
					QuestEntry val = questByName.QuestEntries[0];
					val.Title = title;
					NPC val2 = (((Object)(object)npc != (Object)null) ? NPC.Get(npc.ID) : null);
					if (val2 == null || !val.SetPOIToNPC(val2))
					{
						val.POIPosition = fallback;
					}
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Quest] update entry failed: " + ex.Message);
			}
		}

		internal static void CompleteIfActive()
		{
			try
			{
				Quest questByName = QuestManager.GetQuestByName(Title);
				if (questByName == null)
				{
					return;
				}
				if (questByName.QuestEntries != null)
				{
					foreach (QuestEntry questEntry in questByName.QuestEntries)
					{
						try
						{
							questEntry.Complete();
						}
						catch
						{
						}
					}
				}
				Core.Log.Msg("[Quest] '" + Title + "' completed.");
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Quest] complete failed: " + ex.Message);
			}
		}
	}
	public class RepairRVQuest : Quest
	{
		private static Sprite _icon;

		private static bool _iconTried;

		protected override string Title => RepairQuest.Title;

		protected override string Description => L10n.T("Your RV's wrecked. Someone in Hyland Point has to know a guy.");

		protected override bool AutoBegin => true;

		protected override Sprite QuestIcon
		{
			get
			{
				if (!_iconTried)
				{
					_iconTried = true;
					_icon = LoadIcon();
				}
				return _icon;
			}
		}

		private static Sprite LoadIcon()
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("RVRepairVan.quest_icon.png");
				if (stream == null)
				{
					Core.Log.Warning("[Quest] icon resource missing");
					return null;
				}
				byte[] array = new byte[stream.Length];
				int num;
				for (int i = 0; i < array.Length; i += num)
				{
					num = stream.Read(array, i, array.Length - i);
					if (num <= 0)
					{
						break;
					}
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
				{
					filterMode = (FilterMode)1
				};
				ImageConversion.LoadImage(val, Il2CppStructArray<byte>.op_Implicit(array), false);
				return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Quest] icon load failed: " + ex.Message);
				return null;
			}
		}

		protected override void OnCreated()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			((Registerable)this).OnCreated();
			try
			{
				((Quest)this).AddEntry(L10n.T("Ask the motel manager about the RV"), (Vector3?)null).POIPosition = (RVManager.TryGetPosition(out var position) ? position : Vector3.zero);
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Quest] OnCreated failed: " + ex.Message);
			}
		}
	}
}
namespace RVRepairVan.Persistence
{
	public class RepairSave : Saveable
	{
		[SaveableField("rv_repaired")]
		private bool _repaired;

		[SaveableField("rv_stage")]
		private int _stage;

		[SaveableField("rv_samples")]
		private int _samples;

		[SaveableField("rv_discount")]
		private int _discount;

		internal static RepairSave Instance { get; private set; }

		internal static bool Loaded { get; private set; }

		internal bool Repaired
		{
			get
			{
				return _repaired;
			}
			set
			{
				_repaired = value;
			}
		}

		internal int Stage
		{
			get
			{
				return _stage;
			}
			set
			{
				_stage = value;
			}
		}

		internal int Samples
		{
			get
			{
				return _samples;
			}
			set
			{
				_samples = value;
			}
		}

		internal int Discount
		{
			get
			{
				return _discount;
			}
			set
			{
				_discount = value;
			}
		}

		public RepairSave()
		{
			Instance = this;
		}

		internal static void BeginLoad()
		{
			Loaded = false;
			if (Instance != null)
			{
				Instance._repaired = false;
				Instance._stage = 0;
				Instance._samples = 0;
				Instance._discount = 0;
			}
		}

		protected override void OnLoaded()
		{
			Instance = this;
			Loaded = true;
			Core.Log.Msg($"[State] loaded: repaired={_repaired} stage={_stage} samples={_samples} discount={_discount}");
		}

		protected override void OnCreated()
		{
			Instance = this;
			_repaired = false;
			_stage = 0;
			_samples = 0;
			_discount = 0;
			Loaded = true;
			Core.Log.Msg("[State] created (fresh save) - defaults applied.");
		}

		protected override void OnSaved()
		{
			Core.Log.Msg($"[State] saved: repaired={_repaired} stage={_stage} samples={_samples} discount={_discount}");
		}
	}
	internal static class RepairStateStore
	{
		private static bool _cRepaired;

		private static int _cStage;

		private static int _cSamples;

		private static int _cDiscount;

		private static RepairSave S => RepairSave.Instance;

		private static bool ClientMode
		{
			get
			{
				if (NetworkBus.Online)
				{
					return !NetworkBus.IsServer;
				}
				return false;
			}
		}

		internal static void ResetClient()
		{
			_cRepaired = false;
			_cStage = 0;
			_cSamples = 0;
			_cDiscount = 0;
		}

		internal static bool GetRepaired()
		{
			if (!ClientMode)
			{
				if (S != null)
				{
					return S.Repaired;
				}
				return false;
			}
			return _cRepaired;
		}

		internal static void SetRepaired(bool repaired)
		{
			if (ClientMode)
			{
				_cRepaired = repaired;
			}
			else if (S != null)
			{
				S.Repaired = repaired;
			}
		}

		internal static int GetStage()
		{
			if (!ClientMode)
			{
				if (S == null)
				{
					return 0;
				}
				return S.Stage;
			}
			return _cStage;
		}

		internal static void SetStage(int stage)
		{
			if (ClientMode)
			{
				_cStage = stage;
			}
			else if (S != null)
			{
				S.Stage = stage;
			}
		}

		internal static int GetSamples()
		{
			if (!ClientMode)
			{
				if (S == null)
				{
					return 0;
				}
				return S.Samples;
			}
			return _cSamples;
		}

		internal static void SetSamples(int samples)
		{
			if (ClientMode)
			{
				_cSamples = samples;
			}
			else if (S != null)
			{
				S.Samples = samples;
			}
		}

		internal static int GetDiscountTotal()
		{
			if (!ClientMode)
			{
				if (S == null)
				{
					return 0;
				}
				return S.Discount;
			}
			return _cDiscount;
		}

		internal static void SetDiscountTotal(int discount)
		{
			if (ClientMode)
			{
				_cDiscount = discount;
			}
			else if (S != null)
			{
				S.Discount = discount;
			}
		}
	}
}
namespace RVRepairVan.Net
{
	internal static class NetworkBus
	{
		internal static Action<RvMsg> OnServerIntent;

		internal static Action<RvMsg> OnClientState;

		private static NetworkManager Nm
		{
			get
			{
				try
				{
					return InstanceFinder.NetworkManager;
				}
				catch
				{
					return null;
				}
			}
		}

		internal static bool Online
		{
			get
			{
				NetworkManager nm = Nm;
				try
				{
					return (Object)(object)nm != (Object)null && (nm.IsServer || nm.IsClient);
				}
				catch
				{
					return false;
				}
			}
		}

		internal static bool IsServer
		{
			get
			{
				NetworkManager nm = Nm;
				try
				{
					return (Object)(object)nm != (Object)null && nm.IsServer;
				}
				catch
				{
					return false;
				}
			}
		}

		private static QuestManager Qm
		{
			get
			{
				try
				{
					return NetworkSingleton<QuestManager>.Instance;
				}
				catch
				{
					return null;
				}
			}
		}

		internal static void SendToHost(RvOp op, int a = 0, int b = 0, int c = 0)
		{
			try
			{
				QuestManager qm = Qm;
				if ((Object)(object)qm == (Object)null)
				{
					Core.Log.Warning("[Net] no QuestManager - intent " + op.ToString() + " dropped.");
				}
				else
				{
					qm.SendQuestState(RvMsg.Encode(op, a, b, c), (EQuestState)0);
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Net] SendToHost failed: " + ex.Message);
			}
		}

		internal static void BroadcastToAll(RvOp op, int a = 0, int b = 0, int c = 0)
		{
			try
			{
				QuestManager qm = Qm;
				if (!((Object)(object)qm == (Object)null))
				{
					qm.ReceiveQuestState((NetworkConnection)null, RvMsg.Encode(op, a, b, c), (EQuestState)0);
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Net] BroadcastToAll failed: " + ex.Message);
			}
		}

		internal static void DispatchServerIntent(RvMsg m)
		{
			try
			{
				OnServerIntent?.Invoke(m);
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Net] server dispatch failed: " + ex.Message);
			}
		}

		internal static void DispatchClientState(RvMsg m)
		{
			try
			{
				if (IsServer)
				{
					OnServerIntent?.Invoke(m);
				}
				else
				{
					OnClientState?.Invoke(m);
				}
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Net] dispatch failed: " + ex.Message);
			}
		}

		internal static void Init(Harmony h)
		{
			QuestManagerNetPatches.Apply(h);
		}
	}
	internal enum RvOp
	{
		AcceptErrand = 0,
		DeliverCrate = 1,
		MingPayLoss = 2,
		MarcoGreet = 3,
		MarcoReferral = 4,
		MarcoFavour = 5,
		GotPackage = 6,
		MarcoPayLoss = 7,
		GiveSample = 8,
		PayRepair = 9,
		RequestSnapshot = 10,
		AskDonna = 11,
		CheckedRv = 12,
		ErrandItemPicked = 13,
		StageSync = 100,
		RepairApplied = 101,
		TransientSync = 102,
		Ping = 200
	}
	internal struct RvMsg
	{
		internal const string Prefix = "RVRV:";

		internal RvOp Op;

		internal int A;

		internal int B;

		internal int C;

		internal static string Encode(RvOp op, int a = 0, int b = 0, int c = 0)
		{
			string[] obj = new string[8] { "RVRV:", null, null, null, null, null, null, null };
			int num = (int)op;
			obj[1] = num.ToString();
			obj[2] = ":";
			obj[3] = a.ToString();
			obj[4] = ":";
			obj[5] = b.ToString();
			obj[6] = ":";
			obj[7] = c.ToString();
			return string.Concat(obj);
		}

		internal static bool TryDecode(string guid, out RvMsg msg)
		{
			msg = default(RvMsg);
			if (string.IsNullOrEmpty(guid) || !guid.StartsWith("RVRV:", StringComparison.Ordinal))
			{
				return false;
			}
			try
			{
				string[] array = guid.Substring("RVRV:".Length).Split(':');
				if (array.Length < 1 || !int.TryParse(array[0], out var result))
				{
					return false;
				}
				msg.Op = (RvOp)result;
				if (array.Length > 1)
				{
					int.TryParse(array[1], out msg.A);
				}
				if (array.Length > 2)
				{
					int.TryParse(array[2], out msg.B);
				}
				if (array.Length > 3)
				{
					int.TryParse(array[3], out msg.C);
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		public override string ToString()
		{
			return Op.ToString() + "(" + A + "," + B + "," + C + ")";
		}
	}
}
namespace RVRepairVan.Net.Patches
{
	internal static class QuestManagerNetPatches
	{
		internal static void Apply(Harmony h)
		{
			TryPatch(h, "RpcLogic___SendQuestState", "SendStatePrefix");
			TryPatch(h, "RpcLogic___ReceiveQuestState", "ReceiveStatePrefix");
		}

		private static void TryPatch(Harmony h, string namePrefix, string prefixMethod)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = typeof(QuestManager).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name.StartsWith(namePrefix, StringComparison.Ordinal));
				if (methodInfo == null)
				{
					Core.Log.Warning("[Net] could not find " + namePrefix + " - co-op sync disabled.");
					return;
				}
				MethodInfo method = typeof(QuestManagerNetPatches).GetMethod(prefixMethod, BindingFlags.Static | BindingFlags.NonPublic);
				h.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				Core.Log.Warning("[Net] patch " + namePrefix + " failed: " + ex.Message);
			}
		}

		private static bool SendStatePrefix(string __0)
		{
			if (RvMsg.TryDecode(__0, out var msg))
			{
				NetworkBus.DispatchServerIntent(msg);
				return false;
			}
			return true;
		}

		private static bool ReceiveStatePrefix(string __1)
		{
			if (RvMsg.TryDecode(__1, out var msg))
			{
				NetworkBus.DispatchClientState(msg);
				return false;
			}
			return true;
		}
	}
}
namespace RVRepairVan.Managers
{
	internal static class RVManager
	{
		private static Transform _root;

		private static RV _rv;

		private static Transform _model;

		private static Transform _destroyed;

		private static Transform _cartelNote;

		internal static bool IsReady
		{
			get
			{
				if ((Object)(object)_root != (Object)null)
				{
					return (Object)(object)_rv != (Object)null;
				}
				return false;
			}
		}

		internal static bool TryGetPosition(out Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			position = Vector3.zero;
			try
			{
				if (!TryLocate())
				{
					return false;
				}
				position = _root.position;
				return true;
			}
			catch
			{
				return false;
			}
		}

		internal static void Reset()
		{
			_root = null;
			_rv = null;
			_model = null;
			_destroyed = null;
			_cartelNote = null;
		}

		[Conditional("DEBUG")]
		internal static void LogState()
		{
			try
			{
				Il2CppArrayBase<RV> val = Object.FindObjectsOfType<RV>(true);
				int num = val?.Length ?? 0;
				Core.Log.Msg($"[RVManager] DIAG: FindObjectsOfType<RV>(true) -> {num} RV component(s)");
				for (int i = 0; i < num; i++)
				{
					RV val2 = val[i];
					if (!((Object)(object)val2 == (Object)null))
					{
						Transform transform = ((Component)val2).transform;
						bool value = false;
						bool value2 = false;
						try
						{
							value = val2.IsDestroyed;
						}
						catch
						{
						}
						try
						{
							value2 = val2._exploded;
						}
						catch
						{
						}
						string text = "";
						for (int j = 0; j < transform.childCount; j++)
						{
							Transform child = transform.GetChild(j);
							text = text + ((Object)child).name + "[" + (((Component)child).gameObject.activeSelf ? "ON" : "off") + "] ";
						}
						Core.Log.Msg($"[RVManager] DIAG: RV#{i} path='{FullPath(transform)}' activeInHierarchy={((Component)transform).gameObject.activeInHierarchy} IsDestroyed={value} _exploded={value2}");
						Core.Log.Msg($"[RVManager] DIAG: RV#{i} children -> {text}");
					}
				}
				GameObject val3 = GameObject.Find("@Properties");
				if ((Object)(object)val3 != (Object)null)
				{
					string text2 = "";
					for (int k = 0; k < val3.transform.childCount; k++)
					{
						Transform child2 = val3.transform.GetChild(k);
						text2 = text2