Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of RVRepairVan v2.6.2
RVRepairVan.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
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