Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of RVRepairVan v4.3.1
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.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using Chassis.Api; using DooDesch; using DooDesch.Localization; using DooDesch.Transition; using HarmonyLib; using Il2Cpp; using Il2CppFishNet; using Il2CppFishNet.Connection; using Il2CppFishNet.Managing; using Il2CppFishNet.Object; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne; using Il2CppScheduleOne.Audio; using Il2CppScheduleOne.Core.Audio; using Il2CppScheduleOne.Core.Items.Framework; using Il2CppScheduleOne.Core.Weather; using Il2CppScheduleOne.Delivery; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Dialogue; using Il2CppScheduleOne.Doors; using Il2CppScheduleOne.Employees; using Il2CppScheduleOne.EntityFramework; using Il2CppScheduleOne.GameTime; using Il2CppScheduleOne.Interaction; using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.Map; using Il2CppScheduleOne.Misc; using Il2CppScheduleOne.Money; using Il2CppScheduleOne.NPCs; using Il2CppScheduleOne.NPCs.Behaviour; using Il2CppScheduleOne.NPCs.CharacterClasses; using Il2CppScheduleOne.Persistence; using Il2CppScheduleOne.Persistence.Datas; using Il2CppScheduleOne.PlayerScripts; using Il2CppScheduleOne.PlayerTasks; using Il2CppScheduleOne.Product; using Il2CppScheduleOne.Property; using Il2CppScheduleOne.Quests; using Il2CppScheduleOne.Tiles; using Il2CppScheduleOne.UI; using Il2CppScheduleOne.UI.Phone.Delivery; using Il2CppScheduleOne.Vehicles; using Il2CppScheduleOne.VoiceOver; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using MarcosBodyshop.Api; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using RVRepairVan; using RVRepairVan.Api; using RVRepairVan.Base; using RVRepairVan.Compat; using RVRepairVan.Config; using RVRepairVan.Dialogue; using RVRepairVan.Effects; using RVRepairVan.Garage; 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.Messaging; using S1API.Money; using S1API.Quests; using S1API.Quests.Constants; using S1API.Saveables; using Unity.AI.Navigation; using UnityEngine; using UnityEngine.AI; using UnityEngine.Events; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Core), "RVRepairVan", "4.3.1", "DooDesch", "https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan")] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: MelonAdditionalDependencies(new string[] { "Chassis", "MarcosBodyshop" })] [assembly: AssemblyMetadata("SourceNotice", "RVRepairVan by DooDesch, MIT License. The source code is public at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan. Please read, change and reuse the source code, not decompiled code from this file. Text and data mining reserved under Art. 4(3) Directive (EU) 2019/790 and § 44b(3) UrhG.")] [assembly: AssemblyMetadata("License", "https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan/blob/main/LICENSE.md")] [assembly: AssemblyMetadata("SourceCode", "https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan")] [assembly: AssemblyMetadata("tdm-reservation", "1")] [assembly: AssemblyMetadata("tdm-policy", "https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan/blob/main/LICENSE.md")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("DooDesch")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © DooDesch. MIT License. Source code: https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan")] [assembly: AssemblyFileVersion("4.3.1.0")] [assembly: AssemblyInformationalVersion("4.3.1+7ae6dce554947f4096b73230bdfe607f647349fc")] [assembly: AssemblyProduct("RVRepairVan")] [assembly: AssemblyTitle("RVRepairVan")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially created with the assistance of Generative AI.")] [assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic/OpenAI")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("4.3.1.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 { [AttributeUsage(AttributeTargets.All, Inherited = false)] internal sealed class SourceNoticeAttribute : Attribute { public string Notice { get; } public SourceNoticeAttribute(string notice) { Notice = notice; } } [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] internal static class SourceNotice { internal const string Text = "RVRepairVan by DooDesch, MIT License. The source code is public at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan. Please read, change and reuse the source code, not decompiled code from this file. Text and data mining reserved under Art. 4(3) Directive (EU) 2019/790 and § 44b(3) UrhG."; internal const string Short = "RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code."; } [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] internal static class ModVersion { internal const string Current = "4.3.1"; } } namespace DooDesch.Localization { [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] 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 { [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] 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, (Action)null); } else { instance.Close(t, (Action)null); } } 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 { } } } [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] internal enum VeilMode { Eyelids, Black, EyelidsAndBlack } [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] 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; } [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] 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 Hash.Api { [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] public static class HashCommands { private const string BridgeTypeName = "Hash.Bridge.HashBridge, Hash"; private static readonly List<string[]> _pending = new List<string[]>(); private static Action<string, string, string, string> _declare; private static bool _bound; public static bool Available { get { Bind(); return _bound; } } private static string Owner { get { try { return Assembly.GetExecutingAssembly().GetName().Name ?? ""; } catch { return ""; } } } public static void Add(string word, string description, string example = null) { if (!string.IsNullOrEmpty(word)) { Bind(); if (_declare != null) { Safely(word, description, example); return; } _pending.Add(new string[3] { word, description ?? "", example ?? "" }); } } private static void Safely(string word, string description, string example) { try { _declare(word, description ?? "", example ?? "", Owner); } catch { } } private static void Bind() { if (_bound) { return; } try { Type type = Type.GetType("Hash.Bridge.HashBridge, Hash", throwOnError: false); if (type == null) { return; } _declare = type.GetField("Declare", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) as Action<string, string, string, string>; if (_declare == null) { return; } _bound = true; foreach (string[] item in _pending) { Safely(item[0], item[1], item[2]); } _pending.Clear(); } catch { } } } } namespace RVRepairVan { [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] public sealed class Core : MelonMod { private static readonly HashSet<string> _swallowed = new HashSet<string>(); private static int _sceneGeneration; private const float GameLoadTimeout = 300f; private const float RestoreWatchInterval = 3f; public static Core Instance { get; private set; } public static Instance Log { get; private set; } internal static int SceneGeneration => _sceneGeneration; [Conditional("DEBUG")] public static void LogDebug(string msg) { Instance log = Log; if (log != null) { log.Msg(msg); } } internal static void Swallowed(string where, Exception e) { lock (_swallowed) { if (!_swallowed.Add(where)) { return; } } Instance log = Log; if (log != null) { log.Warning("[Swallowed] " + where + ": " + e.GetType().Name + ": " + e.Message); } } public override void OnInitializeMelon() { Instance = this; Log = ((MelonBase)this).LoggerInstance; RVRepairVanPreferences.Initialize(); German.Register(); NetworkBus.Init(((MelonBase)this).HarmonyInstance); RvGarage.Register(); RvPaint.Init(); Log.Msg($"RVRepairVan initialized. Enabled={RVRepairVanPreferences.Enabled}, Questline={RVRepairVanPreferences.QuestlineEnabled}, RepairPrice={RVRepairVanPreferences.RepairPrice}"); } public override void OnFixedUpdate() { try { RvCargo.FixedStep(); } catch (Exception e) { Swallowed("cargo physics step", e); } } public override void OnLateUpdate() { try { RvCargo.LateStep(); } catch (Exception e) { Swallowed("cargo frame", e); } try { RvRide.LateStep(); } catch (Exception e2) { Swallowed("ride frame", e2); } try { RvRiders.Draw(); } catch (Exception e3) { Swallowed("riders frame", e3); } } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { if (sceneName == "Menu") { _sceneGeneration++; RepairSave.BeginLoad(); } else if (!(sceneName != "Main")) { RVManager.Reset(); RepairCinematic.ForceReset(); Questline.InitNet(); if (RVRepairVanPreferences.QuestlineEnabled) { Questline.Reset(); Questline.Start(); } else { MarcoRepairDialogue.Reset(); MelonCoroutines.Start(MarcoRepairDialogue.SetupCoroutine()); RepairStateStore.ResetClient(); MelonCoroutines.Start(Questline.NetJoinCoroutineShared()); MelonCoroutines.Start(BuildOutQuest.CancelLeftoverCoroutine()); } RvUpgrades.Reset(); ClientPayment.Reset(); _sceneGeneration++; MelonCoroutines.Start(RestoreRepairCoroutine(_sceneGeneration)); MelonCoroutines.Start(RvRepairJob.InstantRepairCoroutine()); MelonCoroutines.Start(ScheduleMaintenance.Watch(_sceneGeneration)); } } public override void OnPreferencesSaved() { try { MarcoRepairDialogue.RefreshPrice(); Questline.RefreshPrice(); } catch (Exception ex) { Log.Warning("[Prefs] OnPreferencesSaved failed: " + ex.Message); } } private static IEnumerator RestoreRepairCoroutine(int generation) { float waited = 0f; while (!RepairSave.Loaded && waited < 10f) { yield return (object)new WaitForSeconds(0.5f); waited += 0.5f; } if (!RepairSave.Loaded) { Log.Warning("[Restore] save state not loaded after 10 s - the repair check runs on defaults."); } Log.Msg("[Restore] RV destroyed at scene start: " + SafeIsDestroyed() + "."); waited = 0f; while (!GameLoaded() && waited < 300f) { if (generation != _sceneGeneration) { Log.Msg("[Restore] a newer load took over - this check stops."); yield break; } yield return (object)new WaitForSeconds(0.5f); waited += 0.5f; } if (generation != _sceneGeneration) { Log.Msg("[Restore] a newer load took over - this check stops."); yield break; } if (waited >= 300f) { Log.Warning("[Restore] the game did not report a finished load after " + 300f + " s - checking the RV anyway."); } else { Log.Msg("[Restore] game load finished after " + waited.ToString("0.0") + " s."); } if (NetworkBus.Online && !NetworkBus.IsServer) { Log.Msg("[Restore] co-op client - deferring RV state to the host (no local restore)."); yield break; } if (NeedsRestore()) { Restore("RV was previously repaired - restoring without charge."); } RvRepairJob.ResumeIfRunning(); for (int i = 0; i < 5; i++) { try { RvUpgrades.ApplyOwned(); } catch (Exception ex) { Log.Warning("[Restore] upgrade apply failed: " + ex.Message); } yield return (object)new WaitForSeconds(2f); } while (generation == _sceneGeneration) { yield return (object)new WaitForSeconds(3f); if (generation != _sceneGeneration) { break; } if (NeedsRestore()) { Restore("a paid-for RV was wrecked again after the load - restoring without charge."); if (NetworkBus.Online && NetworkBus.IsServer) { NetworkBus.BroadcastToAll(RvOp.RepairApplied); } try { RvUpgrades.ApplyOwned(); } catch (Exception ex2) { Log.Warning("[Restore] upgrade apply after the late restore failed: " + ex2.Message); } } } } private static bool GameLoaded() { try { LoadManager instance = Singleton<LoadManager>.Instance; return (Object)(object)instance != (Object)null && instance.IsGameLoaded; } catch (Exception e) { Swallowed("Core.GameLoaded", e); return false; } } private static string SafeIsDestroyed() { try { return RVManager.TryLocate() ? RVManager.IsDestroyed().ToString() : "no RV found"; } catch (Exception ex) { return "unknown (" + ex.Message + ")"; } } private static bool NeedsRestore() { try { return RepairStateStore.GetRepaired() && !RvRepairJob.JobRunning && RVManager.TryLocate() && RVManager.IsDestroyed(); } catch (Exception ex) { Log.Warning("[Restore] check failed: " + ex.Message); return false; } } private static void Restore(string why) { Log.Msg("[Restore] " + why); RVManager.Repair(); } } } namespace RVRepairVan.Quests { [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] internal static class BuildOutQuest { internal static readonly string Title = L10n.T("Home on Wheels"); internal const int StepAsk = 0; internal const int StepEngine = 1; internal const int StepDrive = 2; internal const int StepFloor = 3; internal const int StepCount = 4; internal static void Tick(bool storyOver, bool asked) { Quest questByName = QuestManager.GetQuestByName(Title); bool flag = RvUpgrades.Owned(RvUpgrade.Engine); bool flag2 = RvUpgrades.Owned(RvUpgrade.WorkshopFloor); if (flag && flag2) { if (questByName != null) { Finish(); } } else if (!(RVRepairVanPreferences.Enabled && RVRepairVanPreferences.QuestlineEnabled && RVRepairVanPreferences.UpgradesEnabled && RepairStateStore.GetRepaired() && storyOver)) { if (questByName != null) { Core.Log.Msg("[Quest] '" + Title + "' cancelled - repaired=" + RepairStateStore.GetRepaired() + " storyOver=" + storyOver + " upgrades=" + RVRepairVanPreferences.UpgradesEnabled + "."); QuestSteps.Forget(Title); questByName.Cancel(); } } else if (questByName == null) { QuestSteps.Forget(Title); QuestManager.CreateQuest<BuildOutRVQuest>((string)null); Core.Log.Msg("[Quest] '" + Title + "' started (engine=" + flag + " floor=" + flag2 + " asked=" + asked + ")."); } else { Sync(asked || RvUpgrades.Mask != 0, flag, flag2); } } private static void Sync(bool asked, bool engine, bool floor) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_0072: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) if (QuestSteps.TryOpen(Title, 4, out var steps)) { bool flag = engine && !floor && RvGarage.InGarage(); Vector3 val = Questline.MarcoPosition(); steps.Set(0, (QuestState)((!asked) ? 1 : 2), null, "marco_baron", val); steps.Set(1, (QuestState)(engine ? 2 : (asked ? 1 : 0)), L10n.T("Have Marco put the engine in ({0})", MoneyManager.FormatAmount((float)RvUpgrades.Price(RvUpgrade.Engine), false, false)), "marco_baron", val); steps.Set(2, (QuestState)(engine ? ((!(floor || flag)) ? 1 : 2) : 0), null, null, RvGarage.LotPoint(val)); steps.Set(3, (QuestState)(floor ? 2 : (flag ? 1 : 0)), L10n.T("Have Marco put in the workshop floor there ({0})", MoneyManager.FormatAmount((float)RvUpgrades.Price(RvUpgrade.WorkshopFloor), false, false)), "marco_baron", val); } } private static void Finish() { QuestSteps.CompleteAll(Title); if (!NetworkBus.Online || NetworkBus.IsServer) { RvRepairJob.TextFromMarco(L10n.T("Floor's in. You can set up shop in there now. Crew room and a loading dock I can do too, just ask.")); } } internal static IEnumerator CancelLeftoverCoroutine() { for (int i = 0; i < 15; i++) { if (RepairSave.Loaded) { break; } yield return (object)new WaitForSeconds(1f); } yield return (object)new WaitForSeconds(3f); try { Quest questByName = QuestManager.GetQuestByName(Title); if (questByName != null) { Core.Log.Msg("[Quest] '" + Title + "' cancelled - QuestMode is Simple."); questByName.Cancel(); } } catch (Exception ex) { Core.Log.Warning("[Quest] cancelling the leftover build-out quest failed: " + ex.Message); } } } [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] public class BuildOutRVQuest : Quest { protected override string Title => BuildOutQuest.Title; protected override string Description => L10n.T("Marco can do more to the RV."); protected override bool AutoBegin => true; protected override Sprite QuestIcon => RepairRVQuest.SharedIcon; protected override void OnCreated() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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_008e: Unknown result type (might be due to invalid IL or missing references) ((Registerable)this).OnCreated(); try { Vector3 position; Vector3 pOIPosition = (RVManager.TryGetPosition(out position) ? position : Vector3.zero); ((Quest)this).AddEntry(L10n.T("Ask Marco about the build-outs"), (Vector3?)null).POIPosition = pOIPosition; ((Quest)this).AddEntry(L10n.T("Have Marco put the engine in"), (Vector3?)null).POIPosition = pOIPosition; ((Quest)this).AddEntry(L10n.T("Drive the RV onto Marco's lot"), (Vector3?)null).POIPosition = pOIPosition; ((Quest)this).AddEntry(L10n.T("Have Marco put in the workshop floor there"), (Vector3?)null).POIPosition = pOIPosition; } catch (Exception ex) { Core.Log.Warning("[Quest] build-out OnCreated failed: " + ex.Message); } } } [SourceNotice("RVRepairVan by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-RVRepairVan, not decompiled code.")] 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 int BuildOutsAsked = 11; private const string DonnaId = "donna_martin"; private const string MingId = "ming"; internal const string MarcoId = "marco_baron"; private const string MarcoSayName = "rv_marco_say"; private const string MingSayName = "rv_ming_say"; private const string MoreEngineName = "rv_marco_more_engine"; private const int BuildOutTickDelay = 5; private const string CrateId = "rv_ming_crate"; private const string PackageId = "rv_marco_package"; private const int LostPackageFee = 500; private const string MingAngry = "You lose my crate? Not very good. Five hundred, then we are okay, yes?"; private const string MingPaid = "Very good. Now go see Marco at the docks, tell him Ming sends you."; private const string MingShort = "Okay. You bring money, yes?"; private const string MarcoAngry = "You lost it? Well, that's gonna cost ya. Five hundred and we're even."; private const string MarcoPaid = "Pleasure doing business. Now, bring me packaged product in your hand, and every piece comes off your bill."; private const string MarcoShort = "I'll be here."; 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 bool _errandRestored; private static int _gen; private static Conversation _marcoSay; private static Conversation _mingSay; private static Conversation _marcoQuote; private static Conversation _marcoMoreEngine; 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 static readonly RvUpgrade[] UpgradeOrder = new RvUpgrade[3] { RvUpgrade.CrewQuarters, RvUpgrade.LoadingDock, RvUpgrade.Engine }; private static readonly DialogueChoice[] _upgradeChoices = (DialogueChoice[])(object)new DialogueChoice[UpgradeOrder.Length]; internal const int MarcoPriorityTop = -100; private const int MAX_SAMPLE_CHOICES = 1; private static readonly DialogueChoice[] _sampleChoices = (DialogueChoice[])(object)new DialogueChoice[1]; 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 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 StageForLoad(bool repaired, int stage) { if (!repaired || stage < 1 || stage >= 9) { return stage; } Core.Log.Msg("[Questline] save has a repaired RV at stage " + stage + " - loading it as finished."); return 10; } 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 (Exception e) { Core.Swallowed("Questline.ExplosionBeatPassed", e); } return false; } internal static void Reset() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_007f: Unknown result type (might be due to invalid IL or missing references) _gen++; ResetDiag(); _donnaDone = (_mingDone = (_marcoDone = false)); _pickupActive = false; _hasPackage = false; _clientCheckedRv = false; _errandRestored = false; _marcoSay = (_mingSay = (_marcoQuote = (_marcoMoreEngine = null))); QuestSteps.ResetScene(); _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(_upgradeChoices, 0, _upgradeChoices.Length); Array.Clear(_sampleChoices, 0, _sampleChoices.Length); _sampleSlots.Clear(); RepairStateStore.ResetClient(); RvBattery.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 (RVRepairVanPreferences.Enabled && 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) && RVRepairVanPreferences.Enabled && 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) { Conversation val = S1Container(npc, "rv_donna", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", L10n.T("I run a motel, not a garage. Try Mrs. Ming at the Chinese restaurant, ok? 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) { Conversation val = S1Container(npc, "rv_ming_offer", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", L10n.T("Oh yes! Marco, at the docks. Very good mechanic. But first you do small favor for me, yes? Crate in a dead drop. You bring it here, I call Marco."), (Action<ChoiceList>)delegate(ChoiceList c) { c.Add("MING_ACCEPT", L10n.T("I'll get it."), "MING_ACCEPTED").Add("MING_DEFER", L10n.T("Not now."), "MING_DEFERRED"); }).AddNode("MING_ACCEPTED", L10n.T("Very good! And do not open, yes?"), (Action<ChoiceList>)null).AddNode("MING_DEFERRED", L10n.T("Okay. You come back when ready."), (Action<ChoiceList>)null); }); Conversation val2 = S1Container(npc, "rv_ming_deliver", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", L10n.T("Very good! You are careful. I like careful. I call Marco now. Go see him at the docks, tell him Ming sends you."), (Action<ChoiceList>)null); }); Conversation val3 = S1Container(npc, "rv_ming_lost", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", L10n.T("You lose my crate? Not very good. Five hundred, then we are okay, yes?"), (Action<ChoiceList>)delegate(ChoiceList c) { c.Add("MING_PAY", L10n.T("Pay ${0}", 500), (string)null).Add("MING_DEFER_LOSS", L10n.T("I'll come back with the money."), "MING_LOSS_DEFER"); }).AddNode("MING_LOSS_DEFER", L10n.T("Okay. You bring money, yes?"), (Action<ChoiceList>)null); }); _mingSay = S1Container(npc, "rv_ming_say", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", "...", (Action<ChoiceList>)null); }); if (!degraded && ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)_mingSay == (Object)null)) { return false; } OnPick(npc, "MING_ACCEPT", OnAcceptErrand); AddChoice(ming, L10n.T("Donna says you know someone who can fix my RV."), 92, () => Active && Stage == 2, ((Object)(object)val != (Object)null) ? null : ((Action)delegate { WorldSay(_mingT, L10n.T("Oh yes! Marco, at the docks. Very good mechanic. But first you do small favor for me, yes? Crate in a dead drop. You bring it here, I call Marco.")); 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 lose my crate? Not very good. Five hundred, then we are okay, yes?")); OnMingPayLoss(); }), val3); return true; } private static bool InjectMarco(DialogueController marco, NPC npc, bool degraded) { Conversation val = S1Container(npc, "rv_marco_favour", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", L10n.T("Tell ya what. There's a package of mine in a dead drop. Bring it here and we'll talk."), (Action<ChoiceList>)null); }); Conversation val2 = S1Container(npc, "rv_marco_gotpkg", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", L10n.T("Much obliged. Here's the deal: bring me packaged product in your hand, and every piece comes off your bill."), (Action<ChoiceList>)null); }); Conversation val3 = S1Container(npc, "rv_marco_lost", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", L10n.T("You lost it? Well, that's gonna cost ya. Five hundred and we're even."), (Action<ChoiceList>)delegate(ChoiceList c) { c.Add("MARCO_PAY", L10n.T("Pay ${0}", 500), (string)null).Add("MARCO_DEFER_LOSS", L10n.T("I'll come back with the money."), "MARCO_LOSS_DEFER"); }).AddNode("MARCO_LOSS_DEFER", L10n.T("I'll be here."), (Action<ChoiceList>)null); }); Conversation val4 = S1Container(npc, "rv_marco_bring", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", BringInfoLine(), (Action<ChoiceList>)null); }); _marcoSay = S1Container(npc, "rv_marco_say", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", "...", (Action<ChoiceList>)null); }); _marcoQuote = S1Container(npc, "rv_marco_quote", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", "...", (Action<ChoiceList>)delegate(ChoiceList c) { c.Add("MARCO_QUOTE_REPAIR", "...", (string)null).Add("MARCO_QUOTE_LOWER", L10n.T("Can you go any lower?"), "MARCO_QUOTE_FAVOUR").Add("MARCO_QUOTE_LATER", L10n.T("Later."), "MARCO_QUOTE_BYE"); }).AddNode("MARCO_QUOTE_FAVOUR", L10n.T("Tell ya what. There's a package of mine in a dead drop. Bring it here and we'll talk."), (Action<ChoiceList>)null).AddNode("MARCO_QUOTE_BYE", L10n.T("I'll be here."), (Action<ChoiceList>)null); }); _marcoMoreEngine = S1Container(npc, "rv_marco_more_engine", delegate(DialogueContainerBuilder b) { b.AddNode("ENTRY", L10n.T("I can't work on her where she sits. Get the engine in first, then drive her over."), (Action<ChoiceList>)delegate(ChoiceList c) { c.Add("MARCO_MORE_ENGINE", "...", (string)null).Add("MARCO_MORE_LATER", L10n.T("Later."), "MARCO_MORE_BYE"); }).AddNode("MARCO_MORE_BYE", L10n.T("I'll be here."), (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 || (Object)(object)_marcoSay == (Object)null || (Object)(object)_marcoQuote == (Object)null || (Object)(object)_marcoMoreEngine == (Object)null)) { return false; } OnPick(npc, "MARCO_QUOTE_REPAIR", OnMarcoRepair); OnPick(npc, "MARCO_QUOTE_LOWER", OnMarcoFavour); OnPick(npc, "MARCO_MORE_ENGINE", delegate { RvUpgrades.Buy(RvUpgrade.Engine); }); AddChoice(marco, L10n.T("Mrs. Ming sent me. Can you fix my RV?"), -100, () => Active && (Stage == 5 || Stage == 6), delegate { PrepareQuote(); OnMarcoReferral(); }, _marcoQuote); _marcoRepairChoice = AddChoice(marco, RepairChoiceText(), -103, () => Active && ReferralUsed && Stage < 9, OnMarcoRepair); AddChoice(marco, L10n.T("Can you go any lower?"), -104, () => Active && Stage == 7 && !_pickupActive, OnMarcoFavour, val); AddChoice(marco, L10n.T("Can you go any lower?"), -104, () => Active && Trusted_ && Stage < 9 && HeldPackagedSlot() == null && CurrentPrice() > RVRepairVanPreferences.RepairPrice, ((Object)(object)val4 != (Object)null) ? null : ((Action)delegate { WorldSay(_marcoT, BringInfoLine()); }), val4); AddChoice(marco, L10n.T("Here's your package."), -104, () => 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), -104, () => Active && _pickupActive && _hasPackage && !PlayerHasItem("rv_marco_package"), ((Object)(object)val3 != (Object)null) ? null : ((Action)delegate { WorldSay(_marcoT, L10n.T("You lost it? Well, that's gonna cost ya. Five hundred and we're even.")); OnMarcoPayLoss(); }), val3); for (int num = 0; num < 1; num++) { int idx = num; _sampleChoices[idx] = AddChoice(marco, L10n.T("Give Marco a packaged sample"), -105 - idx, () => SampleChoiceVisible(idx), delegate { OnGiveSample(idx); }); } for (int num2 = 0; num2 < UpgradeOrder.Length; num2++) { RvUpgrade up = UpgradeOrder[num2]; _upgradeChoices[num2] = AddChoice(marco, RvUpgrades.ChoiceText(up), -107 - num2, () => RvUpgrades.Available(up), delegate { RvUpgrades.Buy(up); }); } AddChoice(marco, L10n.T("Can you do more to the RV?"), -111, () => RvGarage.AskMoreVisible(), OnMarcoMore); 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 (Exception e) { Core.Swallowed("Questline.FindNpc", e); } 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 Conversation 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<Conversation> 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 (Exception e) { Core.Swallowed("Questline.DumpNpcDiagnostics.ID", e); 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 (Exception e) { Core.Swallowed("Questline.DiagTarget.gameObject", e); } try { flag2 = (Object)(object)ControllerOf(val, out var _) != (Object)null; } catch (Exception e2) { Core.Swallowed("Questline.DiagTarget.controller", e2); } 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 (Exception e) { Core.Swallowed("Questline.ControllerOf", e); 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 (NetworkBus.Online && !NetworkBus.IsServer) { int num = ClientPayment.Charge(500, "Ming's fee"); if (num == 0) { ReplyMing(L10n.T("Okay. You bring money, yes?")); return; } NetworkBus.SendToHost(RvOp.MingPayLoss, 0, num, 500); ReplyMing(L10n.T("Very good. Now go see Marco at the docks, tell him Ming sends you.")); } else { HostMingPayLoss(-1); } } private static bool HostMingPayLoss(int clientPaid) { if (Stage != 4) { return false; } if (clientPaid < 0) { if (Money.GetCashBalance() < 500f) { ReplyMing(L10n.T("Okay. You bring money, yes?")); return false; } Money.ChangeCashBalance(-500f, true, true); } else if (clientPaid != 500) { Core.Log.Warning("[Questline] Ming fee refused - client paid " + clientPaid + ", fee is " + 500 + "."); return false; } Stage = 5; if (clientPaid < 0) { ReplyMing(L10n.T("Very good. Now go see Marco at the docks, tell him Ming sends you.")); } SyncEntry(); return true; } private static void OnMarcoGreet() { OnMarcoReferral(); } private static void OnMarcoReferral() { if (!RouteIntent(RvOp.MarcoReferral) && (Stage == 5 || Stage == 6)) { Stage = 7; RefreshRepairChoice(); if ((Object)(object)_marcoQuote == (Object)null) { WorldSay(_marcoT, QuoteLine(CurrentPrice())); } SyncEntry(); } } private static string QuoteLine(int price) { return L10n.T("Ming sent ya? Well, any friend of hers. That RV's a burnt-out shell, buddy - another week and she'd have gone to the crusher. Normally that's {0}. For you, {1}.", MoneyManager.FormatAmount((float)RVRepairVanPreferences.BasePriceNoReferral, false, false), MoneyManager.FormatAmount((float)price, false, false)); } private static void PrepareQuote() { if ((Object)(object)_marcoQuote == (Object)null) { return; } int num = Mathf.Max(RVRepairVanPreferences.RepairPrice, RVRepairVanPreferences.BasePriceWithReferral - DiscountTotal); DialogueNodeData dialogueNodeByLabel = _marcoQuote.GetDialogueNodeByLabel("ENTRY"); if (dialogueNodeByLabel == null) { Core.Log.Warning("[Questline] Marco's quote has no ENTRY node - it opens with its placeholder."); return; } dialogueNodeByLabel.DialogueText = QuoteLine(num); if (dialogueNodeByLabel.choices != null && ((Il2CppArrayBase<DialogueChoiceData>)(object)dialogueNodeByLabel.choices).Length > 0) { ((Il2CppArrayBase<DialogueChoiceData>)(object)dialogueNodeByLabel.choices)[0].ChoiceText = L10n.T("Repair it ({0})", MoneyManager.FormatAmount((float)num, false, false)); } } private static void OnMarcoMore() { if ((Stage == 0 || Stage == 10) && !RouteIntent(RvOp.AskedBuildOuts)) { HostAskedBuildOuts(); } if (RvUpgrades.Owned(RvUpgrade.Engine) || !OpenMoreEngine()) { ReplyMarco(RvGarage.AskMoreLine()); } } private static bool OpenMoreEngine() { try { if ((Object)(object)_marcoMoreEngine == (Object)null) { return false; } DialogueNodeData dialogueNodeByLabel = _marcoMoreEngine.GetDialogueNodeByLabel("ENTRY"); NPC val = NPC.Get("marco_baron"); if (dialogueNodeByLabel == null || dialogueNodeByLabel.choices == null || ((Il2CppArrayBase<DialogueChoiceData>)(object)dialogueNodeByLabel.choices).Length == 0 || val == null) { Core.Log.Warning("[Questline] Marco's engine offer cannot open - he answers without it."); return false; } ((Il2CppArrayBase<DialogueChoiceData>)(object)dialogueNodeByLabel.choices)[0].ChoiceText = RvUpgrades.ChoiceText(RvUpgrade.Engine); if (val.Dialogue.JumpTo("rv_marco_more_engine", "ENTRY", false)) { return true; } Core.Log.Warning("[Questline] Marco's engine offer was refused by the dialogue - he answers without it."); } catch (Exception ex) { Core.Log.Warning("[Questline] Marco's engine offer failed: " + ex.Message); } return false; } private static string ShortLine(int price) { return L10n.T("That's {0}, buddy. Come back when you've got it.", MoneyManager.FormatAmount((float)price, false, false)); } private static string BringInfoLine() { return L10n.T("Packaged product, buddy. Have it in your hand when you talk to me - every piece knocks up to {0} off the bill.", MoneyManager.FormatAmount((float)RVRepairVanPreferences.MaxSampleDiscount, false, false)); } private static void OnMarcoRepair() { try { bool flag = NetworkBus.Online && !NetworkBus.IsServer; if (!flag && !RVManager.IsDestroyed()) { ReplyMarco(L10n.T("Your RV looks fine to me.")); return; } int num = CurrentPrice(); if (Money.GetCashBalance() < (float)num) { ReplyMarco(ShortLine(num)); } else if (flag) { int num2 = ClientPayment.Charge(num, "repair"); if (num2 == 0) { ReplyMarco(ShortLine(num)); return; } WorldSay(_marcoT, L10n.T("Alright, let me grab my tools.")); NetworkBus.SendToHost(RvOp.PayRepair, 0, num2, num); RepairCinematic.Play(null, delegate { MarcoOpens(RepairStateStore.GetRepaired() ? RepairDoneLine() : BookedLine()); }, delegate { GruntNpc(FindNpc("marco_baron")); }); } else { HostPayRepair(withCinematic: true); } } catch (Exception ex) { Core.Log.Error("[Questline] repair failed: " + ex); } } private static string BookedLine() { return L10n.T("Alright, I'll send the boys over to your RV. Give me a day - I'll text ya when she's done."); } private static string RepairDoneLine() { if (!RVRepairVanPreferences.QuestlineEnabled) { return L10n.T("There she is, good as new. Try not to blow her up again, buddy."); } return L10n.T("There she is, good as new. Go take a look, and try not to blow her up again, buddy."); } private static void EnterPaid() { _pickupActive = false; _hasPackage = false; _drop = null; Stage = 9; SyncEntry(); } 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 (NetworkBus.Online && !NetworkBus.IsServer) { int num = ClientPayment.Charge(500, "Marco's fee"); if (num == 0) { ReplyMarco(L10n.T("I'll be here.")); return; } NetworkBus.SendToHost(RvOp.MarcoPayLoss, 0, num, 500); ReplyMarco(L10n.T("Pleasure doing business. Now, bring me packaged product in your hand, and every piece comes off your bill.")); } else { HostMarcoPayLoss(-1); } } private static bool HostMarcoPayLoss(int clientPaid) { if (!_pickupActive || !_hasPackage) { return false; } if (clientPaid < 0) { if (Money.GetCashBalance() < 500f) { ReplyMarco(L10n.T("I'll be here.")); return false; } Money.ChangeCashBalance(-500f, true, true); } else if (clientPaid != 500) { Core.Log.Warning("[Questline] Marco fee refused - client paid " + clientPaid + ", fee is " + 500 + "."); return false; } _pickupActive = false; _hasPackage = false; _drop = null; Stage = 8; if (clientPaid < 0) { ReplyMarco(L10n.T("Pleasure doing business. Now, bring me packaged product in your hand, and every piece comes off your bill.")); } SyncEntry(); return true; } private static void OnGiveSample(int i) { try { RefreshSampleSlots(); ItemSlot val = ((i >= 0 && i < _sampleSlots.Count) ? _sampleSlots[i] : HeldPackagedSlot()); 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) { ReplyMarco(L10n.T("That ain't packaged, buddy. Bring it sealed.")); return; } int num = SampleUnitDiscount(val2); string text = ThanksLine(val2, num); 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_Server(val2, false); } RemoveOneFromSlot(val); if (val != null) { _ = val.Quantity; } if (RouteIntent(RvOp.GiveSample, num)) { ReplyMarco(text); } else { HostGiveSample(num, text); } } 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(); ItemSlot val = HeldPackagedSlot(); if (val != null) { _sampleSlots.Add(val); } } 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 (Exception e) { Core.Swallowed("Questline.SampleChoiceText", e); } return L10n.T("Give Marco: {0} (-{1})", text, MoneyManager.FormatAmount((float)SampleUnitDiscount(p), false, false)); } private static void HostGiveSample(int discount, string thanks = null) { Samples++; DiscountTotal += discount; RefreshRepairChoice(); if (thanks != null) { ReplyMarco(thanks); } SyncEntry(); } private static string ThanksLine(ProductItemInstance p, int discount) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 string text = MoneyManager.FormatAmount((float)discount, false, false); try { ProductDefinition val = (((Object)(object)((ItemInstance)p).Definition != (Object)null) ? ((Il2CppObjectBase)((ItemInstance)p).Definition).TryCast<ProductDefinition>() : null); if ((Object)(object)val != (Object)null && (int)val.DrugType == 1 && (int)((QualityItemInstance)p).Quality >= 3) { return L10n.T("Ninety-nine percent pure? Where'd you learn chemistry, buddy? That's {0} off.", text); } } catch (Exception e) { Core.Swallowed("Questline.ThanksLine", e); } return L10n.T("Thanks, buddy. That's {0} off.", text); } 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("Mrs. Ming's Crate"), L10n.T("A sealed crate for Mrs. Ming, stamped 'Golden Moth Chemical'. Don't open it."), new string[3] { "grainbag", "trashbag", "flashlight" }); RegisterItem("rv_marco_package", L10n.T("Marco's Package"), L10n.T("Marco's package. Smells like fried chicken. Bring it to him at the docks."), 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 ex) { Instance log = Core.Log; if (log != null) { log.Warning("[Questline] CloneFrom('" + text + "') failed: " + ex.Message); } 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 (Exception e) { Core.Swallowed("Questline.RegisterItem", e); } } [Conditional("DEBUG")] private static void DumpItemsOnce() { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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 (Exception e) { Core.Swallowed("Questline.DumpItemsOnce", e); } } } } catch (Exception ex) { Core.Log.Warning("[ITEMDUMP] failed: " + ex.Message); } } private static DeadDropInstance AdoptOrPlace(string id, Vector3 origin, out bool placed) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if (PlayerHasItem(id)) { placed = true; return null; } DeadDropInstance val = null; try { DeadDropInstance[] all = DeadDropManager.All; int num = 0; while (all != null && num < all.Length) { DeadDropInstance val2 = all[num]; if (val2 != null && !val2.IsEmpty) { int num2 = val2.Storage.RemoveAllOfDefinition(id); if (num2 > 0) { Core.Log.Msg("[Questline] '" + id + "' found in drop '" + val2.Name + "' (" + num2 + ")" + ((val == null) ? "." : " - extra copy removed.")); if (val == null) { val = val2; } } } num++; } } catch (Exception ex) { Core.Log.Warning("[Questline] looking for '" + id + "' in the drops failed: " + ex.Message); } DeadDropInstance val3 = val ?? ReserveDeadDrop(origin); placed = PlaceItem(val3, id); return val3; } 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 (Exception e) { Core.Swallowed("Questline.PlayerHasItem", e); } 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; int loadedTicks = 0; while (myGen == _gen) { yield return (object)new WaitForSeconds(1f); int num; if (!RepairSave.Loaded) { 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."); } } num = loadedTicks + 1; loadedTicks = num; if (num >= 5) { try { BuildOutQuest.Tick(Stage == 0 || Stage >= 10, Stage >= 11); } catch (Exception ex) { Core.Log.Warning("[Quest] build-out tick failed: " + ex.Message); } } if (NetworkBus.Online && !NetworkBus.IsServer) { if (Stage == 9 && RepairStateStore.GetRepaired() && !_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 (!_errandRestored) { RestorePackageErrand(); } 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 (RepairStateStore.GetRepaired() && !RvRepairJob.JobRunning) { if (Stage >= 1 && Stage < 9) { Core.Log.Msg("[Questline] RV is already repaired at stage " + Stage + " - finishing the story."); Stage = 10; } if (Stage >= 10 && RepairQuest.HasOpenEntry()) { Core.Log.Msg("[Questline] story is finished but the journal entry is still open - closing it."); RepairQuest.CompleteIfActive(); } } if (!Active) { continue; } try { Vector3 a = PlayerPos(); if (Stage == 3) { if (_cratePoint == Vector3.zero && (Object)(object)_mingT != (Object)null) { _crateDrop = AdoptOrPlace("rv_ming_crate", _mingT.position, out _cratePlaced); _cratePoint = ((_crateDrop != null) ? _crateDrop.Position : (_mingT.position + new Vector3(8f, 0f, 8f))); SyncEntry(); } if (_cratePlaced ? PlayerHasItem("rv_ming_crate") : (_cratePoint != Vector3.zero && Dist(a, _cratePoint) < 5f)) { Stage = 4; SyncEntry(); } } if (_pickupActive && !_hasPackage) { if (_dropPoint == Vector3.zero && (Object)(object)_marcoT != (Object)null) { _drop = AdoptOrPlace("rv_marco_package", _marcoT.position, out _pkgPlaced); _dropPoint = ((_drop != null) ? _drop.Position : (_marcoT.position + new Vector3(10f, 0f, 10f))); SyncEntry(); } if (_pkgPlaced ? PlayerHasItem("rv_marco_package") : (_dropPoint != Vector3.zero && Dist(a, _dropPoint) < 5f)) { _hasPackage = true; SyncEntry(); } } } catch (Exception e) { Core.Swallowed("Questline.ProximityCoroutine", e); } } } private static void EnsureQuest() { RepairQuest.StartIfNeeded(); if (RepairQuest.HasEntry()) { SyncEntry(); } else { MelonCoroutines.Start(SyncWhenBuilt()); } } private static IEnumerator SyncWhenBuilt() { int myGen = _gen; for (int i = 0; i < 60; i++) { if (RepairQuest.HasEntry()) { break; } yield return null; } if (myGen == _gen) { SyncEntry(); } } internal static void RefreshEntries() { SyncEntry(); } private static void RestorePackageErrand() { _errandRestored = true; int packageErrand = RepairStateStore.GetPackageErrand(); if (packageErrand != 0) { if (Stage != 7) { Core.Log.Msg("[Questline] saved package run dropped - stage " + Stage + "."); RepairStateStore.SetPackageErrand(0); return; } _pickupActive = true; _hasPackage = packageErrand == 2; Core.Log.Msg("[Questline] package run restored (has package: " + _hasPackage + ")."); SyncEntry(); } } private static int CurrentStep(int stage) { switch (stage) { case 0: case 1: return 0; case 2: return 1; case 3: return 2; case 4: return 3; case 5: case 6: return 4; case 7: case 8: return 5; case 9: if (!RepairStateStore.GetRepaired()) { return 8; } return 9; default: return 10; } } private static QuestState StoryStep(int step, int current) { if (step >= current) { if (step == current) { return (QuestState)1; } return (QuestState)0; } return (QuestState)2; } private static QuestState PackageStep(QuestState now, int stage, bool active, bool completed) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if (stage >= 9) { if ((int)now == 2) { return (QuestState)2; } return (QuestState)0; } if (!active) { if (completed) { return (QuestState)2; } return (QuestState)0; } return (QuestState)1; } private static void SyncEntry() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_008f: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0164: 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_0174: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) if (_errandRestored) { RepairStateStore.SetPackageErrand(_pickupActive ? ((!_hasPackage) ? 1 : 2) : 0); } int stage = Stage; if (stage < 10 && QuestSteps.TryOpen(RepairQuest.Title, 10, out var steps)) { int num = CurrentStep(stage); Vector3 val = MarcoPos(); steps.Set(0, StoryStep(0, num), null, "donna_martin", DonnaPos()); steps.Set(1, StoryStep(1, num), null, "ming", MingPos()); steps.Set(2, StoryStep(2, num), null, null, (_cratePoint != Vector3.zero) ? _cratePoint : MingPos()); steps.Set(3, StoryStep(3, num), null, "ming", MingPos()); steps.Set(4, StoryStep(4, num), null, "marco_baron", val); steps.Set(5, StoryStep(5, num), L10n.T("Pay Marco for the repair ({0})", MoneyManager.FormatAmount((float)CurrentPrice(), false, false)), "marco_baron", val); steps.Set(6, PackageStep(steps.State(6), stage, _pickupActive && !_hasPackage, (_pickupActive && _hasPackage) || stage == 8), null, null, (_dropPoint != Vector3.zero) ? _dropPoint : val); steps.Set(7, PackageStep(steps.State(7), stage, _pickupActive && _hasPackage, stage == 8), null, "marco_baron", val); QuestState want = (QuestState)((num == 8) ? 1 : ((num > 8 && (int)steps.State(8) != 0) ? 2 : 0)); steps.Set(8, want, null, null, RvPos()); steps.Set(9, StoryStep(9, num), null, null, RvPos()); } if (NetworkBus.Online && NetworkBus.IsServer) { HostBroadcastTransient(); } } private static DialogueChoice AddChoice(DialogueController dc, string text, int prio, Func<bool> show, Action onChosen, Conversation 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 (Exception e) { Core.Swallowed("Questline.AddChoice.shouldShowCheck", e); 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 ItemSlot HeldPackagedSlot() { try { PlayerInventory instance = PlayerSingleton<PlayerInventory>.Instance; if ((Object)(object)instance == (Object)null) { return null; } int num = ((instance.EquippedSlotIndex >= 0) ? instance.EquippedSlotIndex : instance.PriorEquippedSlotIndex); if (num < 0) { return null; } ItemSlot val = (ItemSlot)(object)instance.IndexAllSlots(num); object obj; if (val == null) { obj = null; } else { ItemInstance itemInstance = val.ItemInstance; obj = ((itemInstance != null) ? ((Il2CppObjectBase)itemInstance).TryCast<ProductItemInstance>() : null); } ProductItemInstance val2 = (ProductItemInstance)obj; return (val2 != null && (Object)(object)val2.AppliedPackaging != (Object)null) ? val : null; } catch (Exception e) { Core.Swallowed("Questline.HeldPackagedSlot", e); return null; } } internal static void SayMarco(string line) { WorldSay(MarcoTransform(), line); } private static Transform MarcoTransform() { if ((Object)(object)_marcoT != (Object)null) {