Decompiled source of Pebble v1.0.8

Pebble.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using MyMods;
using MyMods.Web;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Rendering;

[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("Pebble")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.8.0")]
[assembly: AssemblyInformationalVersion("1.0.8+c13f19c3b078d3d6a56a0c7fbbe49d5fd3c23638")]
[assembly: AssemblyProduct("Pebble")]
[assembly: AssemblyTitle("Pebble")]
[assembly: AssemblyVersion("1.0.8.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 MyMods
{
	public static class HallClient
	{
		private struct Pending
		{
			public string Id;

			public string Title;

			public int Order;

			public Action<Rect> Draw;
		}

		private static readonly List<Pending> s_pending = new List<Pending>();

		private static Type s_type;

		private static bool s_miss;

		private static MethodInfo s_register;

		private static MethodInfo s_open;

		private static MethodInfo s_close;

		private static MethodInfo s_hosting;

		private static MethodInfo s_yield;

		private static MethodInfo s_hold;

		private static PropertyInfo s_ready;

		private static PropertyInfo s_openProp;

		private static Type HallType
		{
			get
			{
				if (s_type != null || s_miss)
				{
					return s_type;
				}
				s_type = Type.GetType("DFGHub.HallBoard, DFGHub");
				s_miss = s_type == null;
				return s_type;
			}
		}

		public static bool Ready
		{
			get
			{
				if (s_ready == null && HallType != null)
				{
					s_ready = HallType.GetProperty("Ready", BindingFlags.Static | BindingFlags.Public);
				}
				if (s_ready == null)
				{
					return false;
				}
				object value = s_ready.GetValue(null, null);
				bool flag = default(bool);
				int num;
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
		}

		public static bool IsOpen
		{
			get
			{
				if (s_openProp == null && HallType != null)
				{
					s_openProp = HallType.GetProperty("IsOpen", BindingFlags.Static | BindingFlags.Public);
				}
				if (s_openProp == null)
				{
					return false;
				}
				object value = s_openProp.GetValue(null, null);
				bool flag = default(bool);
				int num;
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
		}

		public static void Bind(string id, string title, int order, Action<Rect> draw)
		{
			if (string.IsNullOrEmpty(id) || draw == null)
			{
				return;
			}
			for (int num = s_pending.Count - 1; num >= 0; num--)
			{
				if (string.Equals(s_pending[num].Id, id, StringComparison.OrdinalIgnoreCase))
				{
					s_pending.RemoveAt(num);
				}
			}
			s_pending.Add(new Pending
			{
				Id = id,
				Title = title,
				Order = order,
				Draw = draw
			});
			Flush();
		}

		public static void Flush()
		{
			if (s_pending.Count != 0 && Ready)
			{
				for (int i = 0; i < s_pending.Count; i++)
				{
					Pending pending = s_pending[i];
					TryRegister(pending.Id, pending.Title, pending.Order, pending.Draw);
				}
				s_pending.Clear();
			}
		}

		public static bool TryOpen(string tabId)
		{
			if (!Ready)
			{
				return false;
			}
			if (s_open == null)
			{
				s_open = HallType.GetMethod("Open", BindingFlags.Static | BindingFlags.Public);
			}
			if (s_open == null)
			{
				return false;
			}
			try
			{
				object obj = s_open.Invoke(null, new object[1] { tabId });
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch (Exception)
			{
				return false;
			}
		}

		public static void Close()
		{
			if (HallType == null)
			{
				return;
			}
			if (s_close == null)
			{
				s_close = HallType.GetMethod("Close", BindingFlags.Static | BindingFlags.Public);
			}
			try
			{
				s_close?.Invoke(null, null);
			}
			catch (Exception)
			{
			}
		}

		public static bool IsHosting(string id)
		{
			if (HallType == null || string.IsNullOrEmpty(id))
			{
				return false;
			}
			if (s_hosting == null)
			{
				s_hosting = HallType.GetMethod("IsHosting", BindingFlags.Static | BindingFlags.Public);
			}
			if (s_hosting == null)
			{
				return false;
			}
			try
			{
				object obj = s_hosting.Invoke(null, new object[1] { id });
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch (Exception)
			{
				return false;
			}
		}

		public static void SetYield(bool yieldScreen)
		{
			if (HallType == null)
			{
				return;
			}
			if (s_yield == null)
			{
				s_yield = HallType.GetMethod("SetYield", BindingFlags.Static | BindingFlags.Public);
			}
			try
			{
				s_yield?.Invoke(null, new object[1] { yieldScreen });
			}
			catch (Exception)
			{
			}
		}

		public static void HoldEscape()
		{
			if (HallType == null)
			{
				return;
			}
			if (s_hold == null)
			{
				s_hold = HallType.GetMethod("HoldEscape", BindingFlags.Static | BindingFlags.Public);
			}
			try
			{
				s_hold?.Invoke(null, null);
			}
			catch (Exception)
			{
			}
		}

		private static void TryRegister(string id, string title, int order, Action<Rect> draw)
		{
			if (s_register == null)
			{
				s_register = HallType.GetMethod("Register", BindingFlags.Static | BindingFlags.Public);
			}
			s_register?.Invoke(null, new object[4] { id, title, order, draw });
		}
	}
}
namespace MyMods.Web
{
	public static class WebNav
	{
		public sealed class Link
		{
			public readonly string Id;

			public readonly string Label;

			public readonly int Port;

			public string Url => "http://127.0.0.1:" + Port + "/";

			public Link(string id, string label, int port)
			{
				Id = id;
				Label = label;
				Port = port;
			}
		}

		public static readonly Link[] Links = new Link[14]
		{
			new Link("monsters", "Monsters", 8777),
			new Link("searoute", "SeaRoute", 8778),
			new Link("longboat", "Harpoon Longboat", 8779),
			new Link("tradewind", "TradeWind", 8780),
			new Link("safeharbor", "Safe Harbor", 8781),
			new Link("hub", "DFG Hub", 8782),
			new Link("vault", "Viking Vault", 8783),
			new Link("qolvault", "QoL Vault Buttons", 8784),
			new Link("qolinventory", "QoL Inventory Buttons", 8785),
			new Link("cacophony", "Cacophony", 8787),
			new Link("ominous", "Ominous", 8788),
			new Link("pebble", "Pebble", 8789),
			new Link("trolls", "Trolls", 8790),
			new Link("transporter", "Transporter", 8791)
		};

		public const string Style = "nav.top{position:sticky;top:0;z-index:20;font-family:Segoe UI,Arial,sans-serif;background:#0d1219;border-bottom:1px solid #2b3b4c;padding:6px 14px;display:flex;align-items:center;gap:6px;flex-wrap:wrap}\nnav.top .brand{color:#7f8fa0;font-size:11px;text-transform:uppercase;letter-spacing:.6px;margin-right:8px}\nnav.top a{color:#9fc3dd;text-decoration:none;font-size:13px;padding:4px 9px;border-radius:5px}\nnav.top a:hover{background:#1a2530}\nnav.top a.here{background:#3a2b4c;border:1px solid #55406b;color:#efe0ff}\n";

		public static string Html(string currentId)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("<nav class='top'><span class='brand'>MyMods</span>");
			Link[] links = Links;
			foreach (Link link in links)
			{
				stringBuilder.Append("<a class='");
				if (link.Id == currentId)
				{
					stringBuilder.Append("here");
				}
				stringBuilder.Append("' href='").Append(link.Url).Append("'>")
					.Append(link.Label)
					.Append("</a>");
			}
			stringBuilder.Append("</nav>");
			return stringBuilder.ToString();
		}

		public static Link Find(string id)
		{
			Link[] links = Links;
			foreach (Link link in links)
			{
				if (link.Id == id)
				{
					return link;
				}
			}
			return null;
		}
	}
}
namespace Pebble
{
	public class ClipLibrary
	{
		private readonly Dictionary<string, AudioClip> m_clips = new Dictionary<string, AudioClip>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, string> m_errors = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private readonly List<UnityWebRequest> m_mp3Requests = new List<UnityWebRequest>();

		public bool Loading { get; private set; }

		public int Loaded { get; private set; }

		public int Failed { get; private set; }

		public string CurrentFile { get; private set; }

		public string LastError { get; private set; }

		public int Pending { get; private set; }

		public AudioClip Get(string fileName)
		{
			if (string.IsNullOrEmpty(fileName))
			{
				return null;
			}
			if (!m_clips.TryGetValue(fileName, out var value))
			{
				return null;
			}
			return value;
		}

		public AudioClip AddWav(string fileName, string path)
		{
			if (string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(path))
			{
				return null;
			}
			if (m_clips.TryGetValue(fileName, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			AudioClip val;
			try
			{
				val = LoadWav(fileName, path);
			}
			catch (Exception ex)
			{
				LastError = fileName + ": " + ex.Message;
				return null;
			}
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Object.DontDestroyOnLoad((Object)(object)val);
			m_clips[fileName] = val;
			Loaded++;
			return val;
		}

		public bool HasClip(string fileName)
		{
			if (!string.IsNullOrEmpty(fileName))
			{
				return m_clips.ContainsKey(fileName);
			}
			return false;
		}

		public string ErrorFor(string fileName)
		{
			if (!m_errors.TryGetValue(fileName ?? "", out var value))
			{
				return null;
			}
			return value;
		}

		public void Reset()
		{
			foreach (AudioClip value in m_clips.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			m_clips.Clear();
			m_errors.Clear();
			Loaded = 0;
			Failed = 0;
			Pending = 0;
			CurrentFile = null;
			LastError = null;
		}

		public IEnumerator LoadAll(IEnumerable<string> fileNames, string tracksDir, Action onComplete)
		{
			Reset();
			Loading = true;
			List<string> queue = new List<string>();
			foreach (string fileName in fileNames)
			{
				if (!string.IsNullOrWhiteSpace(fileName))
				{
					queue.Add(fileName.Trim());
				}
			}
			Pending = queue.Count;
			try
			{
				for (int i = 0; i < queue.Count; i++)
				{
					string name = (CurrentFile = queue[i]);
					if (m_clips.ContainsKey(name))
					{
						Loaded++;
						continue;
					}
					AudioClip val = null;
					string error = null;
					string path = Path.Combine(tracksDir, name);
					if (!File.Exists(path))
					{
						error = "File not found in tracks folder";
					}
					else
					{
						string text2 = Path.GetExtension(name).ToLowerInvariant();
						if (text2 == ".wav")
						{
							try
							{
								val = LoadWav(name, path);
								if ((Object)(object)val == (Object)null)
								{
									error = "Unsupported WAV format";
								}
							}
							catch (Exception ex)
							{
								error = ex.Message;
							}
						}
						else if (text2 == ".mp3")
						{
							AudioClip decoded = null;
							yield return LoadMp3(name, path, delegate(AudioClip result)
							{
								decoded = result;
							});
							val = decoded;
							if ((Object)(object)val == (Object)null)
							{
								error = "MP3 decode failed";
							}
						}
						else
						{
							error = "Unsupported audio type " + text2;
						}
					}
					if ((Object)(object)val != (Object)null)
					{
						Object.DontDestroyOnLoad((Object)(object)val);
						m_clips[name] = val;
						Loaded++;
					}
					else
					{
						m_errors[name] = error ?? "unknown error";
						LastError = name + ": " + m_errors[name];
						Failed++;
						Logger.LogWarning((object)("[Pebble] Failed to load track '" + name + "': " + m_errors[name]));
					}
				}
			}
			finally
			{
				ClipLibrary clipLibrary = this;
				clipLibrary.Loading = false;
				clipLibrary.CurrentFile = null;
				clipLibrary.Pending = 0;
			}
			onComplete?.Invoke();
		}

		private static AudioClip LoadWav(string name, string path)
		{
			byte[] array = File.ReadAllBytes(path);
			if (array.Length < 44 || !Tag(array, 0, "RIFF") || !Tag(array, 8, "WAVE"))
			{
				return null;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			byte[] array2 = null;
			int num5 = 12;
			while (num5 + 8 <= array.Length)
			{
				string text = Encoding.ASCII.GetString(array, num5, 4);
				int num6 = BitConverter.ToInt32(array, num5 + 4);
				int num7 = num5 + 8;
				if (num6 < 0 || num7 > array.Length)
				{
					break;
				}
				if (num7 + num6 > array.Length)
				{
					num6 = array.Length - num7;
				}
				if (text == "fmt " && num7 + 16 <= array.Length)
				{
					num = BitConverter.ToUInt16(array, num7);
					num2 = BitConverter.ToUInt16(array, num7 + 2);
					num3 = BitConverter.ToInt32(array, num7 + 4);
					num4 = BitConverter.ToUInt16(array, num7 + 14);
					if (num == 65534 && num7 + 26 <= array.Length)
					{
						num = BitConverter.ToUInt16(array, num7 + 24);
					}
				}
				else if (text == "data")
				{
					array2 = new byte[num6];
					Array.Copy(array, num7, array2, 0, num6);
				}
				num5 = num7 + num6 + (num6 & 1);
			}
			if (array2 == null || num2 < 1 || num2 > 8 || num3 < 1)
			{
				return null;
			}
			int num8 = num4 / 8;
			if (num8 < 1)
			{
				return null;
			}
			int num9 = num8 * num2;
			int num10 = array2.Length / num9;
			if (num10 < 1)
			{
				return null;
			}
			float[] array3 = new float[num10 * num2];
			for (int i = 0; i < array3.Length; i++)
			{
				int num11 = i * num8;
				if (num11 + num8 > array2.Length)
				{
					break;
				}
				array3[i] = DecodeSample(array2, num11, num, num4);
			}
			AudioClip obj = AudioClip.Create(name, num10, num2, num3, false);
			obj.SetData(array3, 0);
			return obj;
		}

		private static float DecodeSample(byte[] data, int offset, int format, int bits)
		{
			if (format == 3)
			{
				return BitConverter.ToSingle(data, offset);
			}
			switch (bits)
			{
			case 8:
				return (float)(data[offset] - 128) / 128f;
			case 16:
				return (float)BitConverter.ToInt16(data, offset) / 32768f;
			case 24:
			{
				int num = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16);
				if ((num & 0x800000) != 0)
				{
					num |= -16777216;
				}
				return (float)num / 8388608f;
			}
			case 32:
				return (float)BitConverter.ToInt32(data, offset) / 2.1474836E+09f;
			default:
				throw new NotSupportedException(bits + "-bit WAV");
			}
		}

		private static bool Tag(byte[] bytes, int offset, string tag)
		{
			if (offset + 4 > bytes.Length)
			{
				return false;
			}
			if (bytes[offset] == tag[0] && bytes[offset + 1] == tag[1] && bytes[offset + 2] == tag[2])
			{
				return bytes[offset + 3] == tag[3];
			}
			return false;
		}

		private IEnumerator LoadMp3(string name, string path, Action<AudioClip> done)
		{
			string text = "file:///" + path.Replace('\\', '/').TrimStart('/');
			UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(text, (AudioType)13);
			yield return request.SendWebRequest();
			if ((int)request.result != 1)
			{
				Logger.LogWarning((object)("[Pebble] MP3 '" + name + "' failed: " + request.error));
				request.Dispose();
				yield break;
			}
			AudioClip content = DownloadHandlerAudioClip.GetContent(request);
			if ((Object)(object)content == (Object)null)
			{
				request.Dispose();
				yield break;
			}
			((Object)content).name = name;
			m_mp3Requests.Add(request);
			done(content);
		}

		public void DisposeRequests()
		{
			foreach (UnityWebRequest mp3Request in m_mp3Requests)
			{
				if (mp3Request != null)
				{
					mp3Request.Dispose();
				}
			}
			m_mp3Requests.Clear();
		}
	}
	public static class Page
	{
		private const string ResourceName = "Pebble.Page.html";

		private static string s_html;

		public static string Html
		{
			get
			{
				if (s_html != null)
				{
					return s_html;
				}
				try
				{
					using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Pebble.Page.html"))
					{
						if (stream == null)
						{
							throw new InvalidOperationException("missing embedded resource Pebble.Page.html");
						}
						using StreamReader streamReader = new StreamReader(stream);
						s_html = streamReader.ReadToEnd();
					}
					s_html = s_html.Replace("{{NAV_STYLE}}", "nav.top{position:sticky;top:0;z-index:20;font-family:Segoe UI,Arial,sans-serif;background:#0d1219;border-bottom:1px solid #2b3b4c;padding:6px 14px;display:flex;align-items:center;gap:6px;flex-wrap:wrap}\nnav.top .brand{color:#7f8fa0;font-size:11px;text-transform:uppercase;letter-spacing:.6px;margin-right:8px}\nnav.top a{color:#9fc3dd;text-decoration:none;font-size:13px;padding:4px 9px;border-radius:5px}\nnav.top a:hover{background:#1a2530}\nnav.top a.here{background:#3a2b4c;border:1px solid #55406b;color:#efe0ff}\n").Replace("{{NAV}}", WebNav.Html("pebble"));
				}
				catch (Exception ex)
				{
					ManualLogSource log = PebblePlugin.Log;
					if (log != null)
					{
						log.LogError((object)("Pebble: the editor page could not be read: " + ex.Message));
					}
					s_html = "<!DOCTYPE html><html><body style='background:#14171b;color:#d6d9dd;font:13px Consolas,monospace;padding:20px'>The Pebble editor page could not be loaded. See the BepInEx log.</body></html>";
				}
				return s_html;
			}
		}
	}
	public class CustomTab
	{
		public string Title { get; set; }

		public Action RenderCallback { get; set; }

		public CustomTab(string title, Action renderCallback)
		{
			Title = title;
			RenderCallback = renderCallback;
		}
	}
	public static class PebbleAPI
	{
		private static readonly List<CustomTab> s_customTabs = new List<CustomTab>();

		public static IReadOnlyList<CustomTab> CustomTabs => s_customTabs;

		public static void RegisterTab(string tabTitle, Action renderCallback)
		{
			if (!string.IsNullOrEmpty(tabTitle) && renderCallback != null)
			{
				s_customTabs.Add(new CustomTab(tabTitle, renderCallback));
				PebblePlugin.Log.LogInfo((object)("PebbleAPI: registered custom tab '" + tabTitle + "'"));
			}
		}
	}
	public class PebbleBankData
	{
		public long Balance { get; set; }

		public string LastUpdated { get; set; }
	}
	public static class PebbleBank
	{
		public static bool UseBankForPurchases = true;

		public const string CoinItemName = "Coins";

		private static bool s_rpcRegistered;

		public static long Balance { get; private set; }

		private static string BankFilePath => Path.Combine(Paths.ConfigPath, "pebble_bank.json");

		public static void Initialize()
		{
			Load();
		}

		public static string GetSharedName(string prefabName)
		{
			if ((Object)(object)ObjectDB.instance != (Object)null)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName);
				if ((Object)(object)itemPrefab != (Object)null)
				{
					ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
					if ((Object)(object)component != (Object)null && component.m_itemData != null && component.m_itemData.m_shared != null)
					{
						return component.m_itemData.m_shared.m_name;
					}
				}
			}
			return prefabName;
		}

		public static void RegisterRPCs()
		{
			if (!s_rpcRegistered && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.Register<long>("PebbleBank_SyncBalance", (Action<long, long>)RPC_SyncBalance);
				ZRoutedRpc.instance.Register<long>("PebbleBank_RequestSync", (Action<long, long>)RPC_RequestSync);
				ZRoutedRpc.instance.Register<long>("PebbleBank_ServerDeposit", (Action<long, long>)RPC_ServerDeposit);
				ZRoutedRpc.instance.Register<long>("PebbleBank_ServerWithdraw", (Action<long, long>)RPC_ServerWithdraw);
				ZRoutedRpc.instance.Register<long>("PebbleBank_ReceiveWithdrawal", (Action<long, long>)RPC_ReceiveWithdrawal);
				s_rpcRegistered = true;
				PebblePlugin.Log.LogInfo((object)"PebbleBank: ZRoutedRpc endpoints registered.");
				if (!ZNet.instance.IsServer())
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PebbleBank_RequestSync", new object[1] { 0L });
				}
			}
		}

		public static void Load()
		{
			try
			{
				string bankFilePath = BankFilePath;
				if (!File.Exists(bankFilePath))
				{
					Balance = 0L;
					return;
				}
				PebbleBankData pebbleBankData = JsonConvert.DeserializeObject<PebbleBankData>(File.ReadAllText(bankFilePath));
				if (pebbleBankData != null)
				{
					Balance = Math.Max(0L, pebbleBankData.Balance);
					PebblePlugin.Log.LogInfo((object)$"PebbleBank: loaded server-wide balance: {Balance:N0} Coins.");
				}
			}
			catch (Exception ex)
			{
				PebblePlugin.Log.LogWarning((object)("PebbleBank: failed to load bank file: " + ex.Message));
			}
		}

		public static void Save()
		{
			try
			{
				string bankFilePath = BankFilePath;
				string directoryName = Path.GetDirectoryName(bankFilePath);
				if (!Directory.Exists(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				string contents = JsonConvert.SerializeObject((object)new PebbleBankData
				{
					Balance = Balance,
					LastUpdated = DateTime.UtcNow.ToString("o")
				}, (Formatting)1);
				File.WriteAllText(bankFilePath, contents);
			}
			catch (Exception ex)
			{
				PebblePlugin.Log.LogWarning((object)("PebbleBank: failed to save bank file: " + ex.Message));
			}
		}

		public static bool Deposit(Player player, int amount)
		{
			if ((Object)(object)player == (Object)null || amount <= 0)
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null || inventory.CountItems(GetSharedName("Coins"), -1, true) < amount)
			{
				return false;
			}
			inventory.RemoveItem(GetSharedName("Coins"), amount, -1, true);
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PebbleBank_ServerDeposit", new object[1] { (long)amount });
			}
			else
			{
				Balance += amount;
				Save();
				BroadcastSync();
			}
			((Character)player).Message((MessageType)2, $"<color=orange>✦ Deposited {amount:N0} Coins into Pebble's Bank! ✦</color>", 0, (Sprite)null, false);
			return true;
		}

		public static bool Withdraw(Player player, int amount)
		{
			if ((Object)(object)player == (Object)null || amount <= 0)
			{
				return false;
			}
			if (Balance < amount)
			{
				return false;
			}
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PebbleBank_ServerWithdraw", new object[1] { (long)amount });
			}
			else
			{
				Balance -= amount;
				Save();
				BroadcastSync();
				GiveCoins(player, amount);
			}
			return true;
		}

		public static void Deposit(long amount)
		{
			if (amount > 0)
			{
				Balance += amount;
				Save();
				BroadcastSync();
			}
		}

		public static void Withdraw(long amount)
		{
			if (amount > 0)
			{
				Balance = Math.Max(0L, Balance - amount);
				Save();
				BroadcastSync();
			}
		}

		public static void ServerSetBalance(long newBalance)
		{
			Balance = Math.Max(0L, newBalance);
			Save();
			BroadcastSync();
		}

		public static long GetAvailableFunds(Player player)
		{
			if ((Object)(object)player == (Object)null || ((Humanoid)player).GetInventory() == null)
			{
				return 0L;
			}
			int num = ((Humanoid)player).GetInventory().CountItems(GetSharedName("Coins"), -1, true);
			if (!UseBankForPurchases)
			{
				return num;
			}
			return num + Balance;
		}

		public static bool DeductFunds(Player player, int amount)
		{
			if ((Object)(object)player == (Object)null || ((Humanoid)player).GetInventory() == null || amount <= 0)
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			int num = inventory.CountItems(GetSharedName("Coins"), -1, true);
			if (!UseBankForPurchases)
			{
				if (num < amount)
				{
					return false;
				}
				inventory.RemoveItem(GetSharedName("Coins"), amount, -1, true);
				return true;
			}
			if (num + Balance < amount)
			{
				return false;
			}
			if (num >= amount)
			{
				inventory.RemoveItem(GetSharedName("Coins"), amount, -1, true);
				return true;
			}
			int num2 = amount - num;
			if (num > 0)
			{
				inventory.RemoveItem(GetSharedName("Coins"), num, -1, true);
			}
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PebbleBank_ServerWithdraw", new object[1] { (long)num2 });
			}
			else
			{
				Balance -= num2;
				Save();
				BroadcastSync();
			}
			return true;
		}

		private static void GiveCoins(Player player, int amount)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || ((Humanoid)player).GetInventory() == null)
			{
				return;
			}
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab("Coins") : null);
			if ((Object)(object)val == (Object)null)
			{
				val = PrefabManager.Instance.GetPrefab("Coins");
			}
			if ((Object)(object)val != (Object)null)
			{
				if (!((Humanoid)player).GetInventory().AddItem(val, amount))
				{
					ItemDrop.DropItem(val.GetComponent<ItemDrop>().m_itemData, amount, ((Component)player).transform.position + Vector3.up * 0.5f, Quaternion.identity);
					((Character)player).Message((MessageType)2, $"Inventory full! {amount:N0} Coins dropped at your feet.", 0, (Sprite)null, false);
				}
				else
				{
					((Character)player).Message((MessageType)2, $"<color=orange>✦ Withdrew {amount:N0} Coins from Pebble's Bank! ✦</color>", 0, (Sprite)null, false);
				}
			}
		}

		private static void BroadcastSync()
		{
			if (ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PebbleBank_SyncBalance", new object[1] { Balance });
			}
		}

		private static void RPC_SyncBalance(long sender, long newBalance)
		{
			Balance = Math.Max(0L, newBalance);
		}

		private static void RPC_RequestSync(long sender, long unused)
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "PebbleBank_SyncBalance", new object[1] { Balance });
			}
		}

		private static void RPC_ServerDeposit(long sender, long amount)
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				Balance += Math.Max(0L, amount);
				Save();
				BroadcastSync();
			}
		}

		private static void RPC_ServerWithdraw(long sender, long amount)
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && Balance >= amount)
			{
				Balance -= amount;
				Save();
				BroadcastSync();
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "PebbleBank_ReceiveWithdrawal", new object[1] { amount });
			}
		}

		private static void RPC_ReceiveWithdrawal(long sender, long amount)
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				GiveCoins(Player.m_localPlayer, (int)amount);
			}
		}
	}
	public class PebbleCompanion : MonoBehaviour, Interactable, Hoverable
	{
		private const float GreetDistance = 6f;

		private const float CleanupInterval = 10f;

		private static float s_nextCleanupAttempt;

		private static bool s_cleanupFinished;

		private Transform m_visual;

		private PebbleNpcAnim m_anim;

		private bool m_logged;

		private float m_nextRandomTalkTime;

		private void OnEnable()
		{
			m_nextRandomTalkTime = Time.time + Random.Range(30f, 90f);
			ApplyScale();
			TryStartVisual();
			KeepSaved();
		}

		private void KeepSaved()
		{
			ZNetView component = ((Component)this).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid())
			{
				ZDO zDO = component.GetZDO();
				if (zDO != null && !zDO.Persistent)
				{
					zDO.Persistent = true;
					PebblePlugin.Log.LogInfo((object)"Pebble: marked this NPC to be kept in the world save.");
				}
			}
		}

		private void OnDisable()
		{
			if ((Object)(object)m_anim != (Object)null)
			{
				m_anim.Shutdown();
			}
		}

		public void ApplyScale()
		{
			//IL_002d: 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)
			float num = 1f;
			if (PebbleSettings.NpcScale != null)
			{
				num = Mathf.Clamp(PebbleSettings.NpcScale.Value, 0.3f, 3f);
			}
			((Component)this).transform.localScale = Vector3.one * num;
		}

		public static void RefreshAllScales()
		{
			try
			{
				PebbleCompanion[] array = Object.FindObjectsOfType<PebbleCompanion>();
				for (int i = 0; i < array.Length; i++)
				{
					array[i].ApplyScale();
				}
			}
			catch
			{
			}
		}

		public static void Shutdown()
		{
		}

		private void TryStartVisual()
		{
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)m_visual != (Object)null)
			{
				if ((Object)(object)m_anim != (Object)null)
				{
					m_anim.Play();
				}
				return;
			}
			Transform val = ((Component)this).transform.Find("Pebble_Npc");
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int num = RebindSkin(val);
			m_visual = val;
			Transform val2 = val.Find("Armature");
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = val;
			}
			m_anim = ((Component)val2).GetComponent<PebbleNpcAnim>();
			if ((Object)(object)m_anim == (Object)null)
			{
				m_anim = ((Component)val2).gameObject.AddComponent<PebbleNpcAnim>();
			}
			int num2 = m_anim.Play();
			if (!m_logged)
			{
				m_logged = true;
				float num3 = 0f;
				Renderer componentInChildren = ((Component)val).GetComponentInChildren<Renderer>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					Bounds bounds = componentInChildren.bounds;
					num3 = ((Bounds)(ref bounds)).size.y;
				}
				PebblePlugin.Log.LogInfo((object)$"Pebble: NPC placed, about {num3:0.00} m tall. Driving '{PebbleGlb.IdleName}' on {num2} bones ({num} rebound). '{PebbleGlb.TalkName}' blends in when you walk up.");
			}
		}

		private static int RebindSkin(Transform visual)
		{
			//IL_010e: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			SkinnedMeshRenderer componentInChildren = ((Component)visual).GetComponentInChildren<SkinnedMeshRenderer>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				return 0;
			}
			Dictionary<string, Transform> dictionary = new Dictionary<string, Transform>();
			Transform[] componentsInChildren = ((Component)visual).GetComponentsInChildren<Transform>(true);
			foreach (Transform val in componentsInChildren)
			{
				if (!dictionary.ContainsKey(((Object)val).name))
				{
					dictionary[((Object)val).name] = val;
				}
			}
			Transform[] bones = componentInChildren.bones;
			int num = 0;
			if (bones != null)
			{
				for (int j = 0; j < bones.Length; j++)
				{
					if (!((Object)(object)bones[j] == (Object)null) && dictionary.TryGetValue(((Object)bones[j]).name, out var value))
					{
						if ((Object)(object)bones[j] != (Object)(object)value)
						{
							num++;
						}
						bones[j] = value;
					}
				}
				Mesh sharedMesh = componentInChildren.sharedMesh;
				componentInChildren.sharedMesh = null;
				componentInChildren.bones = bones;
				componentInChildren.sharedMesh = sharedMesh;
			}
			if ((Object)(object)componentInChildren.rootBone != (Object)null && dictionary.TryGetValue(((Object)componentInChildren.rootBone).name, out var value2))
			{
				componentInChildren.rootBone = value2;
			}
			componentInChildren.updateWhenOffscreen = true;
			((Renderer)componentInChildren).localBounds = new Bounds(new Vector3(0f, 0.75f, 0f), new Vector3(1.4f, 1.6f, 1.4f));
			return num;
		}

		private void Update()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)m_visual == (Object)null)
			{
				TryStartVisual();
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			bool flag = false;
			if ((Object)(object)localPlayer != (Object)null)
			{
				Vector3 val = ((Component)localPlayer).transform.position - m_visual.position;
				val.y = 0f;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				flag = sqrMagnitude < 36f && sqrMagnitude > 0.05f;
				Quaternion val2 = ((Component)this).transform.rotation;
				if (flag)
				{
					val2 = Quaternion.LookRotation(((Vector3)(ref val)).normalized);
					if (Time.time > m_nextRandomTalkTime)
					{
						m_nextRandomTalkTime = Time.time + Random.Range(45f, 120f);
						if (PebbleDialog.Data.Random.Count > 0)
						{
							PebbleDialog.Speak(((Component)this).gameObject, PebbleDialog.Data.Random);
						}
					}
				}
				m_visual.rotation = Quaternion.Slerp(m_visual.rotation, val2, Time.deltaTime * (flag ? 4f : 2f));
			}
			bool talking = flag || ((Object)(object)PebbleGUI.Instance != (Object)null && (Object)(object)PebbleGUI.Instance.ActiveCompanion == (Object)(object)this);
			if ((Object)(object)m_anim != (Object)null)
			{
				m_anim.SetTalking(talking);
				m_anim.Tick();
			}
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			if (hold)
			{
				return false;
			}
			if ((Object)(object)user == (Object)(object)Player.m_localPlayer)
			{
				PebbleGUI.Instance?.Open(this);
				return true;
			}
			return false;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		public string GetHoverText()
		{
			return "[<color=yellow><b>$KEY_Use</b></color>] Talk";
		}

		public string GetHoverName()
		{
			return "Pebble";
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public static IEnumerator RunStrayGolemCleanupRoutine()
		{
			if (s_cleanupFinished || !PebbleSettings.CleanupStrayGolems.Value)
			{
				yield break;
			}
			while ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)ZNetScene.instance == (Object)null || ZDOMan.instance == null)
			{
				yield return (object)new WaitForSeconds(10f);
			}
			PebbleCompanion[] companions;
			while (true)
			{
				companions = Object.FindObjectsOfType<PebbleCompanion>();
				if (companions != null && companions.Length != 0)
				{
					break;
				}
				yield return (object)new WaitForSeconds(10f);
			}
			List<ZDO> found = new List<ZDO>();
			int index = 0;
			while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative("StoneGolem", found, ref index))
			{
				yield return null;
			}
			float num = Mathf.Max(0.5f, PebbleSettings.CleanupRadius.Value);
			float num2 = num * num;
			int num3 = 0;
			foreach (ZDO item in found)
			{
				if (item == null || !item.IsOwner())
				{
					continue;
				}
				Vector3 position = item.GetPosition();
				PebbleCompanion[] array = companions;
				foreach (PebbleCompanion pebbleCompanion in array)
				{
					if (!((Object)(object)pebbleCompanion == (Object)null))
					{
						Vector3 val = ((Component)pebbleCompanion).transform.position - position;
						val.y = 0f;
						if (!(((Vector3)(ref val)).sqrMagnitude > num2))
						{
							ZDOMan.instance.DestroyZDO(item);
							num3++;
							PebblePlugin.Log.LogWarning((object)$"Pebble: removed a stray StoneGolem at {position} ({((Vector3)(ref val)).magnitude:0.0} m from Pebble) that an older build had saved into this world.");
							break;
						}
					}
				}
			}
			s_cleanupFinished = true;
			if (num3 > 0)
			{
				PebblePlugin.Log.LogWarning((object)$"Pebble: removed {num3} stray StoneGolem(s). Any left elsewhere are next to a Pebble that is not currently loaded, and will be cleaned when you load that area.");
			}
		}
	}
	public class PebbleNpcAnim : MonoBehaviour
	{
		private struct Binding
		{
			public Transform Bone;

			public Pose.Track Idle;

			public Pose.Track Talk;
		}

		private const float BlendSeconds = 0.25f;

		private Binding[] m_bindings;

		private float m_idleLength;

		private float m_talkLength;

		private float m_idleTime;

		private float m_talkTime;

		private float m_weight;

		private bool m_talking;

		private bool m_ready;

		public int Play()
		{
			if (m_ready && m_bindings != null)
			{
				return m_bindings.Length;
			}
			Pose idlePose = PebbleGlb.IdlePose;
			Pose talkPose = PebbleGlb.TalkPose;
			if ((idlePose == null || idlePose.Tracks == null || idlePose.Tracks.Length == 0) && (talkPose == null || talkPose.Tracks == null || talkPose.Tracks.Length == 0))
			{
				PebblePlugin.Log.LogWarning((object)"Pebble: no bone tracks to play.");
				return 0;
			}
			Dictionary<string, Binding> dictionary = new Dictionary<string, Binding>();
			AddTracks(dictionary, idlePose, idle: true);
			AddTracks(dictionary, talkPose, idle: false);
			List<Binding> list = new List<Binding>(dictionary.Count);
			int num = 0;
			foreach (KeyValuePair<string, Binding> item in dictionary)
			{
				Binding value = item.Value;
				value.Bone = ((Component)this).transform.Find(item.Key);
				if ((Object)(object)value.Bone == (Object)null)
				{
					num++;
				}
				else
				{
					list.Add(value);
				}
			}
			m_bindings = list.ToArray();
			m_idleLength = idlePose?.Length ?? 0f;
			m_talkLength = talkPose?.Length ?? 0f;
			m_ready = m_bindings.Length != 0;
			if (num > 0)
			{
				PebblePlugin.Log.LogWarning((object)$"Pebble: {num} animation bones were not on this armature.");
			}
			PebblePlugin.Log.LogInfo((object)$"Pebble: playing '{((idlePose != null) ? idlePose.Name : PebbleGlb.IdleName)}' ({m_idleLength:0.00}s) by moving {m_bindings.Length} bones.");
			return m_bindings.Length;
		}

		public void SetTalking(bool talking)
		{
			m_talking = talking;
		}

		public void Tick()
		{
			if (m_ready && m_bindings != null)
			{
				float deltaTime = Time.deltaTime;
				m_idleTime = Advance(m_idleTime, deltaTime, m_idleLength);
				m_talkTime = Advance(m_talkTime, deltaTime, m_talkLength);
				float num = (m_talking ? 1f : 0f);
				m_weight = Mathf.MoveTowards(m_weight, num, deltaTime / 0.25f);
				for (int i = 0; i < m_bindings.Length; i++)
				{
					Apply(m_bindings[i], m_idleTime, m_talkTime, m_weight);
				}
			}
		}

		public void Shutdown()
		{
			m_ready = false;
		}

		private static void AddTracks(Dictionary<string, Binding> byPath, Pose pose, bool idle)
		{
			if (pose == null || pose.Tracks == null)
			{
				return;
			}
			for (int i = 0; i < pose.Tracks.Length; i++)
			{
				Pose.Track track = pose.Tracks[i];
				if (track != null && !string.IsNullOrEmpty(track.Path))
				{
					if (!byPath.TryGetValue(track.Path, out var value))
					{
						value = default(Binding);
					}
					if (idle)
					{
						value.Idle = track;
					}
					else
					{
						value.Talk = track;
					}
					byPath[track.Path] = value;
				}
			}
		}

		private static void Apply(Binding binding, float idleTime, float talkTime, float weight)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			Transform bone = binding.Bone;
			if ((Object)(object)bone == (Object)null)
			{
				return;
			}
			bool flag = weight > 0.001f;
			Quaternion value3;
			if (binding.Idle != null && binding.Idle.SampleRotation(idleTime, out var value))
			{
				if (flag && binding.Talk != null && binding.Talk.SampleRotation(talkTime, out var value2))
				{
					bone.localRotation = Quaternion.Slerp(value, value2, weight);
				}
				else
				{
					bone.localRotation = value;
				}
			}
			else if (flag && binding.Talk != null && binding.Talk.SampleRotation(talkTime, out value3))
			{
				bone.localRotation = value3;
			}
			Vector3 value6;
			if (binding.Idle != null && binding.Idle.SamplePosition(idleTime, out var value4))
			{
				if (flag && binding.Talk != null && binding.Talk.SamplePosition(talkTime, out var value5))
				{
					bone.localPosition = Vector3.Lerp(value4, value5, weight);
				}
				else
				{
					bone.localPosition = value4;
				}
			}
			else if (flag && binding.Talk != null && binding.Talk.SamplePosition(talkTime, out value6))
			{
				bone.localPosition = value6;
			}
			Vector3 value9;
			if (binding.Idle != null && binding.Idle.SampleScale(idleTime, out var value7))
			{
				if (flag && binding.Talk != null && binding.Talk.SampleScale(talkTime, out var value8))
				{
					bone.localScale = Vector3.Lerp(value7, value8, weight);
				}
				else
				{
					bone.localScale = value7;
				}
			}
			else if (flag && binding.Talk != null && binding.Talk.SampleScale(talkTime, out value9))
			{
				bone.localScale = value9;
			}
		}

		private static float Advance(float time, float dt, float length)
		{
			if (length <= 0.0001f)
			{
				return 0f;
			}
			time += dt;
			if (time >= length)
			{
				time %= length;
			}
			return time;
		}
	}
	public class DialogLine
	{
		public string Text = "";

		public string AudioFile = "";
	}
	public class PebbleDialogData
	{
		public List<DialogLine> Broke = new List<DialogLine>();

		public List<DialogLine> Browsing = new List<DialogLine>();

		public List<DialogLine> Greeting = new List<DialogLine>();

		public List<DialogLine> Leaving = new List<DialogLine>();

		public List<DialogLine> Random = new List<DialogLine>();

		public List<DialogLine> Purchase = new List<DialogLine>();
	}
	public static class PebbleDialog
	{
		public static PebbleDialogData Data = new PebbleDialogData();

		public static ClipLibrary Clips = new ClipLibrary();

		private static string s_pluginDir;

		public static string PluginDir
		{
			get
			{
				if (!string.IsNullOrEmpty(s_pluginDir))
				{
					return s_pluginDir;
				}
				string text = Path.GetDirectoryName(typeof(PebbleDialog).Assembly.Location) ?? "";
				string text2 = Path.Combine(Paths.PluginPath ?? "", "Pebble");
				s_pluginDir = ((!string.IsNullOrEmpty(text) && Directory.Exists(text)) ? text : text2);
				return s_pluginDir;
			}
		}

		private static string ConfigPath => Path.Combine(PluginDir, "pebble_dialog.json");

		public static string AudioDir => Path.Combine(PluginDir, "audio");

		public static void Initialize()
		{
			if (!Directory.Exists(AudioDir))
			{
				Directory.CreateDirectory(AudioDir);
			}
			Load();
			EnsureDefaults();
			Save();
			ReloadAudio();
		}

		public static void Load()
		{
			try
			{
				if (File.Exists(ConfigPath))
				{
					Data = JsonConvert.DeserializeObject<PebbleDialogData>(File.ReadAllText(ConfigPath)) ?? new PebbleDialogData();
				}
			}
			catch (Exception ex)
			{
				PebblePlugin.Log.LogError((object)("Pebble: Failed to load dialog config: " + ex));
				Data = new PebbleDialogData();
			}
		}

		public static void Save()
		{
			try
			{
				string directoryName = Path.GetDirectoryName(ConfigPath);
				if (!Directory.Exists(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				string contents = JsonConvert.SerializeObject((object)Data, (Formatting)1);
				File.WriteAllText(ConfigPath, contents);
			}
			catch (Exception ex)
			{
				PebblePlugin.Log.LogError((object)("Pebble: Failed to save dialog config: " + ex));
			}
		}

		public static void ReloadAudio()
		{
			HashSet<string> hashSet = new HashSet<string>();
			hashSet.UnionWith(Data.Broke.Select((DialogLine x) => x.AudioFile));
			hashSet.UnionWith(Data.Browsing.Select((DialogLine x) => x.AudioFile));
			hashSet.UnionWith(Data.Greeting.Select((DialogLine x) => x.AudioFile));
			hashSet.UnionWith(Data.Leaving.Select((DialogLine x) => x.AudioFile));
			hashSet.UnionWith(Data.Random.Select((DialogLine x) => x.AudioFile));
			hashSet.UnionWith(Data.Purchase.Select((DialogLine x) => x.AudioFile));
			hashSet.RemoveWhere(string.IsNullOrWhiteSpace);
			if (hashSet.Count == 0)
			{
				PebblePlugin.Log.LogWarning((object)("Pebble: no audio files referenced by the dialog config at '" + ConfigPath + "'. Expected 'audio' folder at '" + AudioDir + "'."));
			}
			else if ((Object)(object)PebblePlugin.Instance != (Object)null)
			{
				((MonoBehaviour)PebblePlugin.Instance).StartCoroutine(Clips.LoadAll(hashSet, AudioDir, delegate
				{
					PebblePlugin.Log.LogInfo((object)$"Pebble: Loaded {Clips.Loaded} audio clips, {Clips.Failed} failed.");
				}));
			}
		}

		private static void AddMissing(List<DialogLine> list, params string[] texts)
		{
			foreach (string t in texts)
			{
				if (!list.Any((DialogLine l) => l.Text == t))
				{
					list.Add(new DialogLine
					{
						Text = t
					});
				}
			}
		}

		private static void EnsureDefaults()
		{
			AddMissing(Data.Broke, "I trade in gold and blood, wanderer. Not pockets full of resin and wood chips.", "Come back when your purse clinks louder than your knees.", "By Brokkr's beard, even the greydwarfs carry more coin than you.", "I sell dwarven steel, not charity. Go punch a beech tree.", "What, did a neck rob you on the way over?", "My prices are set in stone. Your budget is clearly made of mud.", "I've seen greylings with better financial planning.", "Is that a purse, or just a very sad, empty leather bag?", "I accept gold. I do not accept IOUs written on birch bark.");
			AddMissing(Data.Browsing, "Window shopping? In Valheim? Watch out, or a mosquito will give you a free piercing while you browse.", "Look all you want, but staring never forged an axe.", "Careful leaning on that counter—you don't look like you can afford to replace it.", "The runes say patience is a virtue. My ledger says otherwise.", "You’re breathing on the merchandise. That’ll be two copper coins.", "Are you waiting for the prices to drop, or for Yggdrasil to wither? Both will take an eternity.", "Go on, keep looking. I'll just stand here and slowly judge your armor.", "Fascinating, isn't it? Try actually buying it next time.");
			AddMissing(Data.Greeting, "Ah, another dead warrior looking for shiny things to die in.", "Smells like swamp water and desperation. What do you need?", "Step into the light. If you're a draugr, at least tell me you brought your own coin.", "Make it quick. The forge is hot, and my temper is shorter than my stride.", "Welcome. Wipe your boots. No, seriously, they're tracking ash everywhere.", "You survived the wolves to get here? Good. More coin for me.", "Ah, my favorite walking target practice. Welcome back.");
			AddMissing(Data.Leaving, "Try not to lose that to a falling log on your way home.", "No refunds when you inevitably feed it to a sea serpent.", "A pleasure doing business. Tell the Valkyries where you bought your gear when they drag you back.", "May Odin bless your path... because you'll need it wearing that.", "Take care of that. It's probably worth more than your life.", "Remember, if you die, I call dibs on the scraps!");
			Data.Random.RemoveAll((DialogLine x) => x.Text == "Just because I'm made of stone doesn't mean I can't feel the drafts in here.");
			Data.Random.RemoveAll((DialogLine x) => x.Text == "If another neck wanders in here, I'm throwing a rock at it.");
			AddMissing(Data.Random, "These wooden longhouses are an insult to proper dwarven stonemasonry.", "Do you ever think about how much time you spend chopping wood? It's excessive.", "*Grumbles softly in dwarven*", "If another neck wanders in here, I'm throwing an anvil at it.", "Yes, I see you standing there. No, I'm not going to dance for you.", "I miss the mountains. The air was thinner, and there were fewer Vikings.", "Don't mind me, just polishing my wares while you loiter.");
			AddMissing(Data.Purchase, "A fine choice. I made that one myself.", "Ah, you actually have some coin. Good.", "Excellent doing business with you.", "Don't break it on the first skeleton you see.", "Gold for iron. A fair trade.", "Enjoy it. And no refunds.");
		}

		public static void Speak(GameObject source, List<DialogLine> lines)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)source == (Object)null || lines == null || lines.Count == 0)
			{
				return;
			}
			DialogLine dialogLine = lines[Random.Range(0, lines.Count)];
			if (string.IsNullOrWhiteSpace(dialogLine.Text) && string.IsNullOrWhiteSpace(dialogLine.AudioFile))
			{
				return;
			}
			if (!string.IsNullOrWhiteSpace(dialogLine.Text))
			{
				float num = 1.2f;
				Renderer componentInChildren = source.GetComponentInChildren<Renderer>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					Bounds bounds = componentInChildren.bounds;
					num = Mathf.Max(0.8f, ((Bounds)(ref bounds)).max.y - source.transform.position.y + 0.2f);
				}
				Chat.instance.SetNpcText(source, Vector3.up * num, 20f, 5f, "Pebble", dialogLine.Text, false);
			}
			if (string.IsNullOrWhiteSpace(dialogLine.AudioFile))
			{
				return;
			}
			AudioClip val = Clips.Get(dialogLine.AudioFile);
			if ((Object)(object)val != (Object)null)
			{
				AudioSource val2 = source.GetComponent<AudioSource>();
				if ((Object)(object)val2 == (Object)null)
				{
					val2 = source.AddComponent<AudioSource>();
					val2.spatialBlend = 1f;
					val2.minDistance = 2f;
					val2.maxDistance = 15f;
					val2.rolloffMode = (AudioRolloffMode)1;
					val2.outputAudioMixerGroup = AudioMan.instance?.m_ambientMixer;
				}
				val2.clip = val;
				val2.Play();
			}
		}
	}
	public static class PebbleGlb
	{
		private struct Built
		{
			public AnimationClip Clip;

			public Pose Pose;
		}

		private sealed class Node
		{
			public string Name;

			public int[] Children;

			public Vector3 Translation = Vector3.zero;

			public Quaternion Rotation = Quaternion.identity;

			public Vector3 Scale = Vector3.one;

			public int Mesh = -1;
		}

		private sealed class Doc
		{
			private struct Accessor
			{
				public int Count;

				public int Components;

				public int ComponentSize;

				public int Offset;

				public int Stride;
			}

			public readonly JObject Root;

			public readonly List<Node> Nodes = new List<Node>();

			public readonly int SceneRoot;

			public readonly int[] SkinJoints;

			private readonly Bin m_bin;

			private readonly JArray m_accessors;

			private readonly JArray m_views;

			private readonly JArray m_images;

			public int InverseBindAccessor { get; }

			public Doc(JObject root, Bin bin)
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0035: Expected O, but got Unknown
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				//IL_004b: Expected O, but got Unknown
				//IL_0057: Unknown result type (might be due to invalid IL or missing references)
				//IL_0061: Expected O, but got Unknown
				//IL_006c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0072: Expected O, but got Unknown
				//IL_0083: Unknown result type (might be due to invalid IL or missing references)
				//IL_008d: Expected O, but got Unknown
				//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ed: Expected O, but got Unknown
				//IL_010a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0110: Expected O, but got Unknown
				Root = root;
				m_bin = bin;
				m_accessors = (JArray)root["accessors"];
				m_views = (JArray)root["bufferViews"];
				m_images = (JArray)root["images"];
				JArray val = (JArray)root["nodes"];
				for (int i = 0; i < ((JContainer)val).Count; i++)
				{
					Nodes.Add(ReadNode((JObject)val[i]));
				}
				SceneRoot = (int)root["scenes"][(object)0][(object)"nodes"][(object)0];
				SkinJoints = null;
				JArray val2 = (JArray)root["skins"];
				if (val2 != null && ((JContainer)val2).Count > 0)
				{
					JArray val3 = (JArray)val2[0][(object)"joints"];
					SkinJoints = new int[((JContainer)val3).Count];
					for (int j = 0; j < ((JContainer)val3).Count; j++)
					{
						SkinJoints[j] = (int)val3[j];
					}
					InverseBindAccessor = (int)val2[0][(object)"inverseBindMatrices"];
				}
			}

			public Mesh ReadMesh(int boneCount)
			{
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_0036: Expected O, but got Unknown
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Expected O, but got Unknown
				//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
				//IL_0120: Unknown result type (might be due to invalid IL or missing references)
				//IL_0125: 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_013f: Expected O, but got Unknown
				JObject val = (JObject)Root["meshes"][(object)0][(object)"primitives"][(object)0];
				JObject val2 = (JObject)val["attributes"];
				Vector3[] array = ReadVec3((int)val2["POSITION"], flipZ: true);
				Vector3[] normals = ReadVec3((int)val2["NORMAL"], flipZ: true);
				Vector2[] uv = ReadVec2((int)val2["TEXCOORD_0"]);
				int[] array2 = ReadIndices((int)val["indices"]);
				BoneWeight[] boneWeights = ReadWeights((int)val2["JOINTS_0"], (int)val2["WEIGHTS_0"], boneCount);
				Matrix4x4[] array3 = (Matrix4x4[])(object)new Matrix4x4[boneCount];
				for (int i = 0; i < array3.Length; i++)
				{
					array3[i] = Matrix4x4.identity;
				}
				Color32[] array4 = (Color32[])(object)new Color32[array.Length];
				for (int j = 0; j < array4.Length; j++)
				{
					array4[j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
				}
				Mesh val3 = new Mesh();
				((Object)val3).name = "Pebble_Mesh";
				if (array.Length > 65535)
				{
					val3.indexFormat = (IndexFormat)1;
				}
				val3.vertices = array;
				val3.normals = normals;
				val3.uv = uv;
				val3.colors32 = array4;
				val3.boneWeights = boneWeights;
				val3.bindposes = array3;
				int[] array5 = new int[array2.Length * 2];
				Array.Copy(array2, array5, array2.Length);
				for (int k = 0; k < array2.Length; k += 3)
				{
					array5[array2.Length + k] = array2[k];
					array5[array2.Length + k + 1] = array2[k + 2];
					array5[array2.Length + k + 2] = array2[k + 1];
				}
				val3.triangles = array5;
				val3.RecalculateTangents();
				val3.RecalculateBounds();
				return val3;
			}

			public float[] ReadFloats(int accessor)
			{
				Accessor accessor2 = GetAccessor(accessor);
				int num = accessor2.Count * accessor2.Components;
				float[] array = new float[num];
				int stride = accessor2.Stride;
				int offset = accessor2.Offset;
				for (int i = 0; i < num; i++)
				{
					int num2 = i / accessor2.Components;
					int num3 = i % accessor2.Components;
					int offset2 = offset + num2 * stride + num3 * 4;
					array[i] = m_bin.Float(offset2);
				}
				return array;
			}

			public Texture2D ReadImage(int imageIndex, bool linear)
			{
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Expected O, but got Unknown
				//IL_005a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0060: Expected O, but got Unknown
				//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00af: Expected O, but got Unknown
				if (m_images == null || imageIndex < 0 || imageIndex >= ((JContainer)m_images).Count)
				{
					return null;
				}
				JObject val = (JObject)m_images[imageIndex];
				if (val["bufferView"] == null)
				{
					return null;
				}
				int num = (int)val["bufferView"];
				JObject val2 = (JObject)m_views[num];
				int offset = ((val2["byteOffset"] != null) ? ((int)val2["byteOffset"]) : 0);
				int length = (int)val2["byteLength"];
				byte[] array = m_bin.Slice(offset, length);
				Texture2D val3 = new Texture2D(2, 2, (TextureFormat)4, true, linear);
				if (!ImageConversion.LoadImage(val3, array))
				{
					PebblePlugin.Log.LogWarning((object)("Pebble: failed to decode image " + (object)val["name"]));
					Object.Destroy((Object)(object)val3);
					return null;
				}
				((Object)val3).name = ((string)val["name"]) ?? "pebble_tex";
				((Texture)val3).wrapMode = (TextureWrapMode)1;
				((Texture)val3).anisoLevel = 4;
				return val3;
			}

			private Vector3[] ReadVec3(int accessor, bool flipZ)
			{
				//IL_0066: Unknown result type (might be due to invalid IL or missing references)
				//IL_006b: Unknown result type (might be due to invalid IL or missing references)
				Accessor accessor2 = GetAccessor(accessor);
				Vector3[] array = (Vector3[])(object)new Vector3[accessor2.Count];
				for (int i = 0; i < accessor2.Count; i++)
				{
					int num = accessor2.Offset + i * accessor2.Stride;
					float num2 = m_bin.Float(num);
					float num3 = m_bin.Float(num + 4);
					float num4 = m_bin.Float(num + 8);
					if (flipZ)
					{
						num4 = 0f - num4;
					}
					array[i] = new Vector3(num2, num3, num4);
				}
				return array;
			}

			private Vector2[] ReadVec2(int accessor)
			{
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				//IL_0057: Unknown result type (might be due to invalid IL or missing references)
				Accessor accessor2 = GetAccessor(accessor);
				Vector2[] array = (Vector2[])(object)new Vector2[accessor2.Count];
				for (int i = 0; i < accessor2.Count; i++)
				{
					int num = accessor2.Offset + i * accessor2.Stride;
					float num2 = m_bin.Float(num);
					float num3 = m_bin.Float(num + 4);
					array[i] = new Vector2(num2, 1f - num3);
				}
				return array;
			}

			private int[] ReadIndices(int accessor)
			{
				Accessor accessor2 = GetAccessor(accessor);
				int[] array = new int[accessor2.Count];
				int componentSize = accessor2.ComponentSize;
				for (int i = 0; i < accessor2.Count; i++)
				{
					int offset = accessor2.Offset + i * componentSize;
					array[i] = componentSize switch
					{
						2 => m_bin.UInt16(offset), 
						4 => (int)m_bin.UInt32(offset), 
						_ => m_bin.Byte(offset), 
					};
				}
				return array;
			}

			private BoneWeight[] ReadWeights(int jointsAccessor, int weightsAccessor, int boneCount)
			{
				//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
				//IL_0149: Unknown result type (might be due to invalid IL or missing references)
				//IL_014b: Unknown result type (might be due to invalid IL or missing references)
				Accessor accessor = GetAccessor(jointsAccessor);
				Accessor accessor2 = GetAccessor(weightsAccessor);
				BoneWeight[] array = (BoneWeight[])(object)new BoneWeight[accessor.Count];
				for (int i = 0; i < accessor.Count; i++)
				{
					int elementOffset = accessor.Offset + i * accessor.Stride;
					int num = accessor2.Offset + i * accessor2.Stride;
					int boneIndex = Joint(accessor, elementOffset, 0, boneCount);
					int boneIndex2 = Joint(accessor, elementOffset, 1, boneCount);
					int boneIndex3 = Joint(accessor, elementOffset, 2, boneCount);
					int boneIndex4 = Joint(accessor, elementOffset, 3, boneCount);
					float num2 = m_bin.Float(num);
					float num3 = m_bin.Float(num + 4);
					float num4 = m_bin.Float(num + 8);
					float num5 = m_bin.Float(num + 12);
					float num6 = num2 + num3 + num4 + num5;
					if (num6 > 0.0001f)
					{
						num2 /= num6;
						num3 /= num6;
						num4 /= num6;
						num5 /= num6;
					}
					else
					{
						num2 = 1f;
					}
					int num7 = i;
					BoneWeight val = default(BoneWeight);
					((BoneWeight)(ref val)).boneIndex0 = boneIndex;
					((BoneWeight)(ref val)).weight0 = num2;
					((BoneWeight)(ref val)).boneIndex1 = boneIndex2;
					((BoneWeight)(ref val)).weight1 = num3;
					((BoneWeight)(ref val)).boneIndex2 = boneIndex3;
					((BoneWeight)(ref val)).weight2 = num4;
					((BoneWeight)(ref val)).boneIndex3 = boneIndex4;
					((BoneWeight)(ref val)).weight3 = num5;
					array[num7] = val;
				}
				return array;
			}

			private int Joint(Accessor acc, int elementOffset, int component, int boneCount)
			{
				int offset = elementOffset + component * acc.ComponentSize;
				int num = ((acc.ComponentSize == 1) ? m_bin.Byte(offset) : ((acc.ComponentSize == 2) ? m_bin.UInt16(offset) : ((int)m_bin.UInt32(offset))));
				if (num < 0 || num >= boneCount)
				{
					return 0;
				}
				return num;
			}

			private Matrix4x4[] ReadBindPoses(int accessor)
			{
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0190: 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_019e: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b9: 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_01c8: 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_01d4: Unknown result type (might be due to invalid IL or missing references)
				//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
				//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
				//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
				//IL_0206: Unknown result type (might be due to invalid IL or missing references)
				//IL_020e: Unknown result type (might be due to invalid IL or missing references)
				//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_021f: Unknown result type (might be due to invalid IL or missing references)
				Accessor accessor2 = GetAccessor(accessor);
				Matrix4x4[] array = (Matrix4x4[])(object)new Matrix4x4[accessor2.Count];
				for (int i = 0; i < accessor2.Count; i++)
				{
					int num = accessor2.Offset + i * accessor2.Stride;
					Matrix4x4 val = new Matrix4x4
					{
						m00 = m_bin.Float(num),
						m10 = m_bin.Float(num + 4),
						m20 = m_bin.Float(num + 8),
						m30 = m_bin.Float(num + 12),
						m01 = m_bin.Float(num + 16),
						m11 = m_bin.Float(num + 20),
						m21 = m_bin.Float(num + 24),
						m31 = m_bin.Float(num + 28),
						m02 = m_bin.Float(num + 32),
						m12 = m_bin.Float(num + 36),
						m22 = m_bin.Float(num + 40),
						m32 = m_bin.Float(num + 44),
						m03 = m_bin.Float(num + 48),
						m13 = m_bin.Float(num + 52),
						m23 = m_bin.Float(num + 56),
						m33 = m_bin.Float(num + 60)
					};
					array[i] = new Matrix4x4(new Vector4(val.m00, val.m10, 0f - val.m20, val.m30), new Vector4(val.m01, val.m11, 0f - val.m21, val.m31), new Vector4(0f - val.m02, 0f - val.m12, val.m22, 0f - val.m32), new Vector4(val.m03, val.m13, 0f - val.m23, val.m33));
				}
				return array;
			}

			private Accessor GetAccessor(int index)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Expected O, but got Unknown
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0035: Expected O, but got Unknown
				JObject val = (JObject)m_accessors[index];
				int num = (int)val["bufferView"];
				JObject val2 = (JObject)m_views[num];
				int num2 = (int)val["componentType"];
				int num3 = (string)val["type"] switch
				{
					"VEC2" => 2, 
					"VEC3" => 3, 
					"VEC4" => 4, 
					"MAT4" => 16, 
					_ => 1, 
				};
				int num4;
				switch (num2)
				{
				default:
					num4 = 1;
					break;
				case 5122:
				case 5123:
					num4 = 2;
					break;
				case 5125:
				case 5126:
					num4 = 4;
					break;
				}
				int num5 = num4;
				int num6 = ((val2["byteOffset"] != null) ? ((int)val2["byteOffset"]) : 0);
				int num7 = ((val["byteOffset"] != null) ? ((int)val["byteOffset"]) : 0);
				int num8 = num5 * num3;
				int stride = ((val2["byteStride"] != null) ? ((int)val2["byteStride"]) : num8);
				return new Accessor
				{
					Count = (int)val["count"],
					Components = num3,
					ComponentSize = num5,
					Offset = num6 + num7,
					Stride = stride
				};
			}

			private static Node ReadNode(JObject n)
			{
				//IL_0057: Unknown result type (might be due to invalid IL or missing references)
				//IL_005d: Expected O, but got Unknown
				//IL_0086: Unknown result type (might be due to invalid IL or missing references)
				//IL_008b: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ae: Expected O, but got Unknown
				//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
				//IL_0107: Unknown result type (might be due to invalid IL or missing references)
				//IL_010d: Expected O, but got Unknown
				//IL_0135: Unknown result type (might be due to invalid IL or missing references)
				//IL_013a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0157: Unknown result type (might be due to invalid IL or missing references)
				//IL_015e: Expected O, but got Unknown
				Node node = new Node();
				node.Name = (string)n["name"];
				if (n["mesh"] != null)
				{
					node.Mesh = (int)n["mesh"];
				}
				if (n["translation"] != null)
				{
					JArray val = (JArray)n["translation"];
					node.Translation = new Vector3((float)val[0], (float)val[1], 0f - (float)val[2]);
				}
				if (n["rotation"] != null)
				{
					JArray val2 = (JArray)n["rotation"];
					node.Rotation = new Quaternion((float)val2[0], (float)val2[1], 0f - (float)val2[2], 0f - (float)val2[3]);
				}
				if (n["scale"] != null)
				{
					JArray val3 = (JArray)n["scale"];
					node.Scale = new Vector3((float)val3[0], (float)val3[1], (float)val3[2]);
				}
				if (n["children"] != null)
				{
					JArray val4 = (JArray)n["children"];
					node.Children = new int[((JContainer)val4).Count];
					for (int i = 0; i < ((JContainer)val4).Count; i++)
					{
						node.Children[i] = (int)val4[i];
					}
				}
				return node;
			}
		}

		private sealed class Bin
		{
			private readonly byte[] m_file;

			private readonly int m_start;

			public int Length { get; }

			public Bin(byte[] file, int start, int length)
			{
				m_file = file;
				m_start = start;
				Length = length;
			}

			public float Float(int offset)
			{
				return BitConverter.ToSingle(m_file, m_start + offset);
			}

			public int Byte(int offset)
			{
				return m_file[m_start + offset];
			}

			public int UInt16(int offset)
			{
				return BitConverter.ToUInt16(m_file, m_start + offset);
			}

			public uint UInt32(int offset)
			{
				return BitConverter.ToUInt32(m_file, m_start + offset);
			}

			public byte[] Slice(int offset, int length)
			{
				byte[] array = new byte[length];
				Buffer.BlockCopy(m_file, m_start + offset, array, 0, length);
				return array;
			}
		}

		private const uint GlbMagic = 1179937895u;

		private const int FloatType = 5126;

		public static string IdleName { get; private set; } = "press-up";

		public static string TalkName { get; private set; } = "agree";

		public static AnimationClip IdleClip { get; private set; }

		public static AnimationClip TalkClip { get; private set; }

		public static Pose IdlePose { get; private set; }

		public static Pose TalkPose { get; private set; }

		public static GameObject Build(string path, Material sourceMaterial)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			byte[] array = File.ReadAllBytes(path);
			if (array.Length < 20 || BitConverter.ToUInt32(array, 0) != 1179937895)
			{
				throw new InvalidDataException("Not a GLB file: " + path);
			}
			int num = BitConverter.ToInt32(array, 12);
			string text = Encoding.UTF8.GetString(array, 20, num);
			int num2 = 20 + num;
			int length = BitConverter.ToInt32(array, num2);
			int start = num2 + 8;
			JObject root = JObject.Parse(text);
			Bin bin = new Bin(array, start, length);
			Doc doc = new Doc(root, bin);
			GameObject val = new GameObject("Pebble_Npc");
			Dictionary<int, Transform> transforms;
			Dictionary<int, string> paths;
			GameObject obj = BuildHierarchy(doc, out transforms, out paths);
			obj.transform.SetParent(val.transform, false);
			obj.transform.localRotation = Quaternion.Euler(0f, 180f, 0f);
			AttachMesh(material: CreateMaterial(doc, sourceMaterial), doc: doc, transforms: transforms);
			IdleName = PickAnimation(doc, "press-up", null);
			TalkName = PickAnimation(doc, "agree", IdleName);
			Built built = BuildClip(doc, paths, IdleName);
			Built built2 = BuildClip(doc, paths, TalkName);
			IdleClip = built.Clip;
			TalkClip = built2.Clip;
			IdlePose = built.Pose;
			TalkPose = built2.Pose;
			float num3 = 0f;
			SkinnedMeshRenderer componentInChildren = val.GetComponentInChildren<SkinnedMeshRenderer>();
			if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.sharedMesh != (Object)null)
			{
				Bounds bounds = componentInChildren.sharedMesh.bounds;
				num3 = ((Bounds)(ref bounds)).size.y;
			}
			PebblePlugin.Log.LogInfo((object)$"Pebble: built NPC from {Path.GetFileName(path)} ({num3:0.00} m in the file). Idle clip '{IdleName}' ({(((Object)(object)IdleClip != (Object)null) ? IdleClip.length : 0f):0.00}s), talk clip '{TalkName}' ({(((Object)(object)TalkClip != (Object)null) ? TalkClip.length : 0f):0.00}s).");
			return val;
		}

		public static void Release()
		{
			if ((Object)(object)IdleClip != (Object)null)
			{
				Object.Destroy((Object)(object)IdleClip);
				IdleClip = null;
			}
			if ((Object)(object)TalkClip != (Object)null)
			{
				Object.Destroy((Object)(object)TalkClip);
				TalkClip = null;
			}
			IdlePose = null;
			TalkPose = null;
		}

		private static GameObject BuildHierarchy(Doc doc, out Dictionary<int, Transform> transforms, out Dictionary<int, string> paths)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			int count = doc.Nodes.Count;
			GameObject[] array = (GameObject[])(object)new GameObject[count];
			transforms = new Dictionary<int, Transform>(count);
			for (int i = 0; i < count; i++)
			{
				Node node = doc.Nodes[i];
				GameObject val = new GameObject(string.IsNullOrEmpty(node.Name) ? ("node" + i) : node.Name);
				val.transform.localPosition = node.Translation;
				val.transform.localRotation = node.Rotation;
				val.transform.localScale = node.Scale;
				array[i] = val;
				transforms[i] = val.transform;
			}
			for (int j = 0; j < count; j++)
			{
				int[] children = doc.Nodes[j].Children;
				if (children != null)
				{
					for (int k = 0; k < children.Length; k++)
					{
						array[children[k]].transform.SetParent(array[j].transform, false);
					}
				}
			}
			int sceneRoot = doc.SceneRoot;
			paths = new Dictionary<int, string>();
			WritePaths(doc, sceneRoot, "", paths);
			return array[sceneRoot];
		}

		private static void WritePaths(Doc doc, int index, string path, Dictionary<int, string> paths)
		{
			paths[index] = path;
			int[] children = doc.Nodes[index].Children;
			if (children == null)
			{
				return;
			}
			for (int i = 0; i < children.Length; i++)
			{
				int index2 = children[i];
				string text = doc.Nodes[index2].Name;
				if (string.IsNullOrEmpty(text))
				{
					text = "node" + index2;
				}
				string path2 = (string.IsNullOrEmpty(path) ? text : (path + "/" + text));
				WritePaths(doc, index2, path2, paths);
			}
		}

		private static void AttachMesh(Doc doc, Dictionary<int, Transform> transforms, Material material)
		{
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			int num = -1;
			for (int i = 0; i < doc.Nodes.Count; i++)
			{
				if (doc.Nodes[i].Mesh >= 0)
				{
					num = i;
					break;
				}
			}
			if (num < 0 || doc.SkinJoints == null)
			{
				throw new InvalidDataException("GLB has no skinned mesh.");
			}
			int[] skinJoints = doc.SkinJoints;
			Transform[] array = (Transform[])(object)new Transform[skinJoints.Length];
			for (int j = 0; j < skinJoints.Length; j++)
			{
				array[j] = transforms[skinJoints[j]];
			}
			Mesh sharedMesh = doc.ReadMesh(array.Length);
			SkinnedMeshRenderer obj = ((Component)transforms[num]).gameObject.AddComponent<SkinnedMeshRenderer>();
			obj.sharedMesh = sharedMesh;
			obj.bones = array;
			obj.rootBone = (transforms.ContainsKey(skinJoints[0]) ? transforms[skinJoints[0]] : null);
			((Renderer)obj).sharedMaterial = material;
			obj.updateWhenOffscreen = true;
			((Renderer)obj).localBounds = new Bounds(new Vector3(0f, 0.75f, 0f), new Vector3(1.4f, 1.6f, 1.4f));
			obj.quality = (SkinQuality)4;
			BakeBindPoses(obj);
		}

		private static void BakeBindPoses(SkinnedMeshRenderer renderer)
		{
			//IL_002a: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			Transform[] bones = renderer.bones;
			Mesh sharedMesh = renderer.sharedMesh;
			if (bones != null && !((Object)(object)sharedMesh == (Object)null))
			{
				Matrix4x4[] array = (Matrix4x4[])(object)new Matrix4x4[bones.Length];
				Matrix4x4 localToWorldMatrix = ((Component)renderer).transform.localToWorldMatrix;
				for (int i = 0; i < bones.Length; i++)
				{
					array[i] = (((Object)(object)bones[i] == (Object)null) ? Matrix4x4.identity : (bones[i].worldToLocalMatrix * localToWorldMatrix));
				}
				sharedMesh.bindposes = array;
				((Renderer)renderer).localBounds = sharedMesh.bounds;
			}
		}

		private static Material CreateMaterial(Doc doc, Material sourceMaterial)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = doc.ReadImage(1, linear: false);
			Material val2 = (((Object)(object)sourceMaterial != (Object)null) ? new Material(sourceMaterial) : new Material(Shader.Find("Custom/StaticObject") ?? Shader.Find("Standard")));
			((Object)val2).name = "Pebble_Mat";
			if ((Object)(object)val != (Object)null)
			{
				val2.mainTexture = (Texture)(object)val;
				SetTex(val2, "_MainTex", (Texture)(object)val);
				SetTex(val2, "_BaseMap", (Texture)(object)val);
				SetTex(val2, "_Albedo", (Texture)(object)val);
				val2.SetTextureScale("_MainTex", Vector2.one);
				val2.SetTextureOffset("_MainTex", Vector2.zero);
			}
			if (val2.HasProperty("_Color"))
			{
				val2.SetColor("_Color", Color.white);
			}
			if (val2.HasProperty("_BaseColor"))
			{
				val2.SetColor("_BaseColor", Color.white);
			}
			if (val2.HasProperty("_Mode"))
			{
				val2.SetFloat("_Mode", 0f);
			}
			if (val2.HasProperty("_Cutoff"))
			{
				val2.SetFloat("_Cutoff", 0f);
			}
			val2.DisableKeyword("_ALPHATEST_ON");
			val2.DisableKeyword("_ALPHABLEND_ON");
			val2.DisableKeyword("_ALPHAPREMULTIPLY_ON");
			val2.renderQueue = 2000;
			PebblePlugin.Log.LogInfo((object)string.Format("Pebble: material shader '{0}', albedo {1}.", ((Object)(object)val2.shader != (Object)null) ? ((Object)val2.shader).name : "none", ((Object)(object)val != (Object)null) ? (((Texture)val).width + "x" + ((Texture)val).height) : "missing"));
			return val2;
		}

		private static void SetTex(Material mat, string prop, Texture tex)
		{
			if (mat.HasProperty(prop))
			{
				mat.SetTexture(prop, tex);
			}
		}

		private static void FaceOutward(Vector3[] positions, Vector3[] normals, int[] indices)
		{
			//IL_000e: 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_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_0025: 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_003a: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: 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)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			if (positions == null || normals == null || indices == null || positions.Length == 0)
			{
				return;
			}
			Vector3 val = Vector3.zero;
			for (int i = 0; i < positions.Length; i++)
			{
				val += positions[i];
			}
			val /= (float)positions.Length;
			int num = 0;
			for (int j = 0; j < indices.Length; j += 3)
			{
				int num2 = indices[j];
				int num3 = indices[j + 1];
				int num4 = indices[j + 2];
				Vector3 val2 = Vector3.Cross(positions[num3] - positions[num2], positions[num4] - positions[num2]);
				Vector3 val3 = normals[num2] + normals[num3] + normals[num4];
				Vector3 val4 = (positions[num2] + positions[num3] + positions[num4]) / 3f - val;
				if (Vector3.Dot(val2, val3) < 0f && Vector3.Dot(val2, val4) < 0f)
				{
					indices[j + 1] = num4;
					indices[j + 2] = num3;
					num++;
				}
			}
			PebblePlugin.Log.LogInfo((object)("Pebble: turned " + num + " inward triangles outward so the face draws solid."));
		}

		private static string PickAnimation(Doc doc, string preferred, string skip)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			JArray val = (JArray)doc.Root["animations"];
			if (val == null || ((JContainer)val).Count == 0)
			{
				return preferred;
			}
			for (int i = 0; i < ((JContainer)val).Count; i++)
			{
				if ((string)val[i][(object)"name"] == preferred)
				{
					return preferred;
				}
			}
			for (int j = 0; j < ((JContainer)val).Count; j++)
			{
				string text = (string)val[j][(object)"name"];
				if (!string.IsNullOrEmpty(text) && text != skip)
				{
					return text;
				}
			}
			return ((string)val[0][(object)"name"]) ?? preferred;
		}

		private static Built BuildClip(Doc doc, Dictionary<int, string> paths, string name)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Expected O, but got Unknown
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Expected O, but got Unknown
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			JArray val = (JArray)doc.Root["animations"];
			if (val == null)
			{
				return default(Built);
			}
			JObject val2 = null;
			for (int i = 0; i < ((JContainer)val).Count; i++)
			{
				if ((string)val[i][(object)"name"] == name)
				{
					val2 = (JObject)val[i];
					break;
				}
			}
			if (val2 == null)
			{
				PebblePlugin.Log.LogWarning((object)("Pebble: GLB is missing animation '" + name + "'."));
				return default(Built);
			}
			Dictionary<string, Pose.Track> dictionary = new Dictionary<string, Pose.Track>();
			AnimationClip val3 = new AnimationClip();
			val3.legacy = true;
			((Object)val3).name = name;
			val3.frameRate = 30f;
			val3.wrapMode = (WrapMode)2;
			JArray val4 = (JArray)val2["channels"];
			JArray val5 = (JArray)val2["samplers"];
			for (int j = 0; j < ((JContainer)val4).Count; j++)
			{
				JObject val6 = (JObject)val4[j];
				int num = (int)val6["sampler"];
				JObject val7 = (JObject)val5[num];
				int key = (int)val6["target"][(object)"node"];
				string text = (string)val6["target"][(object)"path"];
				if (!paths.TryGetValue(key, out var value) || string.IsNullOrEmpty(value))
				{
					continue;
				}
				string text2 = ((string)val7["interpolation"]) ?? "LINEAR";
				bool flag = string.Equals(text2, "STEP", StringComparison.OrdinalIgnoreCase);
				float[] array = doc.ReadFloats((int)val7["input"]);
				float[] array2 = doc.ReadFloats((int)val7["output"]);
				Pose.Track track = GetTrack(dictionary, value);
				switch (text)
				{
				case "rotation":
				{
					Quaternion[] array5 = (Quaternion[])(object)new Quaternion[array.Length];
					for (int m = 0; m < array.Length; m++)
					{
						array5[m] = new Quaternion(array2[m * 4], array2[m * 4 + 1], 0f - array2[m * 4 + 2], 0f - array2[m * 4 + 3]);
					}
					track.RotTimes = array;
					track.Rotations = array5;
					track.RotationStep = flag;
					break;
				}
				case "translation":
				{
					Vector3[] array4 = (Vector3[])(object)new Vector3[array.Length];
					for (int l = 0; l < array.Length; l++)
					{
						array4[l] = new Vector3(array2[l * 3], array2[l * 3 + 1], 0f - array2[l * 3 + 2]);
					}
					track.PosTimes = array;
					track.Positions = array4;
					track.PositionStep = flag;
					break;
				}
				case "scale":
				{
					Vector3[] array3 = (Vector3[])(object)new Vector3[array.Length];
					for (int k = 0; k < array.Length; k++)
					{
						array3[k] = new Vector3(array2[k * 3], array2[k * 3 + 1], array2[k * 3 + 2]);
					}
					track.ScaleTimes = array;
					track.Scales = array3;
					track.ScaleStep = flag;
					break;
				}
				}
				int num2 = ((text == "rotation") ? 4 : 3);
				string[] array6 = ((!(text == "rotation")) ? ((!(text == "scale")) ? new string[3] { "localPosition.x", "localPosition.y", "localPosition.z" } : new string[3] { "localScale.x", "localScale.y", "localScale.z" }) : new string[4] { "localRotation.x", "localRotation.y", "localRotation.z", "localRotation.w" });
				for (int n = 0; n < num2; n++)
				{
					float[] array7 = new float[array.Length];
					for (int num3 = 0; num3 < array.Length; num3++)
					{
						float num4 = array2[num3 * num2 + n];
						if (text == "translation" && n == 2)
						{
							num4 = 0f - num4;
						}
						else if (text == "rotation" && (n == 2 || n == 3))
						{
							num4 = 0f - num4;
						}
						array7[num3] = num4;
					}
					val3.SetCurve(value, typeof(Transform), array6[n], MakeCurve(array, array7, text2));
				}
			}
			val3.EnsureQuaternionContinuity();
			Pose pose = new Pose();
			pose.Name = name;
			pose.Length = val3.length;
			pose.Tracks = new Pose.Track[dictionary.Count];
			dictionary.Values.CopyTo(pose.Tracks, 0);
			return new Built
			{
				Clip = val3,
				Pose = pose
			};
		}

		private static Pose.Track GetTrack(Dictionary<string, Pose.Track> tracks, string path)
		{
			if (!tracks.TryGetValue(path, out var value))
			{
				value = (tracks[path] = new Pose.Track
				{
					Path = path
				});
			}
			return value;
		}

		private static AnimationCurve MakeCurve(float[] times, float[] values, string interpolation)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_019d: Expected O, but got Unknown
			//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_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: 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)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			bool flag = string.Equals(interpolation, "STEP", StringComparison.OrdinalIgnoreCase);
			List<Keyframe> list = new List<Keyframe>(times.Length * ((!flag) ? 1 : 2));
			for (int i = 0; i < times.Length; i++)
			{
				float num = times[i];
				Keyframe val;
				if (list.Count > 0)
				{
					float num2 = num;
					val = list[list.Count - 1];
					if (num2 <= ((Keyframe)(ref val)).time)
					{
						val = list[list.Count - 1];
						num = ((Keyframe)(ref val)).time + 0.0001f;
					}
				}
				if (flag && i > 0)
				{
					val = list[list.Count - 1];
					float num3 = Mathf.Max(((Keyframe)(ref val)).time + 0.0001f, num - 0.0001f);
					list.Add(new Keyframe(num3, values[i - 1], float.PositiveInfinity, float.PositiveInfinity));
				}
				if (flag)
				{
					list.Add(new Keyframe(num, values[i], float.PositiveInfinity, float.PositiveInfinity));
				}
				else
				{
					list.Add(new Keyframe(num, values[i]));
				}
			}
			if (!flag && list.Count > 1)
			{
				for (int j = 0; j < list.Count - 1; j++)
				{
					Keyframe value = list[j];
					Keyframe value2 = list[j + 1];
					float num4 = ((Keyframe)(ref value2)).time - ((Keyframe)(ref value)).time;
					float inTangent = (((Keyframe)(ref value)).outTangent = ((num4 > 1E-07f) ? ((((Keyframe)(ref value2)).value - ((Keyframe)(ref value)).value) / num4) : 0f));
					((Keyframe)(ref value2)).inTangent = inTangent;
					list[j] = value;
					list[j + 1] = value2;
				}
			}
			return new AnimationCurve(list.ToArray());
		}
	}
	public sealed class Pose
	{
		public sealed class Track
		{
			public string Path;

			public float[] PosTimes;

			public Vector3[] Positions;

			public bool PositionStep;

			public float[] RotTimes;

			public Quaternion[] Rotations;

			public bool RotationStep;

			public float[] ScaleTimes;

			public Vector3[] Scales;

			public bool ScaleStep;

			public bool SamplePosition(float time, out Vector3 value)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				value = default(Vector3);
				return Sample(PosTimes, Positions, PositionStep, time, out value);
			}

			public bool SampleRotation(float time, out Quaternion value)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0050: Unknown result type (might be due to invalid IL or missing references)
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				//IL_0072: Unknown result type (might be due to invalid IL or missing references)
				//IL_0080: 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_008b: Unknown result type (might be due to invalid IL or missing references)
				value = Quaternion.identity;
				if (RotTimes == null || Rotations == null || RotTimes.Length == 0)
				{
					return false;
				}
				int num = Index(RotTimes, time);
				if (RotationStep || num + 1 >= RotTimes.Length)
				{
					value = Rotations[num];
					return true;
				}
				float num2 = Blend(RotTimes, num, time);
				value = Quaternion.Slerp(Rotations[num], Rotations[num + 1], num2);
				return true;
			}

			public bool SampleScale(float time, out Vector3 value)
			{
				//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)
				value = Vector3.one;
				return Sample(ScaleTimes, Scales, ScaleStep, time, out value);
			}

			private static bool Sample(float[] times, Vector3[] values, bool step, float time, out Vector3 value)
			{
				//IL_0002: 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_0030: 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_0044: Unknown result type (might be due to invalid IL or missing references)
				//IL_0051: 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)
				value = default(Vector3);
				if (times == null || values == null || times.Length == 0)
				{
					return false;
				}
				int num = Index(times, time);
				if (step || num + 1 >= times.Length)
				{
					value = values[num];
					return true;
				}
				value = Vector3.Lerp(values[num], values[num + 1], Blend(times, num, time));
				return true;
			}

			private static int Index(float[] times, float time)
			{
				int i = 0;
				for (int num = times.Length - 1; i < num && times[i + 1] <= time; i++)
				{
				}
				return i;
			}

			private static float Blend(float[] times, int i, float time)
			{
				float num = times[i + 1] - times[i];
				if (num <= 1E-05f)
				{
					return 0f;
				}
				return Mathf.Clamp01((time - times[i]) / num);
			}
		}

		public string Name;

		public float Length;

		public Track[] Tracks;
	}
	public class PebbleGUI : MonoBehaviour
	{
		private struct ShopEntry
		{
			public string Prefab;

			public string Name;

			public string Desc;

			public int Price;

			public ShopEntry(string prefab, string name, string desc, int price)
			{
				Prefab = prefab;
				Name = name;
				Desc = desc;
				Price = price;
			}
		}

		private struct BarterOption
		{
			public string Prefab;

			public string Name;

			public int BundleSize;

			public int CoinPayout;

			public string Category;

			public BarterOption(string prefab, string name, int bundleSize, int coinPayout, string category = "General")
			{
				Prefab = prefab;
				Name = name;
				BundleSize = bundleSize;
				CoinPayout = coinPayout;
				Category = category;
			}
		}

		private bool m_open;

		private PebbleCompanion m_activeCompanion;

		private int m_activeTab;

		private string m_statusMessage = "";

		private float m_statusTimer;

		private bool m_hasPurchased;

		private Vector2 m_curiosScroll;

		private Vector2 m_sellScroll;

		private Vector2 m_potionsScroll;

		private Vector2 m_provisionsScroll;

		private Vector2 m_sounderScroll;

		private string m_sellSearchFilter = "";

		private bool m_sellOnlyInPocket;

		private string m_bankDepositAmountStr = "100";

		private string m_bankWithdrawAmountStr = "100";

		private Texture2D m_whiteTex;

		private static readonly Color ColBackdrop = new Color(0f, 0f, 0f, 0.72f);

		private static readonly Color ColWindowBg = new Color(0.08f, 0.1f, 0.14f, 0.98f);

		private static readonly Color ColWindowBorder = new Color(0.74f, 0.58f, 0.28f, 1f);

		private static readonly Color ColWindowBorderInner = new Color(0.18f, 0.23f, 0.32f, 0.85f);

		private static readonly Color ColCardBg = new Color(0.09f, 0.11f, 0.16f, 0.96f);

		private static readonly Color ColCardBorder = new Color(0.22f, 0.28f, 0.38f, 0.85f);

		private static readonly Color ColCardHeaderBg = new Color(0.12f, 0.16f, 0.22f, 1f);

		private static readonly Color ColGoldText = new Color(1f, 0.84f, 0.38f, 1f);

		private static readonly Color ColWhiteText = new Color(0.92f, 0.95f, 0.98f, 1f);

		private static readonly Color ColMutedText = new Color(0.6f, 0.66f, 0.75f, 1f);

		private static readonly Color ColCyanText = new Color(0.4f, 0.8f, 1f, 1f);

		private static readonly Color ColGreenText = new Color(0.38f, 0.88f, 0.48f, 1f);

		private static readonly Color ColRedText = new Color(0.95f, 0.38f, 0.38f, 1f);

		private static readonly Dictionary<string, GUIStyle> s_styleCache = new Dictionary<string, GUIStyle>();

		private static GUIStyle s_textFieldStyle;

		private static readonly List<ShopEntry> s_rareCurios = new List<ShopEntry>
		{
			new ShopEntry("Chain", "Chain", "Heavy iron link forged in crypts. Essential for armor and forge upgrades.", 75),
			new ShopEntry("Tar", "Tar", "Viscous black pitch excavated from plains tar pits.", 25),
			new ShopEntry("Chitin", "Chitin", "Hard a