Decompiled source of TimbnCore v1.3.0
Timbn.GraveyardKeeperTwo.Core/Timbn.GraveyardKeeperTwo.Core.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.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using LazyBearTechnology; using Microsoft.CodeAnalysis; using TMPro; using Timbn.GraveyardKeeperTwo.Core.Framework; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("gavinsteinhoff")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Shared framework for Graveyard Keeper 2 (BepInEx 5) mods. A plugin base class with auto patching and config, plus the Core mod that hosts it.")] [assembly: AssemblyFileVersion("1.3.0.0")] [assembly: AssemblyInformationalVersion("1.3.0+42577719540a953590d1b1b778a9af1bc1cbf13e")] [assembly: AssemblyProduct("Timbn Core")] [assembly: AssemblyTitle("Timbn.GraveyardKeeperTwo.Core")] [assembly: AssemblyVersion("1.3.0.0")] [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 Timbn.GraveyardKeeperTwo.Core { [BepInPlugin("Timbn.GraveyardKeeperTwo.Core", "Timbn Core", "1.3.0")] public class TimbnCorePlugin : TimbnFrameworkPlugin<TimbnCorePlugin> { public const string Guid = "Timbn.GraveyardKeeperTwo.Core"; public const string Version = "1.3.0"; public const string TestedGameVersion = "1.005"; public const string TestedGameBuild = "70946fdd5c76"; private bool _loadErrorsChecked; public static string GameBuild { get; } = typeof(MainGame).Assembly.ManifestModule.ModuleVersionId.ToString("N").Substring(0, 12); public static TimbnCorePlugin? Instance { get; private set; } protected override void OnAwake() { Instance = this; base.Subscriptions.Add(TimbnGameEvents.GameStarted(TimbnGame.OnGameStarted)); base.Subscriptions.Add(TimbnGameEvents.GoToMainMenu(TimbnGame.OnLeftGame)); base.Subscriptions.Add(TimbnGameEvents.GameStarted(TimbnDialog.OnGameStarted)); base.Subscriptions.Add(TimbnGameEvents.GameStarted(OnGameStarted)); base.Subscriptions.Add(TimbnGameEvents.GoToMainMenu(TimbnDialog.OnLeftGame)); string text = "Core 1.3.0 started on Graveyard Keeper 2 " + Application.version + " (build " + GameBuild + ")"; if (Application.version != "1.005") { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogWarning((object)(text + ", but these mods were tested on 1.005. Include this line when reporting a problem.")); base.MainMenu.Popup("Untested game version", "Timbn mods were tested on version 1.005 and this is " + Application.version + ". Some mods may misbehave until they are updated."); } else if (GameBuild != "70946fdd5c76") { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogWarning((object)(text + ", but these mods were tested on build 70946fdd5c76 of that version, so this is likely a hotfix. Include this line when reporting a problem.")); base.MainMenu.Popup("Untested game build", "Timbn mods were tested on an earlier build of " + Application.version + ", so this is likely a hotfix. Some mods may misbehave until they are updated."); } else { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogInfo((object)(text + ".")); } } private void OnGameStarted() { TimbnQuests.OnGameStarted(); base.SessionSubscriptions.Add(TimbnGameEvents.QuestStarted(TimbnQuests.OnQuestStarted)); TimbnPotions.OnGameStarted(base.SessionSubscriptions); } protected override void OnUpdate() { if (!_loadErrorsChecked) { _loadErrorsChecked = true; string text = TimbnLoadErrors.Describe(); if (text != null) { base.MainMenu.Popup("Some mods did not load", text); } } TimbnDialog.OnUpdate(); TimbnPotions.OnUpdate(); TimbnMainMenu.OnUpdate(); } protected override void OnDestroyed() { Instance = null; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "Timbn.GraveyardKeeperTwo.Core"; public const string PLUGIN_NAME = "Timbn Core"; public const string PLUGIN_VERSION = "1.3.0"; } } namespace Timbn.GraveyardKeeperTwo.Core.Patches { [HarmonyPatch(typeof(EasySpritesCollection))] internal static class EasySpritesCollectionPatch { [HarmonyPrefix] [HarmonyPatch("GetSprite")] private static bool GetSprite(string spriteName, ref Sprite __result) { if (!TimbnSprites.TryGet(spriteName, out Sprite sprite)) { return true; } __result = sprite; return false; } [HarmonyPrefix] [HarmonyPatch("HasSprite")] private static bool HasSprite(string spriteName, ref bool __result) { if (!TimbnSprites.TryGet(spriteName, out Sprite _)) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(GameBalance), "LoadGameBalance")] internal static class GameBalancePatch { private static void Postfix() { GameBalance loaded = TimbnBalance.Loaded; if (loaded != null) { TimbnBalance.OnBalanceLoaded(loaded); } TimbnPotions.OnBalanceLoaded(); } } [HarmonyPatch(typeof(LLBase), "InitHashDictionary")] internal static class LLBasePatch { private static void Postfix(LLBase __instance) { TimbnLocale.ApplyTo(__instance); } } [HarmonyPatch(typeof(QuestSystemData), "PrepareForGame")] internal static class QuestSystemDataPatch { private static void Prefix(QuestSystemData __instance) { TimbnQuests.StubOrphans(__instance); } private static void Postfix(QuestSystemData __instance) { TimbnQuests.OnQuestsPrepared(__instance); } } [HarmonyPatch(typeof(SaveSystem), "Save")] internal static class SaveSystemPatch { private static void Prefix(GameSave gameSave, out IDisposable[] __state) { try { __state = new IDisposable[2] { TimbnQuests.HideForSave(gameSave), TimbnDialog.RemoveForSave() }; } catch (Exception arg) { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|Could not strip mod state from the save: {1}", "SaveSystemPatch", arg)); __state = Array.Empty<IDisposable>(); } } private static void Finalizer(IDisposable[]? __state) { if (__state == null) { return; } foreach (IDisposable disposable in __state) { try { disposable.Dispose(); } catch (Exception arg) { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|Could not restore mod state after the save: {1}", "SaveSystemPatch", arg)); } } } } [HarmonyPatch(typeof(UIMainMenuInfoPanel), "SetMenuOnlyLabelsVisible")] internal static class UIMainMenuInfoPanelPatch { private static void Postfix() { TimbnMainMenu.DrawLines(); } } [HarmonyPatch(typeof(UIMainMenuWindow), "Open")] internal static class UIMainMenuWindowPatch { private static void Postfix(UIMainMenuWindow __instance) { TimbnMainMenu.OnMenuOpened(__instance); } } [HarmonyPatch(typeof(WgoData), "FireInteractionEvent")] internal static class WgoDataPatch { private static bool Prefix(WgoData __instance, ref bool __result) { if (!TimbnDialog.TryHandleInteraction(__instance)) { return true; } __result = true; return false; } } } namespace Timbn.GraveyardKeeperTwo.Core.Framework { public static class TimbnBalance { private sealed class Entry { public BalanceBaseObject Definition { get; } public Action<GameBalance>? Prepare { get; } public Entry(BalanceBaseObject definition, Action<GameBalance>? prepare) { Definition = definition; Prepare = prepare; } } private static readonly FieldRef<GameBalance?> _instance = AccessTools.StaticFieldRefAccess<GameBalance>(AccessTools.Field(typeof(GameBalance), "instance")); private static readonly FieldRef<GameBalanceBase, List<IList>> _datas = AccessTools.FieldRefAccess<GameBalanceBase, List<IList>>("datas"); private static readonly FieldRef<GameBalanceBase, List<Type>> _types = AccessTools.FieldRefAccess<GameBalanceBase, List<Type>>("types"); private static readonly FieldRef<GameBalanceBase, List<Dictionary<string, int>>> _cache = AccessTools.FieldRefAccess<GameBalanceBase, List<Dictionary<string, int>>>("cache"); private static readonly Action<GameBalance> _createQuestsCache = AccessTools.MethodDelegate<Action<GameBalance>>(AccessTools.Method(typeof(GameBalance), "CreateQuestsCache", (Type[])null, (Type[])null), (object)null, true); private static readonly List<Entry> _entries = new List<Entry>(); public static GameBalance? Loaded => _instance.Invoke(); internal static IDisposable Add(BalanceBaseObject definition) { return Add(definition, null); } internal static IDisposable Add(BalanceBaseObject definition, Action<GameBalance>? prepare) { Entry entry = new Entry(definition, prepare); _entries.Add(entry); GameBalance loaded = Loaded; if (loaded != null) { Insert(loaded, entry); RefreshDerivedCaches(loaded, ((object)definition).GetType()); } return new TimbnUndo(delegate { Remove(entry); }); } internal static void OnBalanceLoaded(GameBalance balance) { HashSet<Type> hashSet = new HashSet<Type>(); foreach (Entry entry in _entries) { if (Insert(balance, entry)) { hashSet.Add(((object)entry.Definition).GetType()); } } foreach (Type item in hashSet) { RefreshDerivedCaches(balance, item); } } internal static void RefreshQuestCaches(GameBalance balance) { _createQuestsCache(balance); } private static bool Insert(GameBalance balance, Entry entry) { BalanceBaseObject definition = entry.Definition; int num = _types.Invoke((GameBalanceBase)(object)balance).IndexOf(((object)definition).GetType()); if (num == -1) { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)("TimbnBalance|The balance has no table for " + ((object)definition).GetType().Name + ".")); return false; } IList list = _datas.Invoke((GameBalanceBase)(object)balance)[num]; if (list.Contains(definition)) { return false; } entry.Prepare?.Invoke(balance); int existing = FindById(list, definition.id); if (existing == -1) { list.Add(definition); _cache.Invoke((GameBalanceBase)(object)balance)[num][definition.id] = list.Count - 1; return true; } if (!_entries.Any((Entry e) => e != entry && e.Definition == list[existing])) { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)("TimbnBalance|The game already has a " + ((object)definition).GetType().Name + " with id '" + definition.id + "'.")); return false; } list[existing] = definition; return true; } private static void Remove(Entry entry) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) _entries.Remove(entry); GameBalance loaded = Loaded; if (loaded == null) { return; } BalanceBaseObject definition = entry.Definition; int num = _types.Invoke((GameBalanceBase)(object)loaded).IndexOf(((object)definition).GetType()); if (num == -1) { return; } IList list = _datas.Invoke((GameBalanceBase)(object)loaded)[num]; int num2 = list.IndexOf(definition); if (num2 != -1) { list.RemoveAt(num2); Dictionary<string, int> dictionary = _cache.Invoke((GameBalanceBase)(object)loaded)[num]; dictionary.Clear(); for (int i = 0; i < list.Count; i++) { dictionary[((BalanceBaseObject)list[i]).id] = i; } RefreshDerivedCaches(loaded, ((object)definition).GetType()); } } private static int FindById(IList list, string id) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < list.Count; i++) { if (((BalanceBaseObject)list[i]).id == id) { return i; } } return -1; } private static void RefreshDerivedCaches(GameBalance balance, Type type) { if (type == typeof(QuestDef)) { _createQuestsCache(balance); } } } public sealed class TimbnPluginBalance { private readonly TimbnFrameworkPlugin _owner; internal TimbnPluginBalance(TimbnFrameworkPlugin owner) { _owner = owner; } public IDisposable Add(BalanceBaseObject definition) { return _owner.Subscriptions.Add(TimbnBalance.Add(definition)); } public IDisposable Add(BalanceBaseObject definition, Action<GameBalance>? prepare) { return _owner.Subscriptions.Add(TimbnBalance.Add(definition, prepare)); } } public sealed class TimbnConversation { private enum StepKind { Say, PlayerSay, Do, Ask } private sealed class Step(StepKind kind) { public StepKind Kind { get; } = kind; public string Key { get; set; } = ""; public Action? Action { get; set; } public string[] Answers { get; set; } = Array.Empty<string>(); public Dictionary<string, TimbnConversation> Branches { get; } = new Dictionary<string, TimbnConversation>(); } private readonly List<Step> _steps = new List<Step>(); private readonly string _talkId; public WgoData Npc { get; } internal TimbnConversation(WgoData npc, string talkId) { Npc = npc; _talkId = talkId; } public TimbnConversation Say(string key) { _steps.Add(new Step(StepKind.Say) { Key = key }); return this; } public TimbnConversation PlayerSay(string key) { _steps.Add(new Step(StepKind.PlayerSay) { Key = key }); return this; } public TimbnConversation Do(Action action) { _steps.Add(new Step(StepKind.Do) { Action = action }); return this; } public TimbnConversation Ask(params string[] answers) { if (answers.Length == 0) { throw new ArgumentException("Ask needs at least one answer.", "answers"); } _steps.Add(new Step(StepKind.Ask) { Answers = answers }); return this; } public TimbnConversation If(string answer, Action<TimbnConversation> then) { if (_steps.Count != 0) { List<Step> steps = _steps; if (steps[steps.Count - 1].Kind == StepKind.Ask) { TimbnConversation timbnConversation = new TimbnConversation(Npc, _talkId); then(timbnConversation); List<Step> steps2 = _steps; steps2[steps2.Count - 1].Branches[answer] = timbnConversation; return this; } } throw new InvalidOperationException("If has to follow Ask."); } internal void Run(Action done) { RunFrom(0, done, done); } private void RunFrom(int index, Action next, Action end) { if (index >= _steps.Count) { next(); return; } Step step = _steps[index]; switch (step.Kind) { case StepKind.Say: if (!TimbnDialog.Say(Npc, step.Key, carryOn)) { end(); } break; case StepKind.PlayerSay: if (!TimbnDialog.PlayerSay(step.Key, carryOn)) { end(); } break; case StepKind.Do: try { step.Action(); } catch (Exception arg) { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|A step in talk '{1}' threw: {2}", "TimbnConversation", _talkId, arg)); end(); break; } carryOn(); break; case StepKind.Ask: if (!TimbnDialog.Ask(Npc, step.Answers, delegate(string chosen) { if (step.Branches.TryGetValue(chosen, out TimbnConversation value)) { value.RunFrom(0, carryOn, end); } else { carryOn(); } }, end)) { end(); } break; } void carryOn() { RunFrom(index + 1, next, end); } } } internal static class TimbnDialog { private sealed class Talk { public string NpcId { get; } public string EventId { get; } public Func<bool> IsOffered { get; } public Action<TimbnConversation> OnTalk { get; } public bool Busy { get; set; } public Talk(string npcId, string eventId, Func<bool> isOffered, Action<TimbnConversation> onTalk) { NpcId = npcId; EventId = eventId; IsOffered = isOffered; OnTalk = onTalk; } } private const string LeaveKey = "common_leave"; private static readonly List<Talk> _talks = new List<Talk>(); private static float _nextRefresh; private static bool _holdsControl; internal static IDisposable AddTalk(string npcId, string eventId, Func<bool> isOffered, Action<TimbnConversation> onTalk) { Talk talk = new Talk(npcId, eventId, isOffered, onTalk); _talks.RemoveAll((Talk t) => t.NpcId == npcId && t.EventId == eventId); _talks.Add(talk); Refresh(); return new TimbnUndo(delegate { if (_talks.Remove(talk) && !_talks.Any((Talk t) => t.NpcId == npcId && t.EventId == eventId) && TimbnGame.IsInGame) { WgoData wgoData = MainGame.WorldData.GetWgoData(npcId); if (wgoData != null) { wgoData.RemoveInteractionEvent(eventId); } } }); } internal static bool Say(WgoData npc, string key, Action then) { return Show(key, delegate { //IL_001c: Unknown result type (might be due to invalid IL or missing references) Bubble.Talk(new PhraseData(false, npc, key, then, (SpeechBubblePreset)null, (SpeechBubbleType)1, (ForceCornerPosition)0, 0f, false)); }); } internal static bool PlayerSay(string key, Action then) { return Show(key, delegate { //IL_0017: Unknown result type (might be due to invalid IL or missing references) Bubble.Talk(new PhraseData(true, (WgoData)null, key, then, (SpeechBubblePreset)null, (SpeechBubbleType)1, (ForceCornerPosition)0, 0f, false)); }); } internal static AnswerVisualData Answer(string key) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_0069: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_0055: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown AnswerData val = null; if (GameBalance.Me.questDefByReqPhrase.TryGetValue(key, out var value)) { val = value.finishCheck.GetAnswerDataByReqs(); int num = TimbnQuests.MoneyRewardOf(value); if (num > 0) { object obj = val; if (obj == null) { AnswerData val2 = new AnswerData(); val = val2; obj = (object)val2; } ((AnswerData)obj).AddRewardRes(new SmartRes { gameRes = new GameRes("money", (float)num) }); } } return new AnswerVisualData { id = key, answerData = val }; } internal static bool Ask(WgoData npc, string[] answers, Action<string> chosen, Action dismissed) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown List<AnswerVisualData> visuals = answers.Select(Answer).ToList(); bool addedLeave = visuals.All((AnswerVisualData v) => v.answerData != null); if (addedLeave) { visuals.Add(new AnswerVisualData { id = "common_leave" }); } bool picked = false; return Show(string.Join(", ", answers), delegate { Bubble.ShowMultiAnswer(visuals, MainGame.PlayerController.BubblePoint, npc, (Action<string>)delegate(string id) { picked = true; if (addedLeave && id == "common_leave") { dismissed(); } else { GlobalEventsSystem.FireTrigger((Type)15, id); chosen(id); } }, (Action)delegate { if (!picked) { dismissed(); } }, false); }); } private static bool Show(string what, Action show) { try { show(); return true; } catch (Exception arg) { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|Showing '{1}' threw: {2}", "TimbnDialog", what, arg)); return false; } } internal static void OnGameStarted() { _holdsControl = false; Refresh(); } internal static void OnLeftGame() { _holdsControl = false; foreach (Talk talk in _talks) { talk.Busy = false; } } internal static void OnUpdate() { if (TimbnGame.IsInGame && !(Time.unscaledTime < _nextRefresh)) { _nextRefresh = Time.unscaledTime + 1f; Refresh(); } } internal static bool TryHandleInteraction(WgoData npc) { InteractionEvent next = npc.PeekFirstAddedEvent(); if (next == null) { return false; } Talk talk = _talks.Find((Talk t) => t.NpcId == ((ObjectLinkedToDefinition<WGODef>)(object)npc).id && t.EventId == next.str); if (talk == null) { return false; } npc.RemoveInteractionEvent(talk.EventId); talk.Busy = true; TakeControl(); TimbnConversation timbnConversation = new TimbnConversation(npc, talk.EventId); try { talk.OnTalk(timbnConversation); } catch (Exception ex) { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|Talk '{1}' on {2} threw: {3}", "TimbnDialog", talk.EventId, talk.NpcId, ex)); done(); return true; } timbnConversation.Run(done); return true; void done() { if (talk.Busy) { talk.Busy = false; ReleaseControl(); Refresh(); } } } internal static IDisposable RemoveForSave() { if (!TimbnGame.IsInGame) { return new TimbnUndo(delegate { }); } foreach (Talk talk in _talks) { WgoData wgoData = MainGame.WorldData.GetWgoData(talk.NpcId); if (wgoData != null) { wgoData.RemoveInteractionEvent(talk.EventId); } } return new TimbnUndo(Refresh); } private static void TakeControl() { if (!_holdsControl && TimbnGame.IsInGame && !((Object)(object)MainGame.PlayerController == (Object)null)) { _holdsControl = true; MainGame.PlayerController.SetControlTakenType((TakenControlType)3, false); } } private static void ReleaseControl() { if (_holdsControl) { _holdsControl = false; if (TimbnGame.IsInGame && (Object)(object)MainGame.PlayerController != (Object)null) { MainGame.PlayerController.SetControlTakenType((TakenControlType)3, true); } } } private static void Refresh() { if (!TimbnGame.IsInGame) { return; } foreach (Talk talk in _talks) { WgoData wgoData = MainGame.WorldData.GetWgoData(talk.NpcId); if (wgoData != null) { bool flag = wgoData.Events.Any((InteractionEvent e) => e.str == talk.EventId); bool flag2 = !talk.Busy && IsOffered(talk); if (flag2 && !flag) { wgoData.AddInteractionEvent(talk.EventId, false); } else if (!flag2 && flag) { wgoData.RemoveInteractionEvent(talk.EventId); } } } } private static bool IsOffered(Talk talk) { try { return talk.IsOffered(); } catch (Exception arg) { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|isOffered for '{1}' threw: {2}", "TimbnDialog", talk.EventId, arg)); return false; } } } public sealed class TimbnPluginDialog { private readonly TimbnFrameworkPlugin _owner; internal TimbnPluginDialog(TimbnFrameworkPlugin owner) { _owner = owner; } public IDisposable AddTalk(string npcId, string eventId, Func<bool> isOffered, Action<TimbnConversation> talk) { return _owner.Subscriptions.Add(TimbnDialog.AddTalk(npcId, eventId, isOffered, talk)); } } internal static class TimbnGameEvents { internal static IDisposable GameStarted(Action handler) { return On(handler, delegate(Action h) { MainGame.OnGameStarted = (Action)Delegate.Combine(MainGame.OnGameStarted, h); }, delegate(Action h) { MainGame.OnGameStarted = (Action)Delegate.Remove(MainGame.OnGameStarted, h); }); } internal static IDisposable GoToMainMenu(Action handler) { return On(handler, delegate(Action h) { MainGame.OnGoToMainMenu = (Action)Delegate.Combine(MainGame.OnGoToMainMenu, h); }, delegate(Action h) { MainGame.OnGoToMainMenu = (Action)Delegate.Remove(MainGame.OnGoToMainMenu, h); }); } internal static IDisposable GamePaused(Action handler) { return On(handler, delegate(Action h) { MainGame.OnGamePaused = (Action)Delegate.Combine(MainGame.OnGamePaused, h); }, delegate(Action h) { MainGame.OnGamePaused = (Action)Delegate.Remove(MainGame.OnGamePaused, h); }); } internal static IDisposable GameUnpaused(Action handler) { return On(handler, delegate(Action h) { MainGame.OnGameUnpaused = (Action)Delegate.Combine(MainGame.OnGameUnpaused, h); }, delegate(Action h) { MainGame.OnGameUnpaused = (Action)Delegate.Remove(MainGame.OnGameUnpaused, h); }); } internal static IDisposable NewDayStarted(Action<int> handler) { return On(handler, delegate(Action<int> h) { EnvironmentEngine.OnNewDayStarted += h; }, delegate(Action<int> h) { EnvironmentEngine.OnNewDayStarted -= h; }); } internal static IDisposable NewWeekdayStarted(Action<int> handler) { return On(handler, delegate(Action<int> h) { EnvironmentEngine.OnNewDayStartedWithDayNumber += h; }, delegate(Action<int> h) { EnvironmentEngine.OnNewDayStartedWithDayNumber -= h; }); } internal static IDisposable TimeOfDayChanged(Action<float, bool> handler) { return On(handler, delegate(Action<float, bool> h) { EnvironmentEngine.OnTimeOfDayChangedEvent += h; }, delegate(Action<float, bool> h) { EnvironmentEngine.OnTimeOfDayChangedEvent -= h; }); } internal static IDisposable SaveWriteStarted(Action handler) { return On(handler, delegate(Action h) { SaveSystem.OnSaveWriteStarted += h; }, delegate(Action h) { SaveSystem.OnSaveWriteStarted -= h; }); } internal static IDisposable SaveWriteEnded(Action handler) { return On(handler, delegate(Action h) { SaveSystem.OnSaveWriteEnded += h; }, delegate(Action h) { SaveSystem.OnSaveWriteEnded -= h; }); } internal static IDisposable SaveLoadingStarted(Action handler) { return On(handler, delegate(Action h) { SaveSystem.OnSaveLoadingStarted += h; }, delegate(Action h) { SaveSystem.OnSaveLoadingStarted -= h; }); } internal static IDisposable SaveLoadingEnded(Action handler) { return On(handler, delegate(Action h) { SaveSystem.OnSaveLoadingEnded += h; }, delegate(Action h) { SaveSystem.OnSaveLoadingEnded -= h; }); } internal static IDisposable PlayerTeleported(Action handler) { return On(handler, delegate(Action h) { PlayerController.OnPlayerTeleported += h; }, delegate(Action h) { PlayerController.OnPlayerTeleported -= h; }); } internal static IDisposable WorldObjectSpawned(Action<Wgo> handler) { return On(handler, delegate(Action<Wgo> h) { Wgo.OnWgoSpawn += h; }, delegate(Action<Wgo> h) { Wgo.OnWgoSpawn -= h; }); } internal static IDisposable WorldObjectDestroyed(Action<Wgo> handler) { return On(handler, delegate(Action<Wgo> h) { Wgo.OnWgoDestroy += h; }, delegate(Action<Wgo> h) { Wgo.OnWgoDestroy -= h; }); } internal static IDisposable QuestStarted(Action<QuestData> handler) { QuestSystemData quests = MainGame.Instance.GameSave.questSystemData; return On(handler, delegate(Action<QuestData> h) { quests.OnQuestStarted += h; }, delegate(Action<QuestData> h) { quests.OnQuestStarted -= h; }); } internal static IDisposable QuestCompleted(Action<QuestData> handler) { QuestSystemData quests = MainGame.Instance.GameSave.questSystemData; return On(handler, delegate(Action<QuestData> h) { quests.OnQuestCompleted += h; }, delegate(Action<QuestData> h) { quests.OnQuestCompleted -= h; }); } internal static IDisposable QuestCanceled(Action<QuestData> handler) { QuestSystemData quests = MainGame.Instance.GameSave.questSystemData; return On(handler, delegate(Action<QuestData> h) { quests.OnQuestCanceled += h; }, delegate(Action<QuestData> h) { quests.OnQuestCanceled -= h; }); } internal static IDisposable PlayerResourceChanged(Action handler) { PlayerData player = MainGame.PlayerData; return On(handler, delegate(Action h) { player.OnGameResChanged += h; }, delegate(Action h) { player.OnGameResChanged -= h; }); } internal static IDisposable PlayerDropCollected(Action<List<Item>> handler) { PlayerData player = MainGame.PlayerData; return On(handler, delegate(Action<List<Item>> h) { player.OnDropCollected += h; }, delegate(Action<List<Item>> h) { player.OnDropCollected -= h; }); } internal static IDisposable PlayerItemUsed(Action<Item> handler) { PlayerData player = MainGame.PlayerData; return On(handler, delegate(Action<Item> h) { player.OnItemUsed += h; }, delegate(Action<Item> h) { player.OnItemUsed -= h; }); } internal static IDisposable PlayerItemsAdded(Action<List<Item>> handler) { Inventory inventory = MainGame.PlayerData.inventory; return On(handler, delegate(Action<List<Item>> h) { inventory.OnItemsAdd += h; }, delegate(Action<List<Item>> h) { inventory.OnItemsAdd -= h; }); } internal static IDisposable PlayerItemsRemoved(Action<List<Item>> handler) { Inventory inventory = MainGame.PlayerData.inventory; return On(handler, delegate(Action<List<Item>> h) { inventory.OnItemsRemove += h; }, delegate(Action<List<Item>> h) { inventory.OnItemsRemove -= h; }); } internal static IDisposable On(Action handler, Action<Action> add, Action<Action> remove) { add(safe); return new TimbnUndo(delegate { remove(safe); }); void safe() { try { handler(); } catch (Exception ex) { LogHandlerError(handler, ex); } } } internal static IDisposable On<T>(Action<T> handler, Action<Action<T>> add, Action<Action<T>> remove) { add(safe); return new TimbnUndo(delegate { remove(safe); }); void safe(T arg) { try { handler(arg); } catch (Exception ex) { LogHandlerError(handler, ex); } } } internal static IDisposable On<T1, T2>(Action<T1, T2> handler, Action<Action<T1, T2>> add, Action<Action<T1, T2>> remove) { add(safe); return new TimbnUndo(delegate { remove(safe); }); void safe(T1 first, T2 second) { try { handler(first, second); } catch (Exception ex) { LogHandlerError(handler, ex); } } } private static void LogHandlerError(Delegate handler, Exception ex) { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|{1}.{2} threw: {3}", "TimbnGameEvents", handler.Method.DeclaringType?.FullName, handler.Method.Name, ex)); } } public sealed class TimbnPluginEvents { private readonly TimbnFrameworkPlugin _owner; private readonly List<Func<IDisposable>> _perSave = new List<Func<IDisposable>>(); internal TimbnPluginEvents(TimbnFrameworkPlugin owner) { _owner = owner; } public IDisposable GameStarted(Action handler) { return Own(TimbnGameEvents.GameStarted(handler)); } public IDisposable GoToMainMenu(Action handler) { return Own(TimbnGameEvents.GoToMainMenu(handler)); } public IDisposable GamePaused(Action handler) { return Own(TimbnGameEvents.GamePaused(handler)); } public IDisposable GameUnpaused(Action handler) { return Own(TimbnGameEvents.GameUnpaused(handler)); } public IDisposable NewDayStarted(Action<int> handler) { return Own(TimbnGameEvents.NewDayStarted(handler)); } public IDisposable NewWeekdayStarted(Action<int> handler) { return Own(TimbnGameEvents.NewWeekdayStarted(handler)); } public IDisposable TimeOfDayChanged(Action<float, bool> handler) { return Own(TimbnGameEvents.TimeOfDayChanged(handler)); } public IDisposable SaveWriteStarted(Action handler) { return Own(TimbnGameEvents.SaveWriteStarted(handler)); } public IDisposable SaveWriteEnded(Action handler) { return Own(TimbnGameEvents.SaveWriteEnded(handler)); } public IDisposable SaveLoadingStarted(Action handler) { return Own(TimbnGameEvents.SaveLoadingStarted(handler)); } public IDisposable SaveLoadingEnded(Action handler) { return Own(TimbnGameEvents.SaveLoadingEnded(handler)); } public IDisposable PlayerTeleported(Action handler) { return Own(TimbnGameEvents.PlayerTeleported(handler)); } public IDisposable WorldObjectSpawned(Action<Wgo> handler) { return Own(TimbnGameEvents.WorldObjectSpawned(handler)); } public IDisposable WorldObjectDestroyed(Action<Wgo> handler) { return Own(TimbnGameEvents.WorldObjectDestroyed(handler)); } public IDisposable QuestStarted(Action<QuestData> handler) { return PerSave(() => TimbnGameEvents.QuestStarted(handler)); } public IDisposable QuestCompleted(Action<QuestData> handler) { return PerSave(() => TimbnGameEvents.QuestCompleted(handler)); } public IDisposable QuestCanceled(Action<QuestData> handler) { return PerSave(() => TimbnGameEvents.QuestCanceled(handler)); } public IDisposable PlayerResourceChanged(Action handler) { return PerSave(() => TimbnGameEvents.PlayerResourceChanged(handler)); } public IDisposable PlayerDropCollected(Action<List<Item>> handler) { return PerSave(() => TimbnGameEvents.PlayerDropCollected(handler)); } public IDisposable PlayerItemUsed(Action<Item> handler) { return PerSave(() => TimbnGameEvents.PlayerItemUsed(handler)); } public IDisposable PlayerItemsAdded(Action<List<Item>> handler) { return PerSave(() => TimbnGameEvents.PlayerItemsAdded(handler)); } public IDisposable PlayerItemsRemoved(Action<List<Item>> handler) { return PerSave(() => TimbnGameEvents.PlayerItemsRemoved(handler)); } public IDisposable StaticEvent(Action handler, Action<Action> add, Action<Action> remove) { return Own(TimbnGameEvents.On(handler, add, remove)); } public IDisposable StaticEvent<T>(Action<T> handler, Action<Action<T>> add, Action<Action<T>> remove) { return Own(TimbnGameEvents.On(handler, add, remove)); } public IDisposable StaticEvent<T1, T2>(Action<T1, T2> handler, Action<Action<T1, T2>> add, Action<Action<T1, T2>> remove) { return Own(TimbnGameEvents.On(handler, add, remove)); } internal void AttachToSave() { foreach (Func<IDisposable> item in _perSave.ToList()) { item(); } } private IDisposable Own(IDisposable subscription) { return _owner.Subscriptions.Add(subscription); } private IDisposable PerSave(Func<IDisposable> subscribe) { IDisposable current = null; Func<IDisposable> entry = attach; _perSave.Add(entry); if (TimbnGame.IsInGame) { attach(); } return _owner.Subscriptions.Add(new TimbnUndo(delegate { _perSave.Remove(entry); current?.Dispose(); })); IDisposable attach() { return current = _owner.SessionSubscriptions.Add(subscribe()); } } } public static class TimbnInput { public static bool CanUseHotkeys { get { if (TimbnGame.IsInGame && LazyInput.IsInitialized && LazyInput.IsInputActive()) { return (Object)(object)LazyWindowsStackController.ActiveWindow == (Object)null; } return false; } } public static bool PlayerHasControl { get { if (CanUseHotkeys && (Object)(object)MainGame.PlayerController != (Object)null) { return MainGame.PlayerController.IsControlsEnabled; } return false; } } public static bool IsDownInGame(this KeyboardShortcut shortcut) { if (CanUseHotkeys) { return ((KeyboardShortcut)(ref shortcut)).IsDown(); } return false; } public static bool IsDownWithControl(this KeyboardShortcut shortcut) { if (PlayerHasControl) { return ((KeyboardShortcut)(ref shortcut)).IsDown(); } return false; } } internal static class TimbnMainMenu { private sealed class Line { public string Label { get; } public string Value { get; } public TextMeshProUGUI? Text { get; set; } public Line(string label, string value) { Label = label; Value = value; } } private sealed class Popup { public string Header { get; } public string Text { get; } public Popup(string header, string text) { Header = header; Text = text; } } private static readonly FieldRef<MainGame, UIMainMenuInfoPanel> _infoPanel = AccessTools.FieldRefAccess<MainGame, UIMainMenuInfoPanel>("uiMainMenuInfoPanel"); private static readonly FieldRef<UIMainMenuInfoPanel, TextMeshProUGUI> _developerLabel = AccessTools.FieldRefAccess<UIMainMenuInfoPanel, TextMeshProUGUI>("developerNameLabel"); private static readonly FieldRef<UIMainMenuInfoPanel, TextMeshProUGUI> _publisherLabel = AccessTools.FieldRefAccess<UIMainMenuInfoPanel, TextMeshProUGUI>("publisherNameLabel"); private static readonly FieldRef<UIMainMenuInfoPanel, TextStyle> _valueStyle = AccessTools.FieldRefAccess<UIMainMenuInfoPanel, TextStyle>("rightSideStyle"); private static readonly List<Line> _lines = new List<Line>(); private static readonly List<Popup> _popups = new List<Popup>(); private static UIMainMenuWindow? _menu; internal static IDisposable AddLine(string label, string value) { Line line = new Line(label, value); _lines.Add(line); DrawLines(); return new TimbnUndo(delegate { _lines.Remove(line); if ((Object)(object)line.Text != (Object)null) { Object.Destroy((Object)(object)((Component)line.Text).gameObject); } DrawLines(); }); } internal static IDisposable AddPopup(string header, string text) { Popup popup = new Popup(header, text); _popups.Add(popup); return new TimbnUndo(delegate { _popups.Remove(popup); }); } internal static void OnMenuOpened(UIMainMenuWindow menu) { _menu = menu; } internal static void DrawLines() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)MainGame.Instance == (Object)null) { return; } UIMainMenuInfoPanel val = _infoPanel.Invoke(MainGame.Instance); if (val == null || (Object)(object)val == (Object)null) { return; } TextMeshProUGUI val2 = _developerLabel.Invoke(val); TextMeshProUGUI val3 = _publisherLabel.Invoke(val); Vector2 val4 = ((TMP_Text)val2).rectTransform.anchoredPosition - ((TMP_Text)val3).rectTransform.anchoredPosition; for (int i = 0; i < _lines.Count; i++) { Line line = _lines[i]; if ((Object)(object)line.Text == (Object)null) { line.Text = Object.Instantiate<TextMeshProUGUI>(val3, ((TMP_Text)val3).transform.parent); ((Object)line.Text).name = "TimbnLine"; } ((TMP_Text)line.Text).rectTransform.anchoredPosition = ((TMP_Text)val2).rectTransform.anchoredPosition + val4 * (float)(_lines.Count - i + 1); ((TMP_Text)line.Text).text = line.Label + _valueStyle.Invoke(val).ApplyStyleToString(line.Value, true, true); ((Component)line.Text).gameObject.SetActive(((Component)val2).gameObject.activeSelf); } } internal static void OnUpdate() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown if (_popups.Count != 0 && !((Object)(object)_menu == (Object)null) && ((LazyWindow<LazyWidgetDataBase>)(object)_menu).IsShownAndTop) { Popup popup = _popups[0]; _popups.RemoveAt(0); UIDialogWindow window = LazyUI.GetWindow<UIDialogWindow>(); ButtonData val = new ButtonData((Action)((LazyWindow<UIDialogWindowData>)(object)window).Close, LLBase.L("btn_ok"), (Func<bool>)null, true, GameKey.Select, ""); ((LazyWindow<UIDialogWindowData>)(object)window).Open(new UIDialogWindowData(popup.Header, popup.Text, val)); } } } public sealed class TimbnPluginMainMenu { private readonly TimbnFrameworkPlugin _owner; internal TimbnPluginMainMenu(TimbnFrameworkPlugin owner) { _owner = owner; } public IDisposable AddLine(string label, string value) { return _owner.Subscriptions.Add(TimbnMainMenu.AddLine(label, value)); } public IDisposable Popup(string header, string text) { return _owner.Subscriptions.Add(TimbnMainMenu.AddPopup(header, text)); } } public abstract class TimbnFrameworkPlugin : BaseUnityPlugin { private static readonly FieldRef<BaseUnityPlugin, PluginInfo?> _infoField = AccessTools.FieldRefAccess<BaseUnityPlugin, PluginInfo>("<Info>k__BackingField"); private static int _harmonyInstances; private Harmony? _harmony; protected BepInPlugin Metadata { get; } public TimbnPluginEvents Events { get; } public TimbnPluginPotions Potions { get; } public TimbnPluginQuests Quests { get; } public TimbnPluginDialog Dialog { get; } public TimbnPluginText Text { get; } public TimbnPluginSprites Sprites { get; } public TimbnPluginBalance Balance { get; } public TimbnPluginMainMenu MainMenu { get; } internal TimbnSubscriptions Subscriptions { get; } internal TimbnSubscriptions SessionSubscriptions { get; } private protected virtual bool IsEnabled => true; protected TimbnFrameworkPlugin() { Metadata = MetadataHelper.GetMetadata((object)this); RepairPluginInfo(); Subscriptions = new TimbnSubscriptions(((BaseUnityPlugin)this).Logger); SessionSubscriptions = new TimbnSubscriptions(((BaseUnityPlugin)this).Logger); Events = new TimbnPluginEvents(this); Potions = new TimbnPluginPotions(this); Quests = new TimbnPluginQuests(this); Dialog = new TimbnPluginDialog(this); Text = new TimbnPluginText(this); Sprites = new TimbnPluginSprites(this); Balance = new TimbnPluginBalance(this); MainMenu = new TimbnPluginMainMenu(this); } private void RepairPluginInfo() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown if (((BaseUnityPlugin)this).Info == null) { PluginInfo val = new PluginInfo(); Traverse obj = Traverse.Create((object)val); obj.Property<BepInPlugin>("Metadata", (object[])null).Value = Metadata; obj.Property<BaseUnityPlugin>("Instance", (object[])null).Value = (BaseUnityPlugin)(object)this; obj.Property<IEnumerable<BepInDependency>>("Dependencies", (object[])null).Value = MetadataHelper.GetDependencies(((object)this).GetType()); obj.Property<IEnumerable<BepInProcess>>("Processes", (object[])null).Value = MetadataHelper.GetAttributes<BepInProcess>(((object)this).GetType()); obj.Property<IEnumerable<BepInIncompatibility>>("Incompatibilities", (object[])null).Value = MetadataHelper.GetAttributes<BepInIncompatibility>(((object)this).GetType()); obj.Property<string>("Location", (object[])null).Value = ((object)this).GetType().Assembly.Location; Chainloader.PluginInfos[Metadata.GUID] = val; _infoField.Invoke((BaseUnityPlugin)(object)this) = val; } } private void Awake() { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown InitConfig(); if (!IsEnabled) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin " + Metadata.GUID + " is disabled in config (its own or Core's Enabled toggle); skipping.")); return; } if (!(this is TimbnCorePlugin) && TimbnCorePlugin.Instance == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Plugin " + Metadata.GUID + " not started because Timbn Core did not start. See Core's log lines above.")); return; } string text = $"{Metadata.GUID}.{++_harmonyInstances}"; Harmony harmony = new Harmony(text); if (TryPatchAll(harmony)) { _harmony = harmony; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Subscriptions.Add(TimbnGameEvents.GameStarted(Events.AttachToSave)); Subscriptions.Add(TimbnGameEvents.GoToMainMenu(SessionSubscriptions.Dispose)); Subscriptions.Add(TimbnMainMenu.AddLine(Metadata.Name + ": ", Metadata.Version.ToString())); ((BaseUnityPlugin)this).Logger.LogMessage((object)$"Plugin {Metadata.GUID} v{Metadata.Version} loaded, patches under '{text}'."); OnAwake(); } } private bool TryPatchAll(Harmony harmony) { List<string> list = new List<string>(); Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(((object)this).GetType().Assembly); foreach (Type type in typesFromAssembly) { try { harmony.CreateClassProcessor(type).Patch(); } catch (Exception ex) { list.Add(type.FullName + ": " + (ex.InnerException ?? ex).Message); } } if (list.Count == 0) { return true; } harmony.UnpatchSelf(); ((BaseUnityPlugin)this).Logger.LogError((object)($"Plugin {Metadata.GUID} v{Metadata.Version} not started. {list.Count} patch class(es) failed on game build {Application.version}, " + "most likely a renamed or removed target. Nothing was left patched." + Environment.NewLine + " " + string.Join(Environment.NewLine + " ", list))); TimbnLoadErrors.AddStartFailure($"{Metadata.Name} {Metadata.Version}"); return false; } private void Update() { if (_harmony != null && IsEnabled) { OnUpdate(); } } private void OnDestroy() { if (_harmony != null) { try { OnDestroyed(); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)string.Format("{0} threw: {1}", "OnDestroyed", arg)); } SessionSubscriptions.Dispose(); Subscriptions.Dispose(); _harmony.UnpatchSelf(); _harmony = null; ((BaseUnityPlugin)this).Logger.LogMessage((object)("Plugin " + Metadata.GUID + " unloaded, subscriptions and patches removed.")); } } private protected virtual void InitConfig() { } protected virtual void OnAwake() { } protected virtual void OnUpdate() { } protected virtual void OnDestroyed() { } } public abstract class TimbnFrameworkPlugin<T> : TimbnFrameworkPlugin where T : TimbnFrameworkPlugin<T> { public static ManualLogSource Logger { get; private set; } public static ConfigEntry<bool> Enabled { get; private set; } private protected sealed override bool IsEnabled { get { if (Enabled.Value) { return TimbnFrameworkPlugin<TimbnCorePlugin>.Enabled.Value; } return false; } } protected TimbnFrameworkPlugin() { Logger = ((BaseUnityPlugin)this).Logger; } private protected sealed override void InitConfig() { Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master toggle for " + base.Metadata.Name + "."); BindConfig(((BaseUnityPlugin)this).Config); } protected virtual void BindConfig(ConfigFile config) { } } public static class TimbnGame { private static bool _started; public static bool IsInGame { get { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 if (_started && (Object)(object)MainGame.Instance != (Object)null) { return (int)MainGame.Instance.gameState == 1; } return false; } } internal static void OnGameStarted() { _started = true; } internal static void OnLeftGame() { _started = false; } } internal static class TimbnLoadErrors { private const int _maxLines = 6; private static readonly Regex _missing = new Regex("^Could not load \\[(.+)\\] because it has missing dependencies: (.+)$"); private static readonly Regex _skipped = new Regex("^Skipping \\[(.+)\\] because it has a dependency that was not loaded"); private static readonly Regex _incompatible = new Regex("^Could not load \\[(.+)\\] because it is incompatible with: (.+)$"); private static readonly Regex _wrongBepInEx = new Regex("^Plugin \\[.+\\] targets a wrong version of BepInEx"); private static readonly Regex _dependency = new Regex("^(\\S+)(?: \\(v(.+) or newer\\))?$"); private static readonly List<string> _startFailures = new List<string>(); internal static void AddStartFailure(string plugin) { _startFailures.Add(plugin); } internal static string? Describe() { List<string> list = new List<string>(); foreach (string dependencyError in Chainloader.DependencyErrors) { string text = Describe(dependencyError); if (text != null) { list.Add(text); } } foreach (string startFailure in _startFailures) { list.Add(startFailure + " is off because a game update changed something it needs."); } if (list.Count == 0) { return null; } List<string> list2 = list.Take(6).ToList(); if (list.Count > 6) { list2.Add($"And {list.Count - 6} more."); } list2.Add("BepInEx/LogOutput.log has the details."); return string.Join("\n\n", list2); } private static string? Describe(string error) { if (_wrongBepInEx.IsMatch(error)) { return null; } Match match = _missing.Match(error); if (match.Success) { IEnumerable<string> values = match.Groups[2].Value.Split(new string[1] { ", " }, StringSplitOptions.RemoveEmptyEntries).Select(DescribeDependency); return match.Groups[1].Value + " needs " + string.Join(" and ", values) + "."; } match = _skipped.Match(error); if (match.Success) { return match.Groups[1].Value + " is off because a mod it needs did not load."; } match = _incompatible.Match(error); if (match.Success) { IEnumerable<string> values2 = match.Groups[2].Value.Split(new string[1] { ", " }, StringSplitOptions.RemoveEmptyEntries).Select(NameOf); return match.Groups[1].Value + " can't run alongside " + string.Join(" and ", values2) + "."; } return error; } private static string DescribeDependency(string entry) { Match match = _dependency.Match(entry); if (!match.Success) { return entry; } string value = match.Groups[1].Value; string text = (match.Groups[2].Success ? match.Groups[2].Value : null); if (Chainloader.PluginInfos.TryGetValue(value, out var value2)) { Version version = value2.Metadata.Version; if (text != null) { return $"{value2.Metadata.Name} {text} or newer (you have {version})"; } return value2.Metadata.Name; } if (text != null) { return value + " " + text + " or newer, which is missing"; } return value + ", which is missing"; } private static string NameOf(string guid) { if (!Chainloader.PluginInfos.TryGetValue(guid, out var value)) { return guid; } return value.Metadata.Name; } } internal sealed class TimbnSubscriptions : IDisposable { private readonly List<IDisposable> _items = new List<IDisposable>(); private readonly ManualLogSource _logger; public int Count => _items.Count; internal TimbnSubscriptions(ManualLogSource logger) { _logger = logger; } public T Add<T>(T subscription) where T : IDisposable { _items.Add(subscription); return subscription; } public IDisposable Add(Action undo) { return Add(new TimbnUndo(undo)); } public void Dispose() { for (int num = _items.Count - 1; num >= 0; num--) { try { _items[num].Dispose(); } catch (Exception arg) { _logger.LogError((object)string.Format("{0}|An undo threw: {1}", "TimbnSubscriptions", arg)); } } _items.Clear(); } } internal sealed class TimbnUndo : IDisposable { private Action? _undo; public TimbnUndo(Action undo) { _undo = undo; } public void Dispose() { Action? undo = _undo; _undo = null; undo?.Invoke(); } } public sealed class TimbnPluginPotions { private readonly TimbnFrameworkPlugin _owner; internal TimbnPluginPotions(TimbnFrameworkPlugin owner) { _owner = owner; } public IDisposable Register(TimbnPotion potion) { return _owner.Subscriptions.Add(TimbnPotions.Register(potion)); } } public sealed class TimbnPotion { public string Id { get; set; } = ""; public string Name { get; set; } = ""; public string Description { get; set; } = ""; public string TemplateItemId { get; set; } = "third_eye_salt"; public string IconId { get; set; } = ""; public int Price { get; set; } = 100; public Vector3Int Runes { get; set; } public AlchemyFormulaTab Tab { get; set; } = (AlchemyFormulaTab)3; public bool HiddenAtStart { get; set; } public TimbnPotionBuff Buff { get; set; } = new TimbnPotionBuff(); public string BuffId => Id + "_buff"; } public sealed class TimbnPotionBuff { public string Name { get; set; } = ""; public string Description { get; set; } = ""; public Func<float> Seconds { get; set; } = () => 60f; public Func<string> GlowColor { get; set; } = () => "ltyellow"; public string IconId { get; set; } = "b_speed"; public List<string> OnAddExpressions { get; set; } = new List<string>(); public List<string> OnRemoveExpressions { get; set; } = new List<string>(); public Action? OnStart { get; set; } public Action? WhileActive { get; set; } public Action? OnEnd { get; set; } } internal static class TimbnPotionBalance { private const string _potionDrinkerInspiration = "AddInspiration(\"insp_potion_drinker\", 1)"; private static readonly Action<GameBalanceBase> _createIdsCache = AccessTools.MethodDelegate<Action<GameBalanceBase>>(AccessTools.Method(typeof(GameBalanceBase), "CreateIDsCache", (Type[])null, (Type[])null), (object)null, true); private static readonly FieldRef<ItemDef, string> _itemCustomIcon = AccessTools.FieldRefAccess<ItemDef, string>("customIcon"); private static readonly FieldRef<PerkDef, string> _perkCustomIcon = AccessTools.FieldRefAccess<PerkDef, string>("customIcon"); private static readonly string[] _alchemyStations = new string[2] { "alchemy_mix", "alchemy_mix_2" }; private static readonly List<ItemDef> _items = new List<ItemDef>(); private static readonly List<PerkDef> _perks = new List<PerkDef>(); private static readonly List<AlchemyFormulaDef> _formulas = new List<AlchemyFormulaDef>(); private static readonly List<AlchemyMixSourceDef> _mixes = new List<AlchemyMixSourceDef>(); private static GameBalance? _balance; public static void Apply(IReadOnlyList<TimbnPotion> potions) { Remove(); GameBalance loaded = TimbnBalance.Loaded; if (loaded == null || potions.Count == 0) { return; } foreach (TimbnPotion potion in potions) { if (!TryAdd(loaded, potion, out string reason)) { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogWarning((object)("TimbnPotions|Skipped " + potion.Id + ". " + reason)); } } _balance = loaded; _createIdsCache((GameBalanceBase)(object)loaded); foreach (ItemDef item in _items) { AddToGroupCache(loaded, item); } foreach (AlchemyMixSourceDef mix in _mixes) { loaded.alchemyMixSourcesByIdCache[mix.mixId] = mix; } LLBase.AddAliases(_mixes.Select((AlchemyMixSourceDef m) => m.mixId).ToList(), _mixes.Select((AlchemyMixSourceDef m) => m.formulaId).ToList()); TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogInfo((object)string.Format("{0}|Added {1} potions with {2} ingredient mixes.", "TimbnPotions", _items.Count, _mixes.Count)); } public static void RefreshBuffs(IReadOnlyList<TimbnPotion> potions) { foreach (PerkDef perk in _perks) { TimbnPotion timbnPotion = potions.FirstOrDefault((TimbnPotion p) => p.BuffId == ((BalanceBaseObject)perk).id); if (timbnPotion != null) { ConfigureBuff(perk, timbnPotion.Buff); } } } private static void Remove() { GameBalance balance = _balance; _balance = null; if ((Object)(object)balance == (Object)null) { return; } foreach (ItemDef item in _items) { balance.itemDefs.Remove(item); foreach (string itemGroupId in item.itemGroupIds) { if (balance.groupItemsCache.TryGetValue(itemGroupId, out var value)) { value.Remove(item); } } } foreach (PerkDef perk in _perks) { balance.perkDefs.Remove(perk); } foreach (AlchemyFormulaDef formula in _formulas) { balance.alchemyFormulaDefs.Remove(formula); } foreach (AlchemyMixSourceDef mix in _mixes) { balance.alchemyMixSourceDefs.Remove(mix); balance.alchemyMixSourcesByIdCache.Remove(mix.mixId); balance.alchemyMixDefsCache.Remove(mix.mixId); balance.runtimeCraftDefsCacheAlchemy.Remove(mix.mixId); } _createIdsCache((GameBalanceBase)(object)balance); _items.Clear(); _perks.Clear(); _formulas.Clear(); _mixes.Clear(); } private static bool TryAdd(GameBalance balance, TimbnPotion potion, out string reason) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Expected O, but got Unknown //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Expected O, but got Unknown //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Expected O, but got Unknown ItemDef val = balance.itemDefs.Find((ItemDef i) => ((BalanceBaseObject)i).id == potion.TemplateItemId); if (val == null) { reason = "Template item " + potion.TemplateItemId + " does not exist."; return false; } if (balance.itemDefs.Exists((ItemDef i) => ((BalanceBaseObject)i).id == potion.Id)) { reason = "An item with that id already exists."; return false; } AlchemyFormulaDef val2 = balance.alchemyFormulaDefs.Find((AlchemyFormulaDef f) => f.GetRunesAsVector3Int() == potion.Runes); if (val2 != null) { reason = $"Runes {potion.Runes} already belong to {((BalanceBaseObject)val2).id}, so no mix would brew it."; return false; } PerkDef val3 = balance.perkDefs.Find((PerkDef p) => p.worldFxPrefabId?.StartsWith("buff_char_") ?? false); if (val3 == null) { reason = "No existing buff with a glow to copy."; return false; } PerkDef val4 = Copy<PerkDef>(val3); ((BalanceBaseObject)val4).id = potion.BuffId; ConfigureBuff(val4, potion.Buff); balance.perkDefs.Add(val4); _perks.Add(val4); ItemDef val5 = Copy<ItemDef>(val); ((BalanceBaseObject)val5).id = potion.Id; if (!string.IsNullOrEmpty(potion.IconId)) { val5.iconId = potion.IconId; _itemCustomIcon.Invoke(val5) = potion.IconId; } val5.basePrice = potion.Price; val5.canBeUsedInAlchemy = false; val5.onUseExpressions = new List<LazyExpression>(2) { new LazyExpression("AddInspiration(\"insp_potion_drinker\", 1)"), new LazyExpression("AddPerk(\"" + ((BalanceBaseObject)val4).id + "\")") }; val5.sortOrder = balance.itemDefs.Count; balance.itemDefs.Add(val5); _items.Add(val5); AlchemyFormulaDef val6 = balance.alchemyFormulaDefs.Find((AlchemyFormulaDef f) => ((BalanceBaseObject)f).id == potion.TemplateItemId); AlchemyFormulaDef val7 = new AlchemyFormulaDef { id = potion.Id }; Vector3Int runes = potion.Runes; val7.runesRed = ((Vector3Int)(ref runes)).x; runes = potion.Runes; val7.runesGreen = ((Vector3Int)(ref runes)).y; runes = potion.Runes; val7.runesBlue = ((Vector3Int)(ref runes)).z; val7.tab = potion.Tab; val7.hiddenAtStart = potion.HiddenAtStart; val7.craftsIn = _alchemyStations.ToList(); val7.onCraftEndExpressions = ((val6 != null) ? Copy<AlchemyFormulaDef>(val6).onCraftEndExpressions : new List<LazyExpression>()); AlchemyFormulaDef val8 = val7; balance.alchemyFormulaDefs.Add(val8); _formulas.Add(val8); AddMixes(balance, val8); reason = ""; return true; } private static void ConfigureBuff(PerkDef perk, TimbnPotionBuff buff) { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown string text = buff.GlowColor(); _perkCustomIcon.Invoke(perk) = buff.IconId; perk.duration = buff.Seconds(); perk.worldFxPrefabId = "buff_char_" + text; perk.hudFxPrefabId = "buff_hud_" + text; perk.onAddExpressions = ((IEnumerable<string>)buff.OnAddExpressions).Select((Func<string, LazyExpression>)((string e) => new LazyExpression(e))).ToList(); perk.onRemoveExpressions = ((IEnumerable<string>)buff.OnRemoveExpressions).Select((Func<string, LazyExpression>)((string e) => new LazyExpression(e))).ToList(); perk.setGameResOnAdd = new GameRes(); perk.addGameResOnAdd = new GameRes(); perk.setGameResOnRemove = new GameRes(); perk.addGameResOnRemove = new GameRes(); perk.addGameResPerTick = new GameRes(); } private static void AddMixes(GameBalance balance, AlchemyFormulaDef formula) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_015f: 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_0169: Unknown result type (might be due to invalid IL or missing references) Vector3Int target = formula.GetRunesAsVector3Int(); List<ItemDef> list = balance.itemDefs.Where((ItemDef i) => i.canBeUsedInAlchemy && i.GetRunesAsVector3Int() != Vector3Int.zero).ToList(); List<CraftDef> boosts = balance.craftDefs.Where((CraftDef c) => ((BalanceBaseObject)c).id.EndsWith("_boost")).ToList(); List<Vector3Int> list2 = list.Select((ItemDef i) => i.GetRunesAsVector3Int()).ToList(); List<Vector3Int> boostRunes = boosts.Select((CraftDef b) => b.GetBoostRunesAsVector3Int()).ToList(); for (int num = 0; num < list.Count; num++) { Consider(list2[num], (ItemDef[])(object)new ItemDef[1] { list[num] }); for (int num2 = 0; num2 < list.Count; num2++) { Vector3Int val = list2[num] + list2[num2]; Consider(val, (ItemDef[])(object)new ItemDef[2] { list[num], list[num2] }); for (int num3 = 0; num3 < list.Count; num3++) { Consider(val + list2[num3], (ItemDef[])(object)new ItemDef[3] { list[num], list[num2], list[num3] }); } } } void Consider(Vector3Int sum, params ItemDef[] parts) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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) if (sum == target) { TryAddMix(balance, formula, parts, null); } for (int i = 0; i < boosts.Count; i++) { if (sum + boostRunes[i] == target) { TryAddMix(balance, formula, parts, boosts[i]); } } } } private static void TryAddMix(GameBalance balance, AlchemyFormulaDef formula, ItemDef[] parts, CraftDef? boost) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown string mixId = AlchemyMixDef.MixId(parts.Select((ItemDef p) => ((BalanceBaseObject)p).id).ToArray(), boost); if (!balance.alchemyMixSourcesByIdCache.ContainsKey(mixId) && !_mixes.Exists((AlchemyMixSourceDef m) => m.mixId == mixId)) { AlchemyMixSourceDef item = new AlchemyMixSourceDef { mixId = mixId, formulaId = ((BalanceBaseObject)formula).id, ingredient1 = ((parts.Length != 0) ? ((BalanceBaseObject)parts[0]).id : ""), ingredient2 = ((parts.Length > 1) ? ((BalanceBaseObject)parts[1]).id : ""), ingredient3 = ((parts.Length > 2) ? ((BalanceBaseObject)parts[2]).id : "") }; balance.alchemyMixSourceDefs.Add(item); _mixes.Add(item); } } private static void AddToGroupCache(GameBalance balance, ItemDef item) { foreach (string itemGroupId in item.itemGroupIds) { if (!balance.groupItemsCache.TryGetValue(itemGroupId, out var value)) { value = (balance.groupItemsCache[itemGroupId] = new List<ItemDef>()); } if (!value.Contains(item)) { value.Add(item); } } } private static T Copy<T>(T source) { return JsonUtility.FromJson<T>(JsonUtility.ToJson((object)source)); } } public static class TimbnPotions { private static readonly List<TimbnPotion> _potions = new List<TimbnPotion>(); private static readonly List<TimbnPotion> _active = new List<TimbnPotion>(); internal static IDisposable Register(TimbnPotion potion) { TimbnPotion timbnPotion = _potions.Find((TimbnPotion p) => p.Id == potion.Id); if (timbnPotion != null) { Unregister(timbnPotion); } _potions.Add(potion); IDisposable text = TimbnLocale.Add(new Dictionary<string, string> { [potion.Id] = potion.Name, [potion.Id + "_d"] = potion.Description, [potion.BuffId] = potion.Buff.Name, [potion.BuffId + "_d"] = potion.Buff.Description }); TimbnPotionBalance.Apply(_potions); if (TimbnGame.IsInGame) { GameSave gameSave = MainGame.Instance.GameSave; if (gameSave != null) { PerkSystemData perkSystemData = gameSave.perkSystemData; if (((perkSystemData != null) ? new bool?(perkSystemData.HasPerk(potion.BuffId)) : ((bool?)null)) == true) { Start(potion); } } } return new TimbnUndo(delegate { Unregister(potion); text.Dispose(); }); } internal static void OnBalanceLoaded() { TimbnPotionBalance.Apply(_potions); } internal static void OnGameStarted(TimbnSubscriptions session) { TimbnPotionBalance.RefreshBuffs(_potions); PerkSystemData perks = MainGame.Instance.GameSave.perkSystemData; session.Add(TimbnGameEvents.On(OnPerkAdded, delegate(Action<PerkData> h) { perks.OnPerkAdded += h; }, delegate(Action<PerkData> h) { perks.OnPerkAdded -= h; })); session.Add(TimbnGameEvents.On(OnPerkRemoved, delegate(Action<PerkData> h) { perks.OnPerkRemoved += h; }, delegate(Action<PerkData> h) { perks.OnPerkRemoved -= h; })); session.Add(EndAll); foreach (PerkData activePerk in perks.activePerks) { OnPerkAdded(activePerk); } } internal static void OnUpdate() { for (int i = 0; i < _active.Count; i++) { Run(_active[i], "WhileActive", _active[i].Buff.WhileActive); } } private static void Unregister(TimbnPotion potion) { if (_potions.Remove(potion)) { End(potion); TimbnPotionBalance.Apply(_potions); } } private static void OnPerkAdded(PerkData perk) { TimbnPotion timbnPotion = _potions.Find((TimbnPotion p) => p.BuffId == ((ObjectLinkedToDefinition<PerkDef>)(object)perk).id); if (timbnPotion != null) { Start(timbnPotion); } } private static void OnPerkRemoved(PerkData perk) { TimbnPotion timbnPotion = _active.Find((TimbnPotion p) => p.BuffId == ((ObjectLinkedToDefinition<PerkDef>)(object)perk).id); if (timbnPotion != null) { End(timbnPotion); } } private static void Start(TimbnPotion potion) { if (!_active.Contains(potion)) { _active.Add(potion); Run(potion, "OnStart", potion.Buff.OnStart); } } private static void End(TimbnPotion potion) { if (_active.Remove(potion)) { Run(potion, "OnEnd", potion.Buff.OnEnd); } } private static void EndAll() { foreach (TimbnPotion item in _active.ToList()) { End(item); } } private static void Run(TimbnPotion potion, string hook, Action? action) { if (action == null) { return; } try { action(); } catch (Exception ex) { TimbnFrameworkPlugin<TimbnCorePlugin>.Logger.LogError((object)string.Format("{0}|{1} for {2} threw: {3}", "TimbnPotions", hook, potion.Id, ex)); } } } public sealed class TimbnPluginQuests { private readonly TimbnFrameworkPlugin _owner; internal TimbnPluginQuests(TimbnFrameworkPlugin owner) { _owner = owner; } public IDisposable Add(TimbnQuest quest) { quest.Validate(); IDisposable text = TimbnLocale.Add(quest.Texts()); IDisposable registered = TimbnQuests.Register(quest.ToDefinition()); IDisposable talk = TimbnDialog.AddTalk(quest.Npc, quest.Id + "_talk", quest.IsOffered, quest.Script); return _owner.Subscriptions.Add(new TimbnUndo(delegate { talk.Dispose(); registered.Dispose(); text.Dispose(); })); } public IDisposable Register(QuestDef definition) { return _owner.Subscriptions.Add(TimbnQuests.Register(definition)); } } public enum TimbnQuestCompletion { Fetch, Custom } public sealed class TimbnQuest { public string Id { get; set; } = ""; public string Npc { get; set; } = ""; public string Icon { get; set; } = ""; public string Name { get; set; } = ""; public string Description { get; set; } = ""; public string CompletedDescription { get; set; } = ""; public TimbnQuestCompletion Completion { get; set; } public ItemCount[] Wants { get; set; } = Array.Empty<ItemCount>(); public int RewardMoney { get; set; } public string After { get; set; } = ""; public Func<bool>? AvailableWhen { get; set; } public string[] Offer { get; set; } = Array.Empty<string>(); public string Accept { get; set; } = ""; public string Decline { get; set; } = ""; public string[] Accepted { get; set; } = Array.Empty<string>(); public string[] Declined { get; set; } = Array.Empty<string>(); public string[] Remind { get; set; } = Array.Empty<string>(); public string Give { get; set; } = ""; public string Later { get; set; } = ""; public string[] Thanks { get; set; } = Array.Empty<string>(); public QuestStatus? Status => TimbnQuests.StatusOf(Id); internal string GiveKey => Id + "_give"; public void Complete() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 if ((int)Status.GetValueOrDefault() == 2) { TimbnQuests.Complete(Id); if (RewardMoney > 0) { MainGame.PlayerData.AddRes("money", (float)RewardMoney); } } } internal void Validate() { if (string.IsNullOrEmpty(Id) || string.IsNullOrEmpty(Npc)) { throw new ArgumentException("A TimbnQuest needs an Id and an Npc."); } if (string.IsNullOrEmpty(Accept)) { throw new ArgumentException("Quest " + Id + " needs an Accept answer."); } if (Completion == TimbnQuestCompletion.Fetch && (Wants.Length == 0 || string.IsNullOrEmpty(Give))) { throw new ArgumentException("Fetch quest " + Id + " needs Wants and a Give answer."); } } internal QuestDef ToDefinition() { QuestDef val = TimbnQuests.CreateDefinition(Id); if (Completion == TimbnQuestCompletion.Fetch) { val.FinishOnAnswer(GiveKey, Wants).RewardMoney(RewardMoney); } if (!string.IsNullOrEmpty(After)) { val.After(After); } val.wgoNpcId = Npc; val.iconId = Icon; return val; } internal Dictionary<string, string> Texts() { Dictionary<string, string> dictionary = new Dictionary<string, string> { [Id] = Name, ["quest_open_" + Id + "_d"] = Description, ["quest_closed_" + Id + "_d"] = (string.IsNullOrEmpty(CompletedDescription) ? Description : CompletedDescription), [Id + "_accept"] = Accept, [Id + "_decline"] = Decline, [GiveKey] = Give, [Id + "_later"] = Later }; AddLines(dictionary, "offer", Offer); AddLines(dictionary, "accepted", Accepted); AddLines(dictionary, "declined", Declined); AddLines(dictionary, "remind", Remind); AddLines(dictionary, "thanks", Thanks); return dictionary; } internal bool IsOffered() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Invalid comparison between Unknown and I4 QuestStatus? status = Status; if (status.HasValue) { QuestStatus valueOrDefault = status.GetValueOrDefault(); if ((int)valueOrDefault == 2) { if (Completion != TimbnQuestCompletion.Fetch) { return Remind.Length != 0; } return TimbnQuests.CanFinish(Id); } if (valueOrDefault - 3 > 1) { if (string.IsNullOrEmpty(After) || (int)TimbnQuests.StatusOf(After).GetValueOrDefault() == 3) { return AvailableWhen?.Invoke() ?? true; } return false; } } return false; } internal void Script(TimbnConversation talk) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 if ((int)Status.GetValueOrDefault() == 2) { SayLines(talk, "remind", Remind); if (Completion == TimbnQuestCompletion.Fetch) { talk.Ask(Answers(GiveKey, Id + "_later", Later)).If(GiveKey, delegate(TimbnConversation then) { SayLines(then, "thanks", Thanks); }); } return; } SayLines(talk, "offer", Offer); talk.Ask(Answers(Id + "_accept", Id + "_decline", Decline)).If(Id + "_accept", delegate(TimbnConversation then) { SayLines(then.Do(delegate { TimbnQuests.Start(Id); }), "accepted", Accepted); }).If(Id + "_decline", delegate(TimbnConversation then) { SayLines(then, "declined", Declined); }); } private static string[] Answers(string first, string secondKey, string secondText) { if (string.IsNullOrEmpty(secondText)) { return new string[1] { first }; } return new string[2] { first, secondKey }; } private void AddLines(Dictionary<string, string> texts, string part, string[] lines) { for (int i = 0; i < lines.Length; i++) { texts[$"{Id}_{part}_{i + 1}"] = lines[i]; } } private void SayLines(TimbnConversation talk, string part, string[] lines) { for (int i = 0; i < lines.Length; i++) { talk.Say($"{Id}_{part}_{i + 1}"); } } } public static class TimbnQuests { private static readonly FieldRef<QuestDef, int> _posX = AccessTools.FieldRefAccess<QuestDef, int>("posX"); private static readonly FieldRef<QuestDef, int> _posY = AccessTools.FieldRefAccess<QuestDef, int>("posY"); private static readonly FieldRef<ObjectLinkedToDefinition<QuestDef>, string> _cachedId = AccessTools.FieldRefAccess<ObjectLinkedToDefinition<QuestDef>, string>("cachedId"); private static readonly Action<QuestDef> _linkToParents = AccessTools.MethodDelegate<Action<QuestDef>>(AccessTools.Method(typeof(QuestDef), "InitParentsAndChildren", (Type[])null, (Type[])null), (object)null, true); private static readonly Dictionary<string, QuestDef> _quests = new Dictionary<string, QuestDef>(); private static readonly Dictionary<string, int> _registeredAt = new Dictionary<string, int>(); private static int _registrations; private static readonly Dictionary<string, IDisposable> _stubs = new Dictionary<string, IDisposable>(); private static readonly Dictionary<QuestDef, int> _gameY = new Dictionary<QuestDef, int>(); private static readonly ConditionalWeakTable<QuestDef, StrongBox<int>> _moneyRewards = new ConditionalWeakTable<QuestDef, StrongBox<int>>(); private static QuestSystemData? LoadedQuests { get { MainGame instance = MainGame.Instance; QuestSystemData val = ((instance == null) ? null : instance.GameSave?.questSystemData); if (val?.questCollection?.questsCache != null) { return val; } return null; } } public static QuestDef CreateDefinition(string id) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_003f: Expected O, but got Unknown return new QuestDef { id = id, isHidden = true, hasPosInBalance = true, startCheck = new QuestCheck { questId = id }, finishCheck = new QuestFinishCheck { questId = id } }; } public static QuestDef After(this QuestDef definition, string parentId) { definition.parents.Add(parentId); return definition; } public static QuestDef FinishOnAnswer(this QuestDef definition, string answerId, params ItemCount[] price) { //IL_000a: 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) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown QuestFinishCheck finishCheck = definition.finishCheck; ((QuestCheck)finishCheck).triggerType = (Type)15; ((QuestCheck)finishCheck).triggerId = answerId; ((QuestCheck)finishCheck).hasTrigger = true; finishCheck.phrase = answerId; foreach (ItemCount itemCount in price) { finishCheck.phraseReqs.Add(new QuestPhraseRequirement { requirement = (Requirement)0, entity = (Entity)0, itemCount = itemCount }); } return definition; } public static ItemCount Item(string itemId, int count) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown return new ItemCount { itemId = itemId, count = count }; } public static QuestDef RewardMoney(this QuestDef definition, int amount) { _moneyRewards.Remove(definition); _moneyRewards.Add(definition, new StrongBox<int>(amount)); return definition; } internal static int MoneyRewardOf(QuestDef definition) { if (!_moneyRewards.TryGetValue(definition, out StrongBox<int> value)) { return 0; } return value.Value; } internal static IDisposable Register(QuestDef definition) { FillBlanks(definition); if (!_registeredAt.ContainsKey(((BalanceBaseObject)definition).id)) { _registeredAt[((BalanceBaseObject)definition).id] = _registrations++; } IDisposable balance = TimbnBalance.Add((BalanceBaseObject)(object)definition, delegate(GameBalance b) { Relayout(b, definition); _linkToParents(definition); }); _quests[((BalanceBaseObject)definition).id] = definition; if (_stubs.TryGetValue(((BalanceBaseObject)definition).id, out IDisposable value)) { _stubs.Remove(((BalanceBaseObject)definition).id); value.Dispose(); } AttachToLoadedSave(definition); RelayoutIfLoaded(); return new TimbnUndo(delegate { QuestDef value2; bool flag = _quests.TryGetValue(((BalanceBaseObject)definition).id, out value2) && value2 != definition; if (!flag) { _quests.Remove(((BalanceBaseObject)definition).id); } DetachFromLoadedSave(definition, flag); Unlink(definition); balance.Dispose(); RelayoutIfLoaded(); }); } private static void RelayoutIfLoaded() { GameBalance loaded = TimbnBalance.Loaded; if (loaded != null) { Relayout(loaded, null); } } internal static void OnQuestStarted(QuestData quest) { if (_quests.ContainsKey(((ObjectLinkedToDefinition<QuestDef>)(object)quest).id)) { RelayoutIfLoaded(); } } public static QuestStatus? StatusOf(string id) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) QuestSystemData? loadedQuests = LoadedQuests; if (loadedQuests == null || !loadedQuests.questCollection.questsCache.TryGetValue(id, out var value)) { return null; } return value.status; } public static void Start(string id) { QuestSystemData? loadedQuests = LoadedQuests; if (loadedQuests != null) { loadedQuests.StartQuest(id, 0f); } } internal static void Complete(string id) { QuestSystemData? loadedQuests = LoadedQuests; if (loadedQuests != null) { loadedQuests.CompleteQuest(id, 0f); } } public static bool CanFinish(string id) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 if ((int)StatusOf(id).GetValueOrDefault() == 2) { QuestDef dataOrNull = ((GameBalanceBase)GameBalance.Me).GetDataOrNull<QuestDef>(id); if (dataOrNull == null) { return false; } return dataOrNull.finishCheck.IsReadyToFinish(); } return false; } private static void FillBlanks(QuestDef definition) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown QuestDef val = definition; if (val.iconId == null) { val.iconId = string.Empty; } val = definition; if (val.wgoNpcId == null) { val.wgoNpcId = string.Empty; } val = definition; if (val.repVisualisationRes == null) { val.repVisualisationRes = new GameRes(); } val = definition; if (val.startCheck == null) { val.startCheck = new QuestCheck { questId = ((BalanceBaseObject)definition).id }; } val = definition; if (val.finishCheck == null) { val.finishCheck = new QuestFinishCheck { questId = ((BalanceBaseObject)definition).id }; } QuestCheck[] array = (QuestCheck[])(object)new QuestCheck[2] { definition.startCheck, (QuestCheck)definition.finishCheck }; foreach (QuestCheck val2 in array) { if (val2.triggerId == null) { val2.triggerId = string.Empty; } } QuestFinishCheck finishCheck = definition.finishCheck; if (finishCheck.phrase == null) { finishCheck.phrase = string.Empty; } } private static void Relayout(GameBalance balance, QuestDef? adding, Dictionary<string, QuestData>? saved = null) { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_042f: Unknown result type (might be due to invalid IL or missing references) //IL_053d: Unknown result type (might be due to invalid IL or missing references) //IL_0465: Unknown result type (might be due to invalid IL or missing references) //IL_046a: Unknown result type (might be due to invalid IL or missing references) //IL_0475: Unknown result type (might be due to invalid IL or missing references) //IL_047a: Unknown result type (might be due to invalid IL or missing references) //IL_04b4: Unknown result type (might be due to invalid IL or missing references) //IL_0514: Unknown result type (might be due to invalid IL or missing references) //IL_0519: Unknown result type (might be due to invalid IL or missing references) //IL_0526: Unknown result type (might be due to invalid IL or missing references) //IL_052b: Unknown result type (might be due to invalid IL or missing references) //IL_0534: Unknown result type (might be due to invalid IL or missing references) //IL_0539: Unknown result type (might be due to invalid IL or missing references) List<QuestDef> list = _quests.Values.Where((QuestDef q) => ((BalanceBaseObject)q).id != ((BalanceBaseObject)(adding?)).id).ToList(); if (adding != null) { list.Add(adding); } Dictionary<string, QuestDef> oursById = list.ToDictionary((QuestDef q) => ((BalanceBaseObject)q).id); List<QuestDef> list2 = balance.questDefs.Where((QuestDef q) => !oursById.ContainsKey(((BalanceBaseObject)q).id)).ToList(); Vector2Int treePos; foreach (QuestDef item3 in list2) { if (!_gameY.ContainsKey(item3)) { Dictionary<QuestDef, int> gameY = _gameY; treePos = item3.TreePos; gameY[item3] = ((Vector2Int)(ref treePos)).y; } } Dictionary<string, QuestDef> dictionary = new Dictionary<string, QuestDef>(); foreach (QuestDef item4 in list2) { dictionary[((BalanceBaseObject)item4).id] = item4; } Dictionary<string, QuestData> cache = saved ?? LoadedQuests?.questCollection.questsCache; int num = 0; Dictionary<QuestDef, List<QuestDef>> dictionary2 = new Dictionary<QuestDef, List<QuestDef>>(); List<QuestDef> list3 = new List<QuestDef>(); foreach (QuestDef item5 in list.OrderBy(Order)) { if (item5.parents.Count > 0 && dictionary.TryGetValue(item5.parents[0], out var value)) { LinkToCell(item5, value, list2); } if (!shown(item5)) { SetPosition(item5, new Vector2Int(0, -1 - num++)); continue; } QuestDef val = parentOf(item5); if (val == null) { list3.Add(item5); continue; } if (!dictionary2.TryGetValue(val, out var value2)) { value2 = (dictionary2[val] = new List<QuestDef>()); } value2.Add(item5); } int width = list2.Where((QuestDef q) => q.hasPosInBalance).Select(delegate(QuestDef q) { //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) Vector2Int treePos2 = q.TreePos; return ((Vector2Int)(ref treePos2)).x; }).DefaultIfEmpty(0) .Max() + 1; HashSet<Vector2Int> hashSet = new HashSet<Vector2Int>(); List<(QuestDef, QuestDef)> list5 = new List<(QuestDef, QuestDef)>(); foreach (QuestDef item6 in list3) { if (item6.parents.Count > 0 && dictionary.TryGetValue(item6.parents[0], out var value3)) { list5.Add((item6, value3)); continue; } int width2; Dictionary<QuestDef, Vector2Int> shape = Shape(item6, dictionary2, out width2); Place(shape, FindSpot(shape, width2, hashSet, width, 0, 0), hashSet); } int num2 = ((hashSet.Count != 0) ? (hashSet.Max((Vector2Int c) => ((Vector2Int)(ref c)).y) + 2) : 0); foreach (QuestDef item7 in list2) { _posY.Invoke(item7) = _gameY[item7] + num2; } List<QuestDef> list6 = new List<QuestDef>(); foreach (var item8 in list5.OrderBy<(QuestDef, QuestDef), int>(delegate((QuestDef Root, QuestDef Anchor) a) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) Vector2Int treePos2 = a.Anchor.TreePos; return ((Vector2Int)(ref treePos2)).y; })) { QuestDef item = item8.Item1; QuestDef item2 = item8.Item2; int width3; Dictionary<QuestDef, Vector2Int> dictionary3 = Shape(item, dictionary2, out width3); int num3 = dictionary3.Values.Max((Vector2Int c) => ((Vector2Int)(ref c)).y) + 1; if (!TryNextTo(dictionary3, width3, item2.TreePos, 2, list2, dictionary, hashSet, list6, width, out var origin)) { foreach (QuestDef item9 in list2.Concat(list6)) { treePos = item9.TreePos; int y = ((Vector2Int)(ref treePos)).y; treePos = item2.TreePos; if (y > ((Vector2Int)(ref treePos)).y) { _posY.Invoke(item9) += num3; } } if (!TryNextTo(dictionary3, width3, item2.TreePos, 1, list2, dictionary, hashSet, list6, width, out origin)) { HashSet<Vector2Int> hashSet2 = BlockedCells(list2, dictionary); hashSet2.UnionWith(hashSet); hashSet2.UnionWith(list6.Select((QuestDef q) => q.TreePos)); int shapeWidth = width3; treePos = item2.TreePos; int startY = ((Vector2Int)(ref treePos)).y + 1; treePos = item2.TreePos; origin = FindSpot(dictionary3, shapeWidth, hashSet2, width, startY, ((Vector2Int)(ref treePos)).x); } } Place(dictionary3, origin, new HashSet<Vector2Int>()); list6.AddRange(dictionary3.Keys); } TimbnBalance.RefreshQuestCaches(balance); QuestDef? parentOf(QuestDef q) { if (q.parents.Count <= 0 || !oursById.TryGetValue(q.parents[0], out var value4) || value4 == q || !shown(value4)) { return null; } return value4; } bool shown(QuestDef q) { if (cache != null && cache.TryGetValue(((BalanceBaseObject)q).id, out QuestData value4)) { return !value4.isHidden; } return false; } } private static void LinkToCell(QuestDef root, QuestDef anchor, List<QuestDef> game) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) foreach (QuestDef item in game) { if (item.hasPosInBalance && item.TreePos == anchor.TreePos && !root.parents.Contains(((BalanceBaseObject)item).id)) { root.parents.Add(((BalanceBaseObject)item).id); } } } private static bool TryNextTo(Dictionary<QuestDef, Vector2Int> shape, int shapeWidth, Vector2Int parent, int maxRows, List<QuestDef> game, Dictionary<string, QuestDef> gameById, HashSet<Vector2Int> band, List<QuestDef> placed, int width, out Vector2Int origin) { //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) HashSet<Vector2Int> hashSet = new HashSet<Vector2Int>(game.Select((QuestDef q) => q.TreePos)); hashSet.UnionWith(band); hashSet.UnionWith(placed.Select((QuestDef q) => q.TreePos)); HashSet<Vector2Int> blocked = BlockedCells(game, gameById); blocked.UnionWith(hashSet); for (int num = 1; num <= maxRows && (num <= 1 || !hashSet.Contains(new Vector2Int(((Vector2Int)(ref parent)).x, ((Vector2Int)(ref parent)).y + num - 1))); num++) { int[] array = new int[3] { 0, 1, -1 }; foreach (int num3 in array) { Vector2Int start = new Vector2Int(((Vector2Int)(ref parent)).x + num3, ((Vector2Int)(ref parent)).y + num); if (((Vector2Int)(ref start)).x >= 0 && ((Vector2Int)(ref start)).x + shapeWidth <= width && !shape.Values.Any((Vector2Int cell) => blocked.Contains(start + cell))) { origin = start; return true; } } } origin = default(Vector2Int); return false; } private static Dictionary<QuestDef, Vector2Int> Shape(QuestDef root, Dictionary<QuestDef, List<QuestDef>> kids, out int width) { Dictionary<QuestDef, Vector2Int> shape = new Dictionary<QuestDef, Vector2Int>(); int next = 0; layout(root, 0); width = next; return shape; void layout(QuestDef node, int depth) { //IL_0007: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) shape[node] = Vector2Int.zero; List<QuestDef> value; List<QuestDef> list = (kids.TryGetValue(node, out value) ? value.Where((QuestDef k) => !shape.ContainsKey(k)).ToList() : new List<QuestDef>()); if (list.Count == 0) { shape[node] = new Vector2Int(next++, depth); } else { foreach (QuestDef item in list) { layout(item, depth + 1); } Dictionary<QuestDef, Vector2Int> dictionary = shape; Vector2Int val = shape[list[0]]; dictionary[node] = new Vector2Int(((Vector2Int)(ref val)).x, depth); } } } private static Vector2Int FindSpot(Dictionary<QuestDef, Vector2Int> shape, int shapeWidth, HashSet<Vector2Int> blocked, int width, int startY, int preferX) { //IL_008a: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) List<int> list = (from x in Enumerable.Range(0, Math.Max(1, width - shapeWidth + 1)) orderby Math.Abs(x - preferX), x select x).ToList(); int num = startY; while (true) { foreach (int item in list) { Vector2Int origin = new Vector2Int(item, num); if (!shape.Values.Any((Vector2Int cell) => blocked.Contains(origin + cell))) { return origin; } } num++; } } private static void Place(Dictionary<QuestDef, Vector2Int> shape, Vector2Int origin, HashSet<Vector2Int> taken) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<QuestDef, Vector2Int> item in shape) { SetPosition(item.Key, origin + item.Value); taken.Add(origin + item.Value); } } private static int Order(QuestDef definition) { if (!_registeredAt.TryGetValue(((BalanceBaseObject)definition).id, out var value)) { return int.MaxValue; } return value; } private static HashSet<Vector2Int> BlockedCells(List<QuestDef> quests, Dictionary<string, QuestDef> byId) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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 refe