using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using NodeCanvas.DialogueTrees; using NodeCanvas.Framework; using UnityEngine; using UnityEngine.SceneManagement; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace BankOfAurai; [BepInPlugin("com.spencer4792.bankofaurai", "BankOfAurai", "1.0.3")] public class BankOfAuraiPlugin : BaseUnityPlugin { public const string GUID = "com.spencer4792.bankofaurai"; public const string NAME = "BankOfAurai"; public const string VERSION = "1.0.3"; internal static ManualLogSource Log; internal static BankOfAuraiPlugin Instance; internal static ConfigEntry InterestPerDay; internal static ConfigEntry DepositFeePercent; internal static ConfigEntry PlacementKey; internal static ConfigEntry AudioScanKey; internal static ConfigEntry CopyLookKey; internal static ConfigEntry BarkDialogueKey; internal static ConfigEntry BarkLineText; internal static ConfigEntry BarkNpcKey; internal static ConfigEntry WiretapEnabled; internal static ConfigEntry InteractionRadius; internal void Awake() { Log = ((BaseUnityPlugin)this).Logger; Instance = this; try { Init(); } catch (Exception ex) { Log.LogError((object)("Startup failed: " + ex)); } } private void Init() { //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) InterestPerDay = ((BaseUnityPlugin)this).Config.Bind("Bank", "InterestPercentPerDay", 0.5f, "Interest earned on the deposited balance per in-game day (24h). 0 disables interest."); DepositFeePercent = ((BaseUnityPlugin)this).Config.Bind("Bank", "DepositFeePercent", 0f, "Percentage fee charged on deposits. 0 = free banking."); InteractionRadius = ((BaseUnityPlugin)this).Config.Bind("Bank", "InteractionRadius", 1.6f, "Radius (meters) of the bank interaction spot."); BarkDialogueKey = ((BaseUnityPlugin)this).Config.Bind("Bank", "BarkLineKey", "auto", "Localization key of the banker's voiced bark line. 'auto' = find it by searching dialogue text for BarkLineText (result is logged). Empty = silent banker."); BarkLineText = ((BaseUnityPlugin)this).Config.Bind("Bank", "BarkLineText", "better have the money", "Phrase used to locate the bark line when BarkLineKey is 'auto'."); BarkNpcKey = ((BaseUnityPlugin)this).Config.Bind("Bank", "BarkSpeakerNameKey", "Banker", "Name shown on the bark's vicinity subtitle bubble (a localization key, or plain text)."); WiretapEnabled = ((BaseUnityPlugin)this).Config.Bind("Debug", "AudioWiretap", false, "Log every sound played through the game's audio manager (very chatty)."); AudioScanKey = ((BaseUnityPlugin)this).Config.Bind("Placement", "AudioScanKey", new KeyboardShortcut((KeyCode)288, Array.Empty()), "Debug: scan all dialogue trees loaded in the current scene for statements mentioning money and log their audio clip names. Used to identify the vendor greeting bark for the banker."); CopyLookKey = ((BaseUnityPlugin)this).Config.Bind("Placement", "CopyBankerLookKey", new KeyboardShortcut((KeyCode)289, Array.Empty()), "Stand next to any NPC and press this: their appearance becomes the banker's body at this city's bank spot, remembered across visits."); PlacementKey = ((BaseUnityPlugin)this).Config.Bind("Placement", "PlaceBankSpotKey", new KeyboardShortcut((KeyCode)287, Array.Empty()), "Stand where the bank should be in a city and press this to save/move the bank spot for the current scene. It spawns immediately and on every future visit."); AudioWiretap.Install(); BankLedger.Load(); BankSpots.Load(); BankBody.Load(); SceneManager.sceneLoaded += OnSceneLoaded; Log.LogMessage((object)"BankOfAurai 1.0.3 ready."); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { try { BankSpots.SpawnForScene(((Scene)(ref scene)).name); } catch (Exception ex) { Log.LogError((object)("SpawnForScene: " + ex)); } } private void Update() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) try { KeyboardShortcut value = AudioScanKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { AudioWiretap.SnapshotPlaying(); } KeyboardShortcut value2 = CopyLookKey.Value; if (((KeyboardShortcut)(ref value2)).IsDown()) { BankBody.CopyNearestLook(); } KeyboardShortcut value3 = PlacementKey.Value; if (((KeyboardShortcut)(ref value3)).IsDown()) { Character val = ((!((Object)(object)CharacterManager.Instance != (Object)null)) ? null : CharacterManager.Instance.GetFirstLocalCharacter()); if (!((Object)(object)val == (Object)null)) { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; BankSpots.SetSpot(name, ((Component)val).transform.position); BankSpots.SpawnForScene(name); Log.LogMessage((object)("Bank spot saved for scene '" + name + "' at " + ((Component)val).transform.position)); } } } catch (Exception ex) { Log.LogError((object)("Update: " + ex)); } } } internal static class BankLedger { internal class Account { public long Balance; public float LastInterestTime; } private static readonly Dictionary s_accounts = new Dictionary(); private static string DirPath => Path.Combine(Paths.ConfigPath, "BankOfAurai"); private static string FilePath => Path.Combine(DirPath, "ledger.txt"); public static void Load() { s_accounts.Clear(); try { if (!File.Exists(FilePath)) { return; } string[] array = File.ReadAllLines(FilePath); foreach (string text in array) { string[] array2 = text.Split(new char[1] { '\t' }); if (array2.Length >= 3 && long.TryParse(array2[1], out var result)) { if (!float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { result2 = 0f; } s_accounts[array2[0]] = new Account { Balance = result, LastInterestTime = result2 }; } } BankOfAuraiPlugin.Log.LogInfo((object)("Ledger loaded: " + s_accounts.Count + " account(s).")); } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("Ledger load: " + ex)); } } public static void Save() { try { Directory.CreateDirectory(DirPath); string text = FilePath + ".tmp"; List list = new List(); foreach (KeyValuePair s_account in s_accounts) { list.Add(s_account.Key + "\t" + s_account.Value.Balance + "\t" + s_account.Value.LastInterestTime.ToString(CultureInfo.InvariantCulture)); } File.WriteAllLines(text, list.ToArray()); if (File.Exists(FilePath)) { File.Delete(FilePath); } File.Move(text, FilePath); } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("Ledger save: " + ex)); } } public static Account GetAccount(string uid) { if (!s_accounts.TryGetValue(uid, out var value)) { Account account = new Account(); account.Balance = 0L; account.LastInterestTime = NowGameHours(); value = account; s_accounts[uid] = value; } AccrueInterest(value); return value; } private static void AccrueInterest(Account acc) { float num = NowGameHours(); float value = BankOfAuraiPlugin.InterestPerDay.Value; if (value <= 0f || acc.Balance <= 0) { acc.LastInterestTime = num; return; } float num2 = num - acc.LastInterestTime; if (num2 <= 0f) { acc.LastInterestTime = num; return; } long num3 = (long)Math.Floor((float)acc.Balance * (value / 100f) * (num2 / 24f)); if (num3 > 0) { acc.Balance += num3; acc.LastInterestTime = num; BankOfAuraiPlugin.Log.LogInfo((object)("Interest accrued: +" + num3 + " silver.")); } } private static float NowGameHours() { try { return EnvironmentConditions.GameTimeF; } catch { } return 0f; } } internal static class BankSpots { private static readonly Dictionary s_spots = new Dictionary(); private static GameObject s_current; public static GameObject CurrentSpot => s_current; private static string FilePath => Path.Combine(Path.Combine(Paths.ConfigPath, "BankOfAurai"), "spots.txt"); private static void ApplyDefaults() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) if (!s_spots.ContainsKey("CierzoNewTerrain")) { s_spots["CierzoNewTerrain"] = new Vector3(1426.2f, 5.8f, 1658.8f); } if (!s_spots.ContainsKey("Monsoon")) { s_spots["Monsoon"] = new Vector3(62.5f, -4.6f, 159.5f); } if (!s_spots.ContainsKey("Levant")) { s_spots["Levant"] = new Vector3(-60.8f, 0.1f, 76.8f); } if (!s_spots.ContainsKey("Harmattan")) { s_spots["Harmattan"] = new Vector3(89f, 64.8f, 786.9f); } if (!s_spots.ContainsKey("Berg")) { s_spots["Berg"] = new Vector3(1206.1f, -13.7f, 1383.2f); } if (!s_spots.ContainsKey("NewSirocco")) { s_spots["NewSirocco"] = new Vector3(55.8f, 55.9f, -61.7f); } } public static void Load() { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) s_spots.Clear(); try { if (!File.Exists(FilePath)) { ApplyDefaults(); return; } string[] array = File.ReadAllLines(FilePath); foreach (string text in array) { string[] array2 = text.Split(new char[1] { '\t' }); if (array2.Length >= 4 && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array2[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { s_spots[array2[0]] = new Vector3(result, result2, result3); } } ApplyDefaults(); BankOfAuraiPlugin.Log.LogInfo((object)("Bank spots loaded: " + s_spots.Count + " scene(s).")); } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("Spots load: " + ex)); } } private static void Save() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) try { Directory.CreateDirectory(Path.GetDirectoryName(FilePath)); List list = new List(); foreach (KeyValuePair s_spot in s_spots) { list.Add(s_spot.Key + "\t" + s_spot.Value.x.ToString(CultureInfo.InvariantCulture) + "\t" + s_spot.Value.y.ToString(CultureInfo.InvariantCulture) + "\t" + s_spot.Value.z.ToString(CultureInfo.InvariantCulture)); } File.WriteAllLines(FilePath, list.ToArray()); } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("Spots save: " + ex)); } } public static void SetSpot(string sceneName, Vector3 pos) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) s_spots[sceneName] = pos; Save(); } public static void SpawnForScene(string sceneName) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0050: 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) BankAudio.InvalidateCache(); if ((Object)(object)s_current != (Object)null) { Object.Destroy((Object)(object)s_current); s_current = null; } if (s_spots.TryGetValue(sceneName, out var value)) { GameObject val = new GameObject("BankOfAurai_Spot"); val.SetActive(false); val.transform.position = value; InteractionTriggerBase val2 = val.AddComponent(); val2.DetectionColliderRadius = BankOfAuraiPlugin.InteractionRadius.Value; FieldInfo field = typeof(InteractionTriggerBase).GetField("m_generateColliderIfNone", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(val2, true); } val.AddComponent(); BankDialogue.Attach(val); val.SetActive(true); s_current = val; BankOfAuraiPlugin.Log.LogInfo((object)("Bank spot spawned in '" + sceneName + "' at " + value)); BankBody.SpawnForScene(sceneName, val); } } } internal static class BankDialogue { public const string ACTOR = "Banker"; public static void Attach(GameObject go) { DialogueActor val = go.AddComponent(); val.SetName("Banker"); DialogueTreeController val2 = go.AddComponent(); DialogueTreeExt val3 = ScriptableObject.CreateInstance(); ((Graph)val3).name = "BankOfAuraiDialogue"; val3.CanBeQueued = false; BuildGraph(val3); ((GraphOwner)val2).graph = (Graph)(object)val3; try { FieldInfo field = typeof(GraphOwner).GetField("instances", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { IDictionary dictionary = field.GetValue(val2) as IDictionary; if (dictionary == null) { dictionary = (IDictionary)Activator.CreateInstance(field.FieldType); field.SetValue(val2, dictionary); } dictionary[val3] = val3; } else { BankOfAuraiPlugin.Log.LogWarning((object)"GraphOwner.instances not found; dialogue may not start."); } } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("Instance seed: " + ex)); } DialogueStarter val4 = go.AddComponent(); BankTalkInteraction bankTalkInteraction = go.AddComponent(); bankTalkInteraction.Starter = val4; bankTalkInteraction.Controller = val2; DialogueTreeController dialogueController = val4.DialogueController; InteractionActivator component = go.GetComponent(); if ((Object)(object)component != (Object)null) { component.BasicInteraction = (IInteraction)(object)bankTalkInteraction; } } private static void BuildGraph(DialogueTreeExt tree) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown ((DialogueTree)tree).actorParameters.Add(new ActorParameter("Banker")); StatementNodeExt val = ((Graph)tree).AddNode(); val.statement = new Statement("I'm a representative of the Bank of Aurai. I can help you deposit your silver, or return what's yours."); BankAudio.ApplyGreetingClip(val.statement); val.SetActorName("Banker"); ActionNode val2 = ((Graph)tree).AddNode(); val2.action = (ActionTask)(object)new RefreshVarsTask(); StatementNodeExt val3 = ((Graph)tree).AddNode(); val3.statement = new Statement("Your account holds [BANK_BALANCE] silver. You carry [POUCH] silver."); val3.SetActorName("Banker"); MultipleChoiceNodeExt val4 = ((Graph)tree).AddNode(); AddChoice(val4, "Deposit everything."); AddChoice(val4, "Deposit half of my silver."); AddChoice(val4, "Withdraw everything."); AddChoice(val4, "Withdraw half of my balance."); AddChoice(val4, "I'll be on my way."); ActionNode val5 = MakeOp(tree, BankOpTask.Op.DepositAll); ActionNode val6 = MakeOp(tree, BankOpTask.Op.DepositHalf); ActionNode val7 = MakeOp(tree, BankOpTask.Op.WithdrawAll); ActionNode val8 = MakeOp(tree, BankOpTask.Op.WithdrawHalf); FinishNode val9 = ((Graph)tree).AddNode(); ((Graph)tree).ConnectNodes((Node)(object)val, (Node)(object)val2, -1, -1); ((Graph)tree).ConnectNodes((Node)(object)val2, (Node)(object)val3, -1, -1); ((Graph)tree).ConnectNodes((Node)(object)val3, (Node)(object)val4, -1, -1); ((Graph)tree).ConnectNodes((Node)(object)val4, (Node)(object)val5, 0, -1); ((Graph)tree).ConnectNodes((Node)(object)val4, (Node)(object)val6, 1, -1); ((Graph)tree).ConnectNodes((Node)(object)val4, (Node)(object)val7, 2, -1); ((Graph)tree).ConnectNodes((Node)(object)val4, (Node)(object)val8, 3, -1); ((Graph)tree).ConnectNodes((Node)(object)val4, (Node)(object)val9, 4, -1); ((Graph)tree).ConnectNodes((Node)(object)val5, (Node)(object)val2, -1, -1); ((Graph)tree).ConnectNodes((Node)(object)val6, (Node)(object)val2, -1, -1); ((Graph)tree).ConnectNodes((Node)(object)val7, (Node)(object)val2, -1, -1); ((Graph)tree).ConnectNodes((Node)(object)val8, (Node)(object)val2, -1, -1); } private static void AddChoice(MultipleChoiceNodeExt hub, string text) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown hub.availableChoices.Add(new Choice(new Statement(text))); } private static ActionNode MakeOp(DialogueTreeExt tree, BankOpTask.Op op) { ActionNode val = ((Graph)tree).AddNode(); BankOpTask bankOpTask = new BankOpTask(); bankOpTask.Operation = op; val.action = (ActionTask)(object)bankOpTask; return val; } internal static Character LocalCharacter() { return (!((Object)(object)CharacterManager.Instance != (Object)null)) ? null : CharacterManager.Instance.GetFirstLocalCharacter(); } } public class BankTalkInteraction : InteractionBase { public DialogueStarter Starter; public DialogueTreeController Controller; private bool m_warmed; private static readonly FieldInfo s_lastCharField = typeof(InteractionBase).GetField("m_lastCharacter", BindingFlags.Instance | BindingFlags.NonPublic); protected override string DefaultPressText => "Talk"; protected override void OnActivate() { BankOfAuraiPlugin.Log.LogInfo((object)"Bank talk: OnActivate fired."); ((InteractionBase)this).OnActivate(); try { Character val = null; if (s_lastCharField != null) { object? value = s_lastCharField.GetValue(this); val = (Character)((value is Character) ? value : null); } if ((Object)(object)val == (Object)null) { val = BankDialogue.LocalCharacter(); } if ((Object)(object)val == (Object)null) { BankOfAuraiPlugin.Log.LogWarning((object)"Bank talk: no character found."); return; } if ((Object)(object)Starter == (Object)null) { BankOfAuraiPlugin.Log.LogWarning((object)"Bank talk: no DialogueStarter."); return; } BankOfAuraiPlugin.Log.LogInfo((object)("Bank talk: starting dialogue for " + val.Name)); Starter.StartDialogue(val); if (!m_warmed) { m_warmed = true; BankOfAuraiPlugin.Log.LogInfo((object)"Bank talk: first-use warm restart."); if ((Object)(object)Controller != (Object)null) { Controller.StopDialogue(); } Starter.StartDialogue(val); } else if ((Object)(object)Controller != (Object)null && !((GraphOwner)Controller).isRunning) { BankOfAuraiPlugin.Log.LogInfo((object)"Bank talk: start no-op'd, retrying."); Starter.StartDialogue(val); } BankAudio.PlayGreetingBark(((Component)this).gameObject, val); } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("Bank talk failed: " + ex)); } } } public class RefreshVarsTask : ActionTask { protected override void OnExecute() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) BankOfAuraiPlugin.Log.LogInfo((object)"Bank dialogue running (RefreshVars)."); try { Character val = BankDialogue.LocalCharacter(); if ((Object)(object)val != (Object)null && ((Task)this).blackboard != null) { BankLedger.Account account = BankLedger.GetAccount(UID.op_Implicit(val.UID)); int num = (((Object)(object)val.Inventory != (Object)null) ? val.Inventory.AvailableMoney : 0); SetVar("BANK_BALANCE", account.Balance.ToString()); SetVar("POUCH", num.ToString()); } } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("RefreshVarsTask: " + ex)); } ((ActionTask)this).EndAction(true); } private void SetVar(string name, string value) { if (((Task)this).blackboard.GetVariable(name, (Type)null) == null) { ((Task)this).blackboard.AddVariable(name, (object)value); } else { ((Task)this).blackboard.SetValue(name, (object)value); } } } public class BankOpTask : ActionTask { public enum Op { DepositAll, DepositHalf, WithdrawAll, WithdrawHalf } public Op Operation; protected override void OnExecute() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) try { Character val = BankDialogue.LocalCharacter(); if ((Object)(object)val != (Object)null && (Object)(object)val.Inventory != (Object)null) { BankLedger.Account account = BankLedger.GetAccount(UID.op_Implicit(val.UID)); int availableMoney = val.Inventory.AvailableMoney; switch (Operation) { case Op.DepositAll: Deposit(val, account, availableMoney); break; case Op.DepositHalf: Deposit(val, account, availableMoney / 2); break; case Op.WithdrawAll: Withdraw(val, account, account.Balance); break; case Op.WithdrawHalf: Withdraw(val, account, account.Balance / 2); break; } } } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("BankOpTask: " + ex)); } ((ActionTask)this).EndAction(true); } private static void Deposit(Character character, BankLedger.Account acc, int amount) { if (amount > 0 && character.Inventory.RemoveMoney(amount, false)) { long num = (long)Math.Floor((float)amount * (BankOfAuraiPlugin.DepositFeePercent.Value / 100f)); acc.Balance += amount - num; BankLedger.Save(); } } private static void Withdraw(Character character, BankLedger.Account acc, long amount) { if (amount > 0) { if (amount > acc.Balance) { amount = acc.Balance; } int num = (int)((amount <= int.MaxValue) ? amount : int.MaxValue); character.Inventory.AddMoney(num); acc.Balance -= num; BankLedger.Save(); } } } internal static class BankAudio { private static readonly Type s_clipType = Type.GetType("UnityEngine.AudioClip, UnityEngine.AudioModule"); private static readonly Type s_sourceType = Type.GetType("UnityEngine.AudioSource, UnityEngine.AudioModule"); private static readonly List s_repertoire = new List(); private static string s_cachedPrefix; private static Object s_bundle; private static bool s_warnedBundle; private static readonly List s_clipNames = new List(); private static string s_resolvedKey; public static void InvalidateCache() { s_repertoire.Clear(); s_cachedPrefix = null; } public static void ApplyGreetingClip(Statement statement) { } public static void PlayGreetingBark(GameObject at, Character speaker) { try { string text = BankOfAuraiPlugin.BarkDialogueKey.Value; if (string.IsNullOrEmpty(text)) { return; } if (text == "auto") { text = "dlg_buy_hostileman_1-"; } text = text.ToLowerInvariant(); Type type = Type.GetType("UnityEngine.AssetBundle, UnityEngine.AssetBundleModule"); if (type == null || s_clipType == null || s_sourceType == null) { return; } if (s_bundle == (Object)null) { SNPCManager instance = SNPCManager.Instance; if ((Object)(object)instance == (Object)null) { return; } instance.LoadDialogueSoundsBundle(); FieldInfo[] fields = typeof(SNPCManager).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (type.IsAssignableFrom(fieldInfo.FieldType)) { object? value = fieldInfo.GetValue(instance); Object val = (Object)((value is Object) ? value : null); if (val != (Object)null) { s_bundle = val; break; } } } if (s_bundle == (Object)null) { if (!s_warnedBundle) { s_warnedBundle = true; BankOfAuraiPlugin.Log.LogInfo((object)"Banker voice unavailable (dialogue sound bundle not exposed); see README."); } return; } MethodInfo method = type.GetMethod("GetAllAssetNames"); string[] array = ((!(method != null)) ? null : (method.Invoke(s_bundle, null) as string[])); if (array != null) { s_clipNames.Clear(); string[] array2 = array; foreach (string text2 in array2) { if (text2.ToLowerInvariant().Contains(text)) { s_clipNames.Add(text2); } } BankOfAuraiPlugin.Log.LogInfo((object)("Bark bundle loaded: " + array.Length + " assets, " + s_clipNames.Count + " matching '" + text + "'.")); } } if (s_clipNames.Count == 0) { return; } string text3 = s_clipNames[Random.Range(0, s_clipNames.Count)]; MethodInfo method2 = type.GetMethod("LoadAsset", new Type[2] { typeof(string), typeof(Type) }); Object val2 = (Object)((!(method2 != null)) ? null : /*isinst with value type is only supported in some contexts*/); if (val2 == (Object)null) { BankOfAuraiPlugin.Log.LogWarning((object)("Could not load bark clip: " + text3)); return; } Component val3 = at.GetComponent(s_sourceType); if ((Object)(object)val3 == (Object)null) { val3 = at.AddComponent(s_sourceType); } if (!((Object)(object)val3 == (Object)null)) { PropertyInfo property = s_sourceType.GetProperty("spatialBlend"); if (property != null) { property.SetValue(val3, 0f, null); } PropertyInfo property2 = s_sourceType.GetProperty("volume"); if (property2 != null) { property2.SetValue(val3, 1f, null); } MethodInfo method3 = s_sourceType.GetMethod("PlayOneShot", new Type[1] { s_clipType.MakeByRefType().GetElementType() ?? s_clipType }); if (method3 == null) { method3 = s_sourceType.GetMethod("PlayOneShot", new Type[1] { s_clipType }); } if (method3 != null) { method3.Invoke(val3, new object[1] { val2 }); BankOfAuraiPlugin.Log.LogInfo((object)("Banker bark: " + text3)); } } } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("PlayGreetingBark: " + ex)); } } private static string FindKeyByText(string phrase) { try { if (string.IsNullOrEmpty(phrase)) { return null; } string p = phrase.ToLowerInvariant(); FieldInfo field = typeof(LocalizationManager).GetField("m_dialogueLocalization", BindingFlags.Instance | BindingFlags.NonPublic); IDictionary dictionary = ((!(field != null)) ? null : (field.GetValue(LocalizationManager.Instance) as IDictionary)); if (dictionary == null) { return null; } foreach (DictionaryEntry item in dictionary) { if (ContainsPhrase(item.Value, p, 0)) { BankOfAuraiPlugin.Log.LogMessage((object)string.Concat("Bark line found: key='", item.Key, "'")); return item.Key.ToString(); } } } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("FindKeyByText: " + ex)); } return null; } private static bool ContainsPhrase(object obj, string p, int depth) { if (obj == null || depth > 2) { return false; } Type type = obj.GetType(); FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { object value; try { value = fieldInfo.GetValue(obj); } catch { continue; } if (value is string) { if (((string)value).ToLowerInvariant().Contains(p)) { return true; } } else { if (!(value is IEnumerable) || value is string) { continue; } foreach (object item in (IEnumerable)value) { if (item is string) { if (((string)item).ToLowerInvariant().Contains(p)) { return true; } } else if (ContainsPhrase(item, p, depth + 1)) { return true; } } } } return false; } private static void DumpCandidateKeys(string hint) { try { FieldInfo field = typeof(LocalizationManager).GetField("m_dialogueLocalization", BindingFlags.Instance | BindingFlags.NonPublic); IDictionary dictionary = ((!(field != null)) ? null : (field.GetValue(LocalizationManager.Instance) as IDictionary)); if (dictionary == null) { BankOfAuraiPlugin.Log.LogWarning((object)" (dialogue localization not readable)"); return; } string text = (hint ?? "").ToLowerInvariant(); int num = 0; foreach (object key in dictionary.Keys) { string text2 = key.ToString(); string text3 = text2.ToLowerInvariant(); if (text3.Contains("buy") || text3.Contains("hostile") || (text.Length > 2 && text3.Contains(text.Substring(0, 3)))) { BankOfAuraiPlugin.Log.LogMessage((object)(" candidate: " + text2)); if (++num >= 40) { break; } } } BankOfAuraiPlugin.Log.LogMessage((object)(" (" + dictionary.Count + " dialogue keys total, " + num + " shown)")); } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("DumpCandidateKeys: " + ex)); } } public static void ScanScene() { try { int num = 0; int num2 = 0; int num3 = 0; DialogueTree[] array = Resources.FindObjectsOfTypeAll(); BankOfAuraiPlugin.Log.LogMessage((object)("Audio scan: " + array.Length + " dialogue trees loaded.")); DialogueTree[] array2 = array; foreach (DialogueTree val in array2) { List allNodes = ((Graph)val).allNodes; if (allNodes == null) { continue; } foreach (Node item in allNodes) { StatementNodeExt val2 = (StatementNodeExt)(object)((item is StatementNodeExt) ? item : null); if (val2 == null || val2.statement == null) { continue; } num++; string text = val2.statement.text ?? ""; string text2 = text; try { string loc = LocalizationManager.Instance.GetLoc(text); if (!string.IsNullOrEmpty(loc)) { text2 = loc; } } catch { } Object val3 = null; string text3 = (text + " " + text2).ToLowerInvariant(); if (text3.Contains("money") || text3.Contains("better") || text3.Contains("coin")) { num3++; BankOfAuraiPlugin.Log.LogMessage((object)(" [" + ((Graph)val).name + "] text='" + text2 + "' key='" + text + "' clip=" + ((!(val3 != (Object)null)) ? "none" : val3.name))); } } } BankOfAuraiPlugin.Log.LogMessage((object)("Audio scan done: " + num + " statements, " + num2 + " voiced, " + num3 + " matched.")); } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("Audio scan: " + ex)); } } } internal static class AudioWiretap { public static void Install() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown try { Harmony val = new Harmony("com.spencer4792.bankofaurai.wiretap"); HarmonyMethod val2 = new HarmonyMethod(typeof(AudioWiretap).GetMethod("SoundPostfix", BindingFlags.Static | BindingFlags.NonPublic)); int num = 0; MethodInfo[] methods = typeof(GlobalAudioManager).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!methodInfo.Name.StartsWith("Play")) { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length != 0 && !(parameters[0].Name != "_sound")) { try { val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null); num++; } catch (Exception ex) { BankOfAuraiPlugin.Log.LogWarning((object)("Wiretap skip " + methodInfo.Name + ": " + ex.Message)); } } } BankOfAuraiPlugin.Log.LogInfo((object)("Audio wiretap installed on " + num + " methods.")); } catch (Exception ex2) { BankOfAuraiPlugin.Log.LogError((object)("Wiretap install: " + ex2)); } } private static void SoundPostfix(object _sound, MethodBase __originalMethod) { try { if (BankOfAuraiPlugin.WiretapEnabled.Value) { BankOfAuraiPlugin.Log.LogMessage((object)("[wiretap] " + __originalMethod.Name + ": " + _sound)); } } catch { } } public static void SnapshotPlaying() { try { Type type = Type.GetType("UnityEngine.AudioSource, UnityEngine.AudioModule"); if (type == null) { BankOfAuraiPlugin.Log.LogWarning((object)"Snapshot: AudioSource type not found."); return; } PropertyInfo property = type.GetProperty("isPlaying"); PropertyInfo property2 = type.GetProperty("clip"); Object[] array = Resources.FindObjectsOfTypeAll(type); int num = 0; Object[] array2 = array; foreach (Object val in array2) { bool flag = false; try { flag = (bool)property.GetValue(val, null); } catch { continue; } if (flag) { num++; object? value = property2.GetValue(val, null); Object val2 = (Object)((value is Object) ? value : null); Component val3 = (Component)(object)((val is Component) ? val : null); string text = ((!((Object)(object)val3 != (Object)null)) ? "?" : ((Object)val3.gameObject).name); BankOfAuraiPlugin.Log.LogMessage((object)("[snapshot] playing: clip=" + ((!(val2 != (Object)null)) ? "none" : val2.name) + " on '" + text + "'")); } } BankOfAuraiPlugin.Log.LogMessage((object)("[snapshot] " + num + " sources playing of " + array.Length + ".")); } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("Snapshot: " + ex)); } } } internal static class BankBody { private static readonly Dictionary s_sources = new Dictionary(); private static readonly Dictionary s_yaws = new Dictionary(); private static GameObject s_currentBody; private static string FilePath => Path.Combine(Path.Combine(Paths.ConfigPath, "BankOfAurai"), "bodies.txt"); public static void Load() { s_sources.Clear(); s_yaws.Clear(); s_sources["CierzoNewTerrain"] = "snpc:HumanSNPCMoving (6)"; s_yaws["CierzoNewTerrain"] = 73.34101f; s_sources["Monsoon"] = "snpc:HumanSNPC_Priest"; s_yaws["Monsoon"] = 125.9873f; s_sources["Levant"] = "snpc:HumanSNPC_FestiveNoble"; s_yaws["Levant"] = 26.10077f; s_sources["Berg"] = "snpc:HumanSNPCMoving (1)"; s_yaws["Berg"] = 251.4524f; s_sources["NewSirocco"] = "snpc:UNPC_DLC2_AlchemySkillGiver"; s_yaws["NewSirocco"] = 35.72704f; s_sources["Harmattan"] = "snpc:UNPC_DLC_VictorBerthelot_MilitaryDean"; s_yaws["Harmattan"] = 125.2129f; try { if (!File.Exists(FilePath)) { return; } string[] array = File.ReadAllLines(FilePath); foreach (string text in array) { string[] array2 = text.Split(new char[1] { '\t' }); if (array2.Length >= 2) { s_sources[array2[0]] = array2[1]; } if (array2.Length >= 3 && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { s_yaws[array2[0]] = result; } } BankOfAuraiPlugin.Log.LogInfo((object)("Banker bodies loaded: " + s_sources.Count + " scene(s).")); } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("Bodies load: " + ex)); } } private static void Save() { try { Directory.CreateDirectory(Path.GetDirectoryName(FilePath)); List list = new List(); foreach (KeyValuePair s_source in s_sources) { string text = s_source.Key + "\t" + s_source.Value; if (s_yaws.TryGetValue(s_source.Key, out var value)) { text = text + "\t" + value.ToString(CultureInfo.InvariantCulture); } list.Add(text); } File.WriteAllLines(FilePath, list.ToArray()); } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("Bodies save: " + ex)); } } public static void CopyNearestLook() { //IL_0093: 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_010f: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0231: 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_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) try { Character val = ((!((Object)(object)CharacterManager.Instance != (Object)null)) ? null : CharacterManager.Instance.GetFirstLocalCharacter()); if ((Object)(object)val == (Object)null) { return; } GameObject val2 = null; string text = null; float num = 6f; Character[] array = Object.FindObjectsOfType(); foreach (Character val3 in array) { if ((Object)(object)val3 == (Object)null || (Object)(object)val3 == (Object)(object)val) { continue; } CharacterVisuals visuals = val3.Visuals; if (!((Object)(object)visuals == (Object)null)) { float num2 = Vector3.Distance(((Component)val3).transform.position, ((Component)val).transform.position); if (num2 < num) { num = num2; val2 = ((Component)visuals).gameObject; text = "char:" + val3.Name; } } } SNPC[] array2 = Object.FindObjectsOfType(); foreach (SNPC val4 in array2) { if (!((Object)(object)val4 == (Object)null)) { float num3 = Vector3.Distance(((Component)val4).transform.position, ((Component)val).transform.position); if (num3 < num) { num = num3; val2 = ((Component)val4).gameObject; text = "snpc:" + ((Object)((Component)val4).gameObject).name; } } } GameObject currentSpot = BankSpots.CurrentSpot; if ((Object)(object)val2 == (Object)null) { if ((Object)(object)currentSpot != (Object)null && (Object)(object)s_currentBody != (Object)null && Vector3.Distance(((Component)val).transform.position, currentSpot.transform.position) < 10f) { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; float value = AimBodyAt(currentSpot, ((Component)val).transform.position); s_yaws[name] = value; Save(); BankOfAuraiPlugin.Log.LogMessage((object)("Banker re-aimed (yaw " + value.ToString("F0") + ") and saved.")); } else { BankOfAuraiPlugin.Log.LogMessage((object)"No NPC within 6m to copy."); } return; } Scene activeScene2 = SceneManager.GetActiveScene(); string name2 = ((Scene)(ref activeScene2)).name; s_sources[name2] = text; if ((Object)(object)BankSpots.CurrentSpot != (Object)null) { Vector3 val5 = ((Component)val).transform.position - BankSpots.CurrentSpot.transform.position; val5.y = 0f; if (((Vector3)(ref val5)).sqrMagnitude > 0.01f) { Dictionary dictionary = s_yaws; Quaternion val6 = Quaternion.LookRotation(val5); dictionary[name2] = ((Quaternion)(ref val6)).eulerAngles.y; } } Save(); BankOfAuraiPlugin.Log.LogMessage((object)("Banker for '" + name2 + "' cast: " + text)); GameObject currentSpot2 = BankSpots.CurrentSpot; if ((Object)(object)currentSpot2 != (Object)null) { AttachCloneGO(val2, currentSpot2); } else { BankOfAuraiPlugin.Log.LogMessage((object)"No bank spot in this scene yet (F6 to place one)."); } } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("CopyNearestLook: " + ex)); } } public static void SpawnForScene(string sceneName, GameObject spot) { if ((Object)(object)s_currentBody != (Object)null) { Object.Destroy((Object)(object)s_currentBody); s_currentBody = null; } if (s_sources.TryGetValue(sceneName, out var value)) { ((MonoBehaviour)BankOfAuraiPlugin.Instance).StartCoroutine(WaitAndClone(value, spot)); } } private static IEnumerator WaitAndClone(string key, GameObject spot) { for (int attempt = 0; attempt < 15; attempt++) { yield return (object)new WaitForSeconds(2f); if ((Object)(object)spot == (Object)null) { yield break; } GameObject found = null; if (key.StartsWith("snpc:")) { string text = key.Substring(5); SNPC[] array = Object.FindObjectsOfType(); foreach (SNPC val in array) { if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).name == text) { found = ((Component)val).gameObject; break; } } } else { string text2 = ((!key.StartsWith("char:")) ? key : key.Substring(5)); Character[] array2 = Object.FindObjectsOfType(); foreach (Character val2 in array2) { if ((Object)(object)val2 != (Object)null && val2.Name == text2 && (Object)(object)val2.Visuals != (Object)null) { found = ((Component)val2.Visuals).gameObject; break; } } } if ((Object)(object)found != (Object)null) { AttachCloneGO(found, spot); yield break; } } BankOfAuraiPlugin.Log.LogWarning((object)("Banker body source '" + key + "' never appeared in this scene.")); } private static float AimBodyAt(GameObject spot, Vector3 target) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) Vector3 val = target - spot.transform.position; val.y = 0f; float num; if (((Vector3)(ref val)).sqrMagnitude > 0.01f) { Quaternion val2 = Quaternion.LookRotation(val); num = ((Quaternion)(ref val2)).eulerAngles.y; } else { num = 0f; } float num2 = num; if ((Object)(object)s_currentBody != (Object)null) { s_currentBody.transform.rotation = Quaternion.Euler(0f, num2, 0f); } return num2; } private static void AttachCloneGO(GameObject sourceGO, GameObject spot) { //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)s_currentBody != (Object)null) { Object.Destroy((Object)(object)s_currentBody); s_currentBody = null; } GameObject val = Object.Instantiate(sourceGO); ((Object)val).name = "BankOfAurai_Body"; MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren(true); foreach (MonoBehaviour val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null) { Object.Destroy((Object)(object)val2); } } Collider[] componentsInChildren2 = val.GetComponentsInChildren(true); foreach (Collider val3 in componentsInChildren2) { if ((Object)(object)val3 != (Object)null) { Object.Destroy((Object)(object)val3); } } Rigidbody[] componentsInChildren3 = val.GetComponentsInChildren(true); foreach (Rigidbody val4 in componentsInChildren3) { if ((Object)(object)val4 != (Object)null) { Object.Destroy((Object)(object)val4); } } val.transform.SetParent(spot.transform, false); val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; Dictionary dictionary = s_yaws; Scene activeScene = SceneManager.GetActiveScene(); if (dictionary.TryGetValue(((Scene)(ref activeScene)).name, out var value)) { val.transform.rotation = Quaternion.Euler(0f, value, 0f); } else { Character val5 = ((!((Object)(object)CharacterManager.Instance != (Object)null)) ? null : CharacterManager.Instance.GetFirstLocalCharacter()); if ((Object)(object)val5 != (Object)null) { Vector3 val6 = ((Component)val5).transform.position - spot.transform.position; val6.y = 0f; if (((Vector3)(ref val6)).sqrMagnitude > 0.01f) { val.transform.rotation = Quaternion.LookRotation(val6); } } } try { Type type = Type.GetType("UnityEngine.Animation, UnityEngine.AnimationModule"); if (type != null) { Component componentInChildren = val.GetComponentInChildren(type); if ((Object)(object)componentInChildren != (Object)null) { MethodInfo method = type.GetMethod("Play", new Type[0]); if (method != null) { method.Invoke(componentInChildren, null); } } } } catch { } val.SetActive(true); s_currentBody = val; BankOfAuraiPlugin.Log.LogMessage((object)("Banker body attached from: " + ((Object)sourceGO).name)); } catch (Exception ex) { BankOfAuraiPlugin.Log.LogError((object)("AttachClone: " + ex)); } } }