Decompiled source of DvergrSkyTram v1.0.0

DvergrSkyTram.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
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.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using MyMods;
using MyMods.Web;
using Newtonsoft.Json;
using UnityEngine;
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("DvergrSkyTram")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+2979fb955216b6cc5de4783d080b3b69482390ed")]
[assembly: AssemblyProduct("DvergrSkyTram")]
[assembly: AssemblyTitle("DvergrSkyTram")]
[assembly: AssemblyVersion("1.0.0.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[15]
		{
			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),
			new Link("skytram", "Sky-Tram", 8792)
		};

		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 DvergrSkyTram
{
	[BepInPlugin("com.dfg.dvergarskytram", "Dvergr Sky-Tram", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class DvergrSkyTramPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.dfg.dvergarskytram";

		public const string PluginName = "Dvergr Sky-Tram";

		public const string PluginVersion = "1.0.0";

		public const string BoardTab = "tram";

		internal static ManualLogSource Log;

		private Harmony m_harmony;

		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<bool> DebugLogging;

		public static ConfigEntry<bool> CleanupOrphanNodes;

		public static ConfigEntry<float> OrphanCleanupRange;

		public static ConfigEntry<float> OrphanCleanupSeconds;

		public static ConfigEntry<float> TramSpeed;

		public static ConfigEntry<float> FuelPerKm;

		public static ConfigEntry<float> TurnRate;

		public static ConfigEntry<float> BobAmplitude;

		public static ConfigEntry<float> MaxCableSpanDistance;

		public static ConfigEntry<bool> RequireLineOfSight;

		public static ConfigEntry<int> MaxLinksPerNode;

		public static ConfigEntry<float> CableSag;

		public static ConfigEntry<float> CableDrawRange;

		public static ConfigEntry<float> CableWidth;

		public static ConfigEntry<float> DepotScale;

		public static ConfigEntry<float> DepotYaw;

		public static ConfigEntry<float> DepotOffsetY;

		public static ConfigEntry<float> PylonScale;

		public static ConfigEntry<float> PylonYaw;

		public static ConfigEntry<float> PylonOffsetY;

		public static ConfigEntry<float> TramScale;

		public static ConfigEntry<float> TramYaw;

		public static ConfigEntry<float> TramOffsetY;

		public static ConfigEntry<float> BalloonLift;

		public static ConfigEntry<float> BallistaScale;

		public static ConfigEntry<float> BigTramScale;

		public static ConfigEntry<float> BigTramYaw;

		public static ConfigEntry<float> BigTramOffsetY;

		public static ConfigEntry<float> Dock4Scale;

		public static ConfigEntry<float> Dock4Yaw;

		public static ConfigEntry<float> Dock4OffsetY;

		public static ConfigEntry<string> DockPads;

		public static ConfigEntry<int> WebPort;

		public static ConfigEntry<float> BigPace;

		public static ConfigEntry<string> DepotCost;

		public static ConfigEntry<string> Dock4Cost;

		public static ConfigEntry<string> PylonCost;

		public static ConfigEntry<string> TramCost;

		public static ConfigEntry<string> StorageUpgrade;

		public static ConfigEntry<string> SpeedUpgrade;

		public static ConfigEntry<string> TurretUpgrade;

		public static ConfigEntry<float> DefenseRange;

		public static ConfigEntry<float> DefenseScanSeconds;

		public static ConfigEntry<float> DefenseDamageMult;

		public static ConfigEntry<bool> DefendWhileMoving;

		private static readonly Dictionary<string, ConfigEntry<string>> s_parts = new Dictionary<string, ConfigEntry<string>>();

		private void Awake()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			if (Enabled != null && !Enabled.Value)
			{
				Log.LogInfo((object)"Dvergr Sky-Tram is disabled in config.");
				return;
			}
			try
			{
				m_harmony = new Harmony("com.dfg.dvergarskytram");
				m_harmony.PatchAll();
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Sky-Tram: failed to apply Harmony patches: " + ex));
			}
			TramPiece.Initialize();
			TramDockWeb.Start();
			HallClient.Bind("tram", "Sky-Tram", 60, TramBoard.Draw);
			Log.LogInfo((object)"Dvergr Sky-Tram v1.0.0 loaded.");
		}

		private void Update()
		{
			HallClient.Flush();
			if (Enabled != null && Enabled.Value)
			{
				TramPiece.Update();
				SkyTramGlb.RepairShaders();
				TramNode.RepairEdges();
				TramOrphans.Tick();
				TramNetwork.Tick();
				TramFlight.Tick();
				TramNodePanel.Tick();
				TramDockWeb.Drain();
			}
		}

		private void OnGUI()
		{
			if (Enabled != null && Enabled.Value)
			{
				TramNodePanel.Draw();
			}
		}

		private void OnDestroy()
		{
			TramDockWeb.Stop();
			TramNodePanel.Close();
			TramRpc.Shutdown();
			TramNetwork.Shutdown();
			TramPiece.Shutdown();
			if (m_harmony != null)
			{
				m_harmony.UnpatchSelf();
			}
		}

		internal static void Debug(string message)
		{
			if (DebugLogging != null && DebugLogging.Value)
			{
				Log.LogInfo((object)("[Sky-Tram] " + message));
			}
		}

		private void BindConfig()
		{
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Master switch.");
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "DebugLogging", false, "Log rig measurements, resolved part indices and per-dispatch decisions.");
			CleanupOrphanNodes = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "CleanupOrphanNodes", true, "Host only, once per session. Remove depot and pylon ZDOs that nothing in the world claims. ZNetView.Awake creates a ZDO, and one that is never claimed is re-reported fifty times a second on every load. Only ZDOs within OrphanCleanupRange of you are considered, so a piece standing in an unloaded chunk is never mistaken for a stray.");
			OrphanCleanupRange = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "OrphanCleanupRange", 100f, "How near you a stray must be before it is removed, in metres. Raise it if you build far from where you stand; lower it to be more cautious.");
			OrphanCleanupSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "OrphanCleanupSeconds", 60f, "How often the sweep repeats, in seconds. The build-menu ghost re-instantiates the selected piece and every ZNetView.Awake creates a ZDO, so strays can be created continuously while you look at a preview; this bounds the log flood to one interval instead of the whole session.");
			TramSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Trams", "TramSpeed", 8f, "Cruising speed in metres per second (2-20). Server-authoritative: the value is read by the host and pushed to clients.");
			FuelPerKm = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Trams", "FuelPerKm", 0.25f, "Surtling Cores burned per kilometre. 0.25 means one core every 4 km. Taken from the airship's hold, then the sender's pockets. The host burns it, including when the ship is in an unloaded chunk.");
			TurnRate = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Trams", "TurnRate", 60f, "Rate the zeppelin yaws towards the next waypoint, in degrees per second.");
			BobAmplitude = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Trams", "BobAmplitude", 0.35f, "How hard the rig bobs and banks while moving, scaled by speed.");
			BigPace = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Trams", "BigPace", 0.85f, "The larger airship's cruise speed as a fraction of TramSpeed, before upgrades. 0.85 is a little slower than the smaller hull.");
			MaxCableSpanDistance = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Network", "MaxCableSpanDistance", 120f, "Largest gap a cable may span between two nodes, in metres (20-300).");
			RequireLineOfSight = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Network", "RequireLineOfSight", true, "Refuse a cable whose straight line crosses terrain or a structure. Foliage and creatures never block it.");
			MaxLinksPerNode = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Network", "MaxLinksPerNode", 16, "Cap on cables leaving one node. Keeps a ZDO from growing without bound and stops the network degenerating into an all-to-all mesh.");
			CableSag = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Network", "CableSag", 0.06f, "Sag as a fraction of span length. Applied about the chord, so a depot-to-pylon span can climb instead of hanging below both ends.");
			CableDrawRange = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Network", "CableDrawRange", 220f, "Cables further than this from the camera are not drawn.");
			CableWidth = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Network", "CableWidth", 0.09f, "Rendered cable thickness in metres.");
			DepotScale = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "DepotScale", 8f, "Scale of platform_norope.glb, which is 0.979 long as exported.");
			DepotYaw = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "DepotYaw", -90f, "Depot yaw in degrees.");
			DepotOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "DepotOffsetY", 0f, "Depot vertical offset in metres.");
			PylonScale = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "PylonScale", 10f, "Scale of tower.glb, which is 0.999 tall as exported.");
			PylonYaw = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "PylonYaw", -90f, "Pylon yaw in degrees.");
			PylonOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "PylonOffsetY", 0f, "Pylon vertical offset in metres.");
			TramScale = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "TramScale", 7f, "Scale of airship_norope.glb, which is 0.975 long as exported.");
			TramYaw = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "TramYaw", 90f, "Tram yaw in degrees. 90 points the bow along travel.");
			TramOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "TramOffsetY", 0f, "Tram vertical offset in metres.");
			BalloonLift = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "BalloonLift", 1.6f, "How far the balloon is raised above the deck, in metres, so a seated rider has head room. 0 leaves the export as authored. The deck, the bench and the cable wheels stay put.");
			BallistaScale = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "BallistaScale", 2f, "Scale of part_ballista.glb, which is 0.871 long as exported. 2.0 makes it roughly a quarter of the zeppelin's length.");
			BigTramScale = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "BigTramScale", 14f, "Scale of bigship.glb. The export is about 0.63 long, so 14 is a little longer than the smaller airship.");
			BigTramYaw = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "BigTramYaw", 90f, "Large airship yaw in degrees. 90 points the bow along travel, the same as the smaller hull.");
			BigTramOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "BigTramOffsetY", 0f, "Large airship vertical offset in metres.");
			Dock4Scale = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "Dock4Scale", 24f, "Scale of industrialplatform.glb. The export is about 0.78 m across, so 24 is about 19 m, the same footprint as the old quay.");
			Dock4Yaw = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "Dock4Yaw", -90f, "Quay yaw in degrees.");
			Dock4OffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Models", "Dock4OffsetY", 0f, "Quay vertical offset in metres.");
			DockPads = ((BaseUnityPlugin)this).Config.Bind<string>("4 - Models", "DockPads", "", "Four quay landing spots, each x,y,z,yaw separated by semicolons. x,y,z are metres in the quay's own space. yaw is degrees added to the quay's facing. Empty uses the measured apron.");
			WebPort = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Models", "WebPort", 8792, "Port for the landing-spot page. Open http://127.0.0.1:8792/ while the game is running.");
			BindPart("DepotSpindle", "tripo_part_1", "The depot's cable coil, at model x -0.162, y 0.243.");
			BindPart("DepotSpindleWheelA", "tripo_part_12", "Wheel on the +Z end of the depot spindle.");
			BindPart("DepotSpindleWheelB", "tripo_part_25", "Wheel on the -Z end of the depot spindle.");
			BindPart("DepotLantern", "tripo_part_6", "The depot's hanging lantern on the -X end.");
			BindPart("DepotPad", "tripo_part_2", "The flat slab the airship's wheels land on. Measured 0.338 x 0.049 x 0.112, top at y 0.209.");
			BindPart("PylonWheelA", "tripo_part_14", "Pylon cable wheel on the -X arm, y 0.910.");
			BindPart("PylonWheelB", "tripo_part_18", "Pylon cable wheel on the +X arm, y 0.910.");
			BindPart("PylonLantern", "tripo_part_6", "The pylon's hanging lantern on the +Z side, y 0.748.");
			BindPart("TramGripA", "tripo_part_8", "The zeppelin's forward grip wheel, which rides the cable.");
			BindPart("TramGripB", "tripo_part_35", "The zeppelin's aft grip wheel, which rides the cable.");
			BindPart("TramGripRod", "tripo_part_47", "The rod between the grip wheels, 0.297 long on the centreline.");
			BindPart("TramSole", "tripo_part_32", "The boat's sole, the flat plate above the grip wheels.");
			BindPart("TramBench", "tripo_part_132", "The flat bench the riders sit on. About a metre across, two seats side by side.");
			BindPart("BallistaBolt", "tripo_part_36", "The bolt in the ballista's groove; marks the muzzle end.");
			DepotCost = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Crafting", "DepotCost", "BlackMarble:20,Copper:10,SurtlingCore:4,BronzeNails:2", "Hammer cost for the tram depot, as PrefabName:amount pairs. Requires a Black Forge.");
			Dock4Cost = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Crafting", "Dock4Cost", "BlackMarble:40,Copper:20,SurtlingCore:8,BronzeNails:4", "Hammer cost for the four-berth quay, as PrefabName:amount pairs.");
			PylonCost = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Crafting", "PylonCost", "wood:10:2,Iron:4,Chain:2", "Hammer cost for the tram pylon, as PrefabName:amount[:quality] pairs. Needs no crafting station. wood at quality 2 is core wood; Valheim has no CoreWood item.");
			TramCost = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Crafting", "TramCost", "Finewood:30,Iron:20,TrollHide:10,SurtlingCore:5", "What the depot terminal charges to build a zeppelin, as PrefabName:amount pairs. Paid server-side, never from the Hammer.");
			StorageUpgrade = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Crafting", "StorageUpgrade", "FineWood:20,Iron:8;FineWood:30,Iron:16,Bronze:8;FineWood:40,Iron:20,Silver:8", "Hold upgrades, one tier per semicolon. Each tier is PrefabName:amount pairs. Tiers grow the hold from 4x2 to 6x3, 8x4 and 8x6.");
			SpeedUpgrade = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Crafting", "SpeedUpgrade", "SurtlingCore:4,Iron:8;SurtlingCore:8,Iron:16;SurtlingCore:12,BlackMetal:8", "Speed upgrades, one tier per semicolon. Tiers multiply cruise speed by 1.25, 1.5 and 2.");
			TurretUpgrade = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Crafting", "TurretUpgrade", "Iron:20,FineWood:16,SurtlingCore:4;Iron:30,BlackMetal:10,SurtlingCore:8;Iron:40,BlackMetal:16,SurtlingCore:12", "Deck gun upgrades, one tier per semicolon. The smaller hull stops at one gun. The larger hull mounts up to three. They do not fire yet.");
			DefenseRange = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Defenses", "DefenseRange", 30f, "Range the deck ballistas acquire targets in, in metres.");
			DefenseScanSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Defenses", "DefenseScanSeconds", 0.75f, "Seconds between target scans.");
			DefenseDamageMult = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Defenses", "DefenseDamageMult", 1f, "Damage multiplier applied to bolt damage from the deck guns.");
			DefendWhileMoving = ((BaseUnityPlugin)this).Config.Bind<bool>("6 - Defenses", "DefendWhileMoving", true, "Let the ballistas fire while the zeppelin is under way. They never target passengers or the zeppelin itself.");
		}

		private void BindPart(string role, string defaultPart, string description)
		{
			s_parts[role] = ((BaseUnityPlugin)this).Config.Bind<string>("4 - Models", role + "Part", defaultPart, description + " (glTF node name; inactive models are still measured correctly.)");
		}

		internal static string PartName(string role)
		{
			if (s_parts.TryGetValue(role, out var value) && value != null && !string.IsNullOrEmpty(value.Value))
			{
				return value.Value;
			}
			return null;
		}

		internal static string PluginFile(string fileName)
		{
			try
			{
				string directoryName = Path.GetDirectoryName(new Uri(AssemblyLocation()).LocalPath);
				string text = Path.Combine(directoryName ?? string.Empty, fileName);
				if (File.Exists(text))
				{
					return text;
				}
				string[] array = new string[2]
				{
					Path.Combine(Paths.PluginPath ?? string.Empty, "DvergrSkyTram", fileName),
					Path.Combine(Paths.PluginPath ?? string.Empty, "DFG-DvergrSkyTram", fileName)
				};
				foreach (string text2 in array)
				{
					if (File.Exists(text2))
					{
						return text2;
					}
				}
				Debug("could not locate " + fileName + "; looked in " + directoryName + " and " + Paths.PluginPath);
				return null;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Sky-Tram: resolving " + fileName + " failed: " + ex.Message));
				return null;
			}
		}

		private static string AssemblyLocation()
		{
			return typeof(DvergrSkyTramPlugin).Assembly.Location;
		}
	}
	public static class SkyTramGlb
	{
		private struct PendingMaterial
		{
			public Material Material;

			public Texture Albedo;
		}

		[Serializable]
		private class GLTFRoot
		{
			public GLTFScene[] scenes;

			public GLTFNode[] nodes;

			public GLTFSkin[] skins;

			public GLTFMesh[] meshes;

			public GLTFMaterial[] materials;

			public GLTFAccessor[] accessors;

			public GLTFBufferView[] bufferViews;

			public GLTFTexture[] textures;

			public GLTFImage[] images;
		}

		[Serializable]
		private class GLTFSkin
		{
			public int inverseBindMatrices = -1;

			public int[] joints;
		}

		[Serializable]
		private class GLTFMesh
		{
			public string name;

			public GLTFPrimitive[] primitives;
		}

		[Serializable]
		private class GLTFPrimitive
		{
			public GLTFAttributes attributes;

			public int indices = -1;

			public int material = -1;
		}

		[Serializable]
		private class GLTFAttributes
		{
			public int POSITION = -1;

			public int NORMAL = -1;

			public int TEXCOORD_0 = -1;

			public int JOINTS_0 = -1;

			public int WEIGHTS_0 = -1;
		}

		[Serializable]
		private class GLTFNode
		{
			public string name;

			public int mesh = -1;

			public int skin = -1;

			public float[] translation;

			public float[] rotation;

			public float[] scale;

			public int[] children;
		}

		[Serializable]
		private class GLTFMaterial
		{
			public string name;

			public GLTFPBR pbrMetallicRoughness;
		}

		[Serializable]
		private class GLTFPBR
		{
			public float[] baseColorFactor;

			public float? metallicFactor;

			public float? roughnessFactor;

			public GLTFTextureInfo baseColorTexture;
		}

		[Serializable]
		private class GLTFTextureInfo
		{
			public int index = -1;
		}

		[Serializable]
		private class GLTFImage
		{
			public string mimeType;

			public int bufferView = -1;

			public string name;

			public string uri;
		}

		[Serializable]
		private class GLTFTexture
		{
			public int source = -1;
		}

		[Serializable]
		private class GLTFAccessor
		{
			public int bufferView;

			public int byteOffset;

			public int componentType;

			public int count;

			public string type;
		}

		[Serializable]
		private class GLTFBufferView
		{
			public int buffer;

			public int byteOffset;

			public int byteLength;

			public int byteStride;
		}

		[Serializable]
		private class GLTFScene
		{
			public int[] nodes;
		}

		private static readonly Dictionary<string, GameObject> s_cachedPrefabs = new Dictionary<string, GameObject>();

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

		private static readonly List<Renderer> s_solid = new List<Renderer>();

		private static Material s_liveCreature;

		private static bool s_shaderReady;

		public static GameObject LoadGlb(string path, Material sourceMaterialTemplate = null)
		{
			if (string.IsNullOrEmpty(path) || !File.Exists(path))
			{
				DvergrSkyTramPlugin.Log.LogError((object)("DrakkarGlb: Model file not found: " + path));
				return null;
			}
			if (s_cachedPrefabs.TryGetValue(path, out var value) && (Object)(object)value != (Object)null)
			{
				GameObject obj = Object.Instantiate<GameObject>(value);
				obj.SetActive(true);
				return obj;
			}
			try
			{
				byte[] array = File.ReadAllBytes(path);
				if (array.Length < 20 || BitConverter.ToUInt32(array, 0) != 1179937895)
				{
					DvergrSkyTramPlugin.Log.LogError((object)("DrakkarGlb: File is not a valid GLB: " + path));
					return null;
				}
				int num = 12;
				string text = null;
				byte[] array2 = null;
				while (num < array.Length && num + 8 <= array.Length)
				{
					uint num2 = BitConverter.ToUInt32(array, num);
					uint num3 = BitConverter.ToUInt32(array, num + 4);
					num += 8;
					switch (num3)
					{
					case 1313821514u:
						text = Encoding.UTF8.GetString(array, num, (int)num2);
						break;
					case 5130562u:
						array2 = new byte[num2];
						Buffer.BlockCopy(array, num, array2, 0, (int)num2);
						break;
					}
					num += (int)num2;
					num = (num + 3) & -4;
				}
				if (string.IsNullOrEmpty(text) || array2 == null)
				{
					DvergrSkyTramPlugin.Log.LogError((object)("DrakkarGlb: Failed to extract JSON or BIN chunk from " + path));
					return null;
				}
				Shader shader = (((Object)(object)sourceMaterialTemplate != (Object)null) ? sourceMaterialTemplate.shader : FindValidShader());
				GameObject val = ParseGLTF(text, array2, shader, Path.GetFileNameWithoutExtension(path));
				if ((Object)(object)val != (Object)null)
				{
					val.SetActive(false);
					Object.DontDestroyOnLoad((Object)(object)val);
					s_cachedPrefabs[path] = val;
					GameObject obj2 = Object.Instantiate<GameObject>(val);
					obj2.SetActive(true);
					return obj2;
				}
			}
			catch (Exception ex)
			{
				DvergrSkyTramPlugin.Log.LogError((object)("DrakkarGlb: Exception loading GLB " + path + ": " + ex));
			}
			return null;
		}

		public static Shader FindValidShader()
		{
			Shader val = Shader.Find("Custom/Creature") ?? Shader.Find("Custom/Piece") ?? Shader.Find("Custom/StaticObject") ?? Shader.Find("Standard");
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			try
			{
				Shader[] array = Resources.FindObjectsOfTypeAll<Shader>();
				foreach (Shader val2 in array)
				{
					if (((Object)val2).name == "Custom/Creature" || ((Object)val2).name == "Custom/Piece" || ((Object)val2).name == "Custom/StaticObject" || ((Object)val2).name == "Standard")
					{
						return val2;
					}
				}
			}
			catch
			{
			}
			return Shader.Find("Diffuse") ?? Shader.Find("Unlit/Texture");
		}

		private static GameObject ParseGLTF(string json, byte[] binData, Shader shader, string rootName)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			//IL_0784: Unknown result type (might be due to invalid IL or missing references)
			//IL_078a: Expected O, but got Unknown
			//IL_053e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0543: Unknown result type (might be due to invalid IL or missing references)
			//IL_07e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_082f: Unknown result type (might be due to invalid IL or missing references)
			//IL_055c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0561: Unknown result type (might be due to invalid IL or missing references)
			//IL_0566: Unknown result type (might be due to invalid IL or missing references)
			//IL_056b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0870: Unknown result type (might be due to invalid IL or missing references)
			//IL_063b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0640: Unknown result type (might be due to invalid IL or missing references)
			//IL_0572: Unknown result type (might be due to invalid IL or missing references)
			//IL_0578: Unknown result type (might be due to invalid IL or missing references)
			//IL_057d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0582: Unknown result type (might be due to invalid IL or missing references)
			//IL_0584: Unknown result type (might be due to invalid IL or missing references)
			//IL_058a: Unknown result type (might be due to invalid IL or missing references)
			//IL_058f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0594: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ab5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ad2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ade: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ae3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ae5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0377: Unknown result type (might be due to invalid IL or missing references)
			//IL_03af: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(rootName);
			try
			{
				GLTFRoot gLTFRoot = JsonConvert.DeserializeObject<GLTFRoot>(json);
				if (gLTFRoot == null)
				{
					DvergrSkyTramPlugin.Log.LogError((object)"DrakkarGlb: Failed to parse GLTF JSON");
					return null;
				}
				GLTFBufferView[] bufferViews = gLTFRoot.bufferViews ?? new GLTFBufferView[0];
				GLTFAccessor[] array = gLTFRoot.accessors ?? new GLTFAccessor[0];
				GLTFMesh[] array2 = gLTFRoot.meshes ?? new GLTFMesh[0];
				GLTFMaterial[] array3 = gLTFRoot.materials ?? new GLTFMaterial[0];
				GLTFNode[] array4 = gLTFRoot.nodes ?? new GLTFNode[0];
				GLTFSkin[] array5 = gLTFRoot.skins ?? new GLTFSkin[0];
				GLTFTexture[] array6 = gLTFRoot.textures ?? new GLTFTexture[0];
				GLTFImage[] images = gLTFRoot.images ?? new GLTFImage[0];
				Dictionary<int, Texture2D> texCache = new Dictionary<int, Texture2D>();
				Func<int, Texture2D> func = delegate(int imageIndex)
				{
					//IL_0097: Unknown result type (might be due to invalid IL or missing references)
					//IL_009e: Expected O, but got Unknown
					if (imageIndex < 0 || imageIndex >= images.Length)
					{
						return (Texture2D)null;
					}
					if (texCache.TryGetValue(imageIndex, out var value))
					{
						return value;
					}
					GLTFImage gLTFImage = images[imageIndex];
					if (gLTFImage.bufferView < 0 || gLTFImage.bufferView >= bufferViews.Length)
					{
						return (Texture2D)null;
					}
					GLTFBufferView gLTFBufferView = bufferViews[gLTFImage.bufferView];
					if (gLTFBufferView.byteOffset + gLTFBufferView.byteLength > binData.Length)
					{
						return (Texture2D)null;
					}
					byte[] array22 = new byte[gLTFBufferView.byteLength];
					Buffer.BlockCopy(binData, gLTFBufferView.byteOffset, array22, 0, gLTFBufferView.byteLength);
					Texture2D val12 = new Texture2D(2, 2, (TextureFormat)4, false);
					if (!ImageConversion.LoadImage(val12, array22))
					{
						Object.Destroy((Object)(object)val12);
						return (Texture2D)null;
					}
					((Object)val12).name = gLTFImage.name ?? $"img_{imageIndex}";
					((Texture)val12).wrapMode = (TextureWrapMode)0;
					((Texture)val12).filterMode = (FilterMode)1;
					texCache[imageIndex] = val12;
					return val12;
				};
				Mesh[] array7 = (Mesh[])(object)new Mesh[array2.Length];
				Material[][] array8 = new Material[array2.Length][];
				Vector3[] array9 = (Vector3[])(object)new Vector3[array2.Length];
				for (int num = 0; num < array2.Length; num++)
				{
					GLTFMesh gLTFMesh = array2[num];
					Mesh val2 = new Mesh();
					((Object)val2).name = gLTFMesh.name ?? $"Mesh_{num}";
					bool flag = false;
					if (gLTFMesh.primitives != null && gLTFMesh.primitives.Length != 0)
					{
						List<Vector3> list = new List<Vector3>();
						List<Vector3> list2 = new List<Vector3>();
						List<Vector2> list3 = new List<Vector2>();
						List<Vector4> list4 = new List<Vector4>();
						List<Vector4> list5 = new List<Vector4>();
						List<int[]> list6 = new List<int[]>();
						List<Material> list7 = new List<Material>();
						bool flag2 = false;
						for (int num2 = 0; num2 < gLTFMesh.primitives.Length; num2++)
						{
							GLTFPrimitive gLTFPrimitive = gLTFMesh.primitives[num2];
							int count = list.Count;
							GLTFAttributes attributes = gLTFPrimitive.attributes;
							Vector3[] array10 = ((attributes != null && attributes.POSITION >= 0 && gLTFPrimitive.attributes.POSITION < array.Length) ? ReadVec3Accessor(array[gLTFPrimitive.attributes.POSITION], bufferViews, binData) : null);
							GLTFAttributes attributes2 = gLTFPrimitive.attributes;
							Vector3[] array11 = ((attributes2 != null && attributes2.NORMAL >= 0 && gLTFPrimitive.attributes.NORMAL < array.Length) ? ReadVec3Accessor(array[gLTFPrimitive.attributes.NORMAL], bufferViews, binData) : null);
							GLTFAttributes attributes3 = gLTFPrimitive.attributes;
							Vector2[] array12 = ((attributes3 != null && attributes3.TEXCOORD_0 >= 0 && gLTFPrimitive.attributes.TEXCOORD_0 < array.Length) ? ReadVec2Accessor(array[gLTFPrimitive.attributes.TEXCOORD_0], bufferViews, binData) : null);
							GLTFAttributes attributes4 = gLTFPrimitive.attributes;
							Vector4[] array13 = ((attributes4 != null && attributes4.JOINTS_0 >= 0 && gLTFPrimitive.attributes.JOINTS_0 < array.Length) ? ReadVec4Accessor(array[gLTFPrimitive.attributes.JOINTS_0], bufferViews, binData) : null);
							GLTFAttributes attributes5 = gLTFPrimitive.attributes;
							Vector4[] array14 = ((attributes5 != null && attributes5.WEIGHTS_0 >= 0 && gLTFPrimitive.attributes.WEIGHTS_0 < array.Length) ? ReadVec4Accessor(array[gLTFPrimitive.attributes.WEIGHTS_0], bufferViews, binData) : null);
							if (array13 != null && array14 != null)
							{
								flag2 = true;
							}
							if (array10 != null)
							{
								list.AddRange(array10);
								if (array11 != null && array11.Length == array10.Length)
								{
									list2.AddRange(array11);
								}
								else
								{
									for (int num3 = 0; num3 < array10.Length; num3++)
									{
										list2.Add(Vector3.up);
									}
								}
								if (array12 != null && array12.Length == array10.Length)
								{
									list3.AddRange(array12);
								}
								else
								{
									for (int num4 = 0; num4 < array10.Length; num4++)
									{
										list3.Add(Vector2.zero);
									}
								}
								if (array13 != null && array13.Length == array10.Length)
								{
									list4.AddRange(array13);
								}
								if (array14 != null && array14.Length == array10.Length)
								{
									list5.AddRange(array14);
								}
							}
							if (gLTFPrimitive.indices >= 0 && gLTFPrimitive.indices < array.Length)
							{
								int[] array15 = ReadIndicesAccessor(array[gLTFPrimitive.indices], bufferViews, binData);
								if (array15 != null)
								{
									int[] array16 = new int[array15.Length];
									for (int num5 = 0; num5 < array15.Length; num5++)
									{
										array16[num5] = array15[num5] + count;
									}
									list6.Add(BothSides(array16));
								}
							}
							if (gLTFPrimitive.material >= 0 && gLTFPrimitive.material < array3.Length)
							{
								GLTFMaterial gLTFMaterial = array3[gLTFPrimitive.material];
								Texture2D baseTexture = null;
								int num6 = gLTFMaterial.pbrMetallicRoughness?.baseColorTexture?.index ?? (-1);
								if (num6 >= 0 && num6 < array6.Length)
								{
									baseTexture = func(array6[num6].source);
								}
								list7.Add(CreateMaterial(gLTFMaterial, shader, baseTexture));
							}
							else
							{
								list7.Add(CreateFallbackMaterial(shader));
							}
						}
						if (list.Count > 65535)
						{
							val2.indexFormat = (IndexFormat)1;
						}
						Vector3 val3 = Vector3.zero;
						if (!flag2 && list.Count > 0)
						{
							Vector3 val4 = list[0];
							Vector3 val5 = list[0];
							for (int num7 = 1; num7 < list.Count; num7++)
							{
								val4 = Vector3.Min(val4, list[num7]);
								val5 = Vector3.Max(val5, list[num7]);
							}
							val3 = (val4 + val5) * 0.5f;
							for (int num8 = 0; num8 < list.Count; num8++)
							{
								List<Vector3> list8 = list;
								int index = num8;
								list8[index] -= val3;
							}
						}
						array9[num] = val3;
						val2.SetVertices(list);
						val2.SetNormals(list2);
						val2.SetUVs(0, list3);
						val2.SetUVs(1, list3);
						Color[] array17 = (Color[])(object)new Color[list.Count];
						for (int num9 = 0; num9 < array17.Length; num9++)
						{
							array17[num9] = Color.white;
						}
						val2.SetColors(array17);
						if (flag2 && list4.Count == list.Count && list5.Count == list.Count)
						{
							val2.boneWeights = ConvertToBoneWeights(list4.ToArray(), list5.ToArray());
						}
						val2.subMeshCount = list6.Count;
						for (int num10 = 0; num10 < list6.Count; num10++)
						{
							val2.SetTriangles(list6[num10], num10);
						}
						array8[num] = list7.ToArray();
						flag = list2.Count == list.Count && list.Count > 0;
					}
					if (!flag)
					{
						val2.RecalculateNormals();
					}
					if (val2.vertexCount > 0 && val2.uv != null && val2.uv.Length == val2.vertexCount)
					{
						val2.RecalculateTangents();
					}
					val2.RecalculateBounds();
					array7[num] = val2;
				}
				GameObject[] array18 = (GameObject[])(object)new GameObject[array4.Length];
				for (int num11 = 0; num11 < array4.Length; num11++)
				{
					array18[num11] = new GameObject(array4[num11].name ?? $"Node_{num11}");
				}
				for (int num12 = 0; num12 < array4.Length; num12++)
				{
					GLTFNode gLTFNode = array4[num12];
					GameObject val6 = array18[num12];
					if (gLTFNode.translation != null && gLTFNode.translation.Length == 3)
					{
						val6.transform.localPosition = new Vector3(gLTFNode.translation[0], gLTFNode.translation[1], gLTFNode.translation[2]);
					}
					if (gLTFNode.rotation != null && gLTFNode.rotation.Length == 4)
					{
						val6.transform.localRotation = new Quaternion(gLTFNode.rotation[0], gLTFNode.rotation[1], gLTFNode.rotation[2], gLTFNode.rotation[3]);
					}
					if (gLTFNode.scale != null && gLTFNode.scale.Length == 3)
					{
						val6.transform.localScale = new Vector3(gLTFNode.scale[0], gLTFNode.scale[1], gLTFNode.scale[2]);
					}
				}
				for (int num13 = 0; num13 < array4.Length; num13++)
				{
					GLTFNode gLTFNode2 = array4[num13];
					GameObject val7 = array18[num13];
					if (gLTFNode2.children == null)
					{
						continue;
					}
					int[] children = gLTFNode2.children;
					foreach (int num14 in children)
					{
						if (num14 >= 0 && num14 < array18.Length)
						{
							array18[num14].transform.SetParent(val7.transform, false);
						}
					}
				}
				for (int num15 = 0; num15 < array4.Length; num15++)
				{
					GLTFNode gLTFNode3 = array4[num15];
					GameObject val8 = array18[num15];
					if (gLTFNode3.mesh < 0 || gLTFNode3.mesh >= array7.Length)
					{
						continue;
					}
					Mesh val9 = array7[gLTFNode3.mesh];
					Material[] sharedMaterials = (Material[])(((object)array8[gLTFNode3.mesh]) ?? ((object)new Material[1] { CreateFallbackMaterial(shader) }));
					if (gLTFNode3.skin >= 0 && gLTFNode3.skin < array5.Length && array5[gLTFNode3.skin].joints != null && array5[gLTFNode3.skin].joints.Length != 0 && val9.boneWeights != null && val9.boneWeights.Length != 0)
					{
						GLTFSkin gLTFSkin = array5[gLTFNode3.skin];
						Transform[] array19 = (Transform[])(object)new Transform[gLTFSkin.joints.Length];
						for (int num16 = 0; num16 < gLTFSkin.joints.Length; num16++)
						{
							int num17 = gLTFSkin.joints[num16];
							if (num17 >= 0 && num17 < array18.Length)
							{
								array19[num16] = array18[num17].transform;
							}
						}
						if (gLTFSkin.inverseBindMatrices >= 0 && gLTFSkin.inverseBindMatrices < array.Length)
						{
							Matrix4x4[] array20 = ReadMat4Accessor(array[gLTFSkin.inverseBindMatrices], bufferViews, binData);
							if (array20 != null && array20.Length == gLTFSkin.joints.Length)
							{
								val9.bindposes = array20;
							}
						}
						SkinnedMeshRenderer val10 = val8.AddComponent<SkinnedMeshRenderer>();
						val10.sharedMesh = val9;
						val10.bones = array19;
						if (array19.Length != 0 && (Object)(object)array19[0] != (Object)null)
						{
							val10.rootBone = array19[0];
						}
						((Renderer)val10).sharedMaterials = sharedMaterials;
						val10.updateWhenOffscreen = true;
						val10.quality = (SkinQuality)4;
					}
					else
					{
						Vector3 val11 = array9[gLTFNode3.mesh];
						if (((Vector3)(ref val11)).sqrMagnitude > 0f)
						{
							Transform transform = val8.transform;
							transform.localPosition += val8.transform.localRotation * val11;
						}
						val8.AddComponent<MeshFilter>().sharedMesh = val9;
						((Renderer)val8.AddComponent<MeshRenderer>()).sharedMaterials = sharedMaterials;
					}
				}
				bool flag3 = false;
				GLTFScene[] array21 = gLTFRoot.scenes ?? new GLTFScene[0];
				foreach (GLTFScene gLTFScene in array21)
				{
					if (gLTFScene.nodes == null)
					{
						continue;
					}
					int[] children = gLTFScene.nodes;
					foreach (int num19 in children)
					{
						if (num19 >= 0 && num19 < array18.Length)
						{
							array18[num19].transform.SetParent(val.transform, false);
							flag3 = true;
						}
					}
				}
				if (!flag3)
				{
					for (int num20 = 0; num20 < array18.Length; num20++)
					{
						if ((Object)(object)array18[num20].transform.parent == (Object)null)
						{
							array18[num20].transform.SetParent(val.transform, false);
						}
					}
				}
				return val;
			}
			catch (Exception arg)
			{
				DvergrSkyTramPlugin.Log.LogError((object)$"DrakkarGlb: Error parsing GLTF: {arg}");
				return null;
			}
		}

		private static Vector3[] ReadVec3Accessor(GLTFAccessor accessor, GLTFBufferView[] bufferViews, byte[] binData)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			if (accessor == null || accessor.bufferView < 0 || accessor.bufferView >= bufferViews.Length)
			{
				return null;
			}
			GLTFBufferView gLTFBufferView = bufferViews[accessor.bufferView];
			int num = gLTFBufferView.byteOffset + accessor.byteOffset;
			int count = accessor.count;
			int num2 = ((gLTFBufferView.byteStride > 0) ? gLTFBufferView.byteStride : 12);
			Vector3[] array = (Vector3[])(object)new Vector3[count];
			for (int i = 0; i < count; i++)
			{
				int num3 = num + i * num2;
				if (num3 + 12 <= binData.Length)
				{
					array[i] = new Vector3(BitConverter.ToSingle(binData, num3), BitConverter.ToSingle(binData, num3 + 4), BitConverter.ToSingle(binData, num3 + 8));
				}
			}
			return array;
		}

		private static Vector2[] ReadVec2Accessor(GLTFAccessor accessor, GLTFBufferView[] bufferViews, byte[] binData)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if (accessor == null || accessor.bufferView < 0 || accessor.bufferView >= bufferViews.Length)
			{
				return null;
			}
			GLTFBufferView gLTFBufferView = bufferViews[accessor.bufferView];
			int num = gLTFBufferView.byteOffset + accessor.byteOffset;
			int count = accessor.count;
			int num2 = ((gLTFBufferView.byteStride > 0) ? gLTFBufferView.byteStride : 8);
			Vector2[] array = (Vector2[])(object)new Vector2[count];
			for (int i = 0; i < count; i++)
			{
				int num3 = num + i * num2;
				if (num3 + 8 <= binData.Length)
				{
					float num4 = BitConverter.ToSingle(binData, num3);
					float num5 = BitConverter.ToSingle(binData, num3 + 4);
					array[i] = new Vector2(num4, 1f - num5);
				}
			}
			return array;
		}

		private static Vector4[] ReadVec4Accessor(GLTFAccessor accessor, GLTFBufferView[] bufferViews, byte[] binData)
		{
			//IL_00a2: 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_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			if (accessor == null || accessor.bufferView < 0 || accessor.bufferView >= bufferViews.Length)
			{
				return null;
			}
			GLTFBufferView gLTFBufferView = bufferViews[accessor.bufferView];
			int num = gLTFBufferView.byteOffset + accessor.byteOffset;
			int count = accessor.count;
			Vector4[] array = (Vector4[])(object)new Vector4[count];
			if (accessor.componentType == 5126)
			{
				int num2 = ((gLTFBufferView.byteStride > 0) ? gLTFBufferView.byteStride : 16);
				for (int i = 0; i < count; i++)
				{
					int num3 = num + i * num2;
					if (num3 + 16 <= binData.Length)
					{
						array[i] = new Vector4(BitConverter.ToSingle(binData, num3), BitConverter.ToSingle(binData, num3 + 4), BitConverter.ToSingle(binData, num3 + 8), BitConverter.ToSingle(binData, num3 + 12));
					}
				}
			}
			else if (accessor.componentType == 5121)
			{
				int num4 = ((gLTFBufferView.byteStride > 0) ? gLTFBufferView.byteStride : 4);
				for (int j = 0; j < count; j++)
				{
					int num5 = num + j * num4;
					if (num5 + 4 <= binData.Length)
					{
						array[j] = new Vector4((float)(int)binData[num5], (float)(int)binData[num5 + 1], (float)(int)binData[num5 + 2], (float)(int)binData[num5 + 3]);
					}
				}
			}
			else if (accessor.componentType == 5123)
			{
				int num6 = ((gLTFBufferView.byteStride > 0) ? gLTFBufferView.byteStride : 8);
				for (int k = 0; k < count; k++)
				{
					int num7 = num + k * num6;
					if (num7 + 8 <= binData.Length)
					{
						array[k] = new Vector4((float)(int)BitConverter.ToUInt16(binData, num7), (float)(int)BitConverter.ToUInt16(binData, num7 + 2), (float)(int)BitConverter.ToUInt16(binData, num7 + 4), (float)(int)BitConverter.ToUInt16(binData, num7 + 6));
					}
				}
			}
			return array;
		}

		private static Matrix4x4[] ReadMat4Accessor(GLTFAccessor accessor, GLTFBufferView[] bufferViews, byte[] binData)
		{
			//IL_006a: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if (accessor == null || accessor.bufferView < 0 || accessor.bufferView >= bufferViews.Length)
			{
				return null;
			}
			GLTFBufferView gLTFBufferView = bufferViews[accessor.bufferView];
			int num = gLTFBufferView.byteOffset + accessor.byteOffset;
			int count = accessor.count;
			int num2 = ((gLTFBufferView.byteStride > 0) ? gLTFBufferView.byteStride : 64);
			Matrix4x4[] array = (Matrix4x4[])(object)new Matrix4x4[count];
			for (int i = 0; i < count; i++)
			{
				int num3 = num + i * num2;
				if (num3 + 64 > binData.Length)
				{
					continue;
				}
				Matrix4x4 identity = Matrix4x4.identity;
				for (int j = 0; j < 4; j++)
				{
					for (int k = 0; k < 4; k++)
					{
						((Matrix4x4)(ref identity))[k, j] = BitConverter.ToSingle(binData, num3 + (j * 4 + k) * 4);
					}
				}
				array[i] = identity;
			}
			return array;
		}

		private static BoneWeight[] ConvertToBoneWeights(Vector4[] joints, Vector4[] weights)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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_0068: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
			if (joints == null || weights == null)
			{
				return null;
			}
			int num = Mathf.Min(joints.Length, weights.Length);
			BoneWeight[] array = (BoneWeight[])(object)new BoneWeight[num];
			for (int i = 0; i < num; i++)
			{
				Vector4 val = joints[i];
				Vector4 val2 = weights[i];
				((BoneWeight)(ref array[i])).boneIndex0 = Mathf.RoundToInt(val.x);
				((BoneWeight)(ref array[i])).boneIndex1 = Mathf.RoundToInt(val.y);
				((BoneWeight)(ref array[i])).boneIndex2 = Mathf.RoundToInt(val.z);
				((BoneWeight)(ref array[i])).boneIndex3 = Mathf.RoundToInt(val.w);
				((BoneWeight)(ref array[i])).weight0 = val2.x;
				((BoneWeight)(ref array[i])).weight1 = val2.y;
				((BoneWeight)(ref array[i])).weight2 = val2.z;
				((BoneWeight)(ref array[i])).weight3 = val2.w;
			}
			return array;
		}

		private static int[] ReadIndicesAccessor(GLTFAccessor accessor, GLTFBufferView[] bufferViews, byte[] binData)
		{
			if (accessor == null || accessor.bufferView < 0 || accessor.bufferView >= bufferViews.Length)
			{
				return null;
			}
			GLTFBufferView gLTFBufferView = bufferViews[accessor.bufferView];
			int num = gLTFBufferView.byteOffset + accessor.byteOffset;
			int count = accessor.count;
			int[] array = new int[count];
			if (accessor.componentType == 5123)
			{
				int num2 = ((gLTFBufferView.byteStride > 0) ? gLTFBufferView.byteStride : 2);
				for (int i = 0; i < count; i++)
				{
					int num3 = num + i * num2;
					if (num3 + 2 <= binData.Length)
					{
						array[i] = BitConverter.ToUInt16(binData, num3);
					}
				}
			}
			else if (accessor.componentType == 5125)
			{
				int num4 = ((gLTFBufferView.byteStride > 0) ? gLTFBufferView.byteStride : 4);
				for (int j = 0; j < count; j++)
				{
					int num5 = num + j * num4;
					if (num5 + 4 <= binData.Length)
					{
						array[j] = BitConverter.ToInt32(binData, num5);
					}
				}
			}
			else if (accessor.componentType == 5121)
			{
				for (int k = 0; k < count; k++)
				{
					int num6 = num + k;
					if (num6 < binData.Length)
					{
						array[k] = binData[num6];
					}
				}
			}
			return array;
		}

		internal static void Solidify(Renderer renderer)
		{
			if (!((Object)(object)renderer == (Object)null))
			{
				ApplySolid(renderer);
			}
		}

		private static void ApplySolid(Renderer renderer)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_0069: 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)
			if ((Object)(object)renderer == (Object)null)
			{
				return;
			}
			Shader val = Shader.Find("Unlit/Color") ?? Shader.Find("Sprites/Default") ?? Shader.Find("UI/Default");
			if (!((Object)(object)val == (Object)null))
			{
				Material val2 = new Material(val);
				((Object)val2).name = "SkyTramIron";
				Color val3 = default(Color);
				((Color)(ref val3))..ctor(0.32f, 0.33f, 0.35f, 1f);
				val2.color = val3;
				if (val2.HasProperty("_Color"))
				{
					val2.SetColor("_Color", val3);
				}
				renderer.sharedMaterial = val2;
				renderer.enabled = true;
			}
		}

		internal static void RepairShaders()
		{
			if (s_shaderReady || s_pending.Count == 0)
			{
				return;
			}
			Material val = LiveCreature();
			if (!((Object)(object)val == (Object)null))
			{
				for (int i = 0; i < s_pending.Count; i++)
				{
					Paint(s_pending[i].Material, val, s_pending[i].Albedo);
				}
				for (int j = 0; j < s_solid.Count; j++)
				{
					ApplySolid(s_solid[j]);
				}
				DvergrSkyTramPlugin.Log.LogInfo((object)("Sky-Tram: model shader is " + ((Object)val.shader).name + " on " + s_pending.Count + " part(s)."));
				s_pending.Clear();
				s_solid.Clear();
				s_shaderReady = true;
			}
		}

		private static Material LiveCreature()
		{
			if ((Object)(object)s_liveCreature != (Object)null)
			{
				return s_liveCreature;
			}
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			string[] array = new string[5] { "Greydwarf", "Boar", "Deer", "Skeleton", "Wolf" };
			for (int i = 0; i < array.Length; i++)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(array[i]);
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
				for (int j = 0; j < componentsInChildren.Length; j++)
				{
					if ((Object)(object)componentsInChildren[j] == (Object)null)
					{
						continue;
					}
					Material[] sharedMaterials = componentsInChildren[j].sharedMaterials;
					if (sharedMaterials == null)
					{
						continue;
					}
					foreach (Material val in sharedMaterials)
					{
						if (!((Object)(object)val == (Object)null) && !((Object)(object)val.shader == (Object)null))
						{
							string name = ((Object)val.shader).name;
							if (name.IndexOf("Creature", StringComparison.Ordinal) >= 0 && name.IndexOf("InternalError", StringComparison.Ordinal) < 0 && !name.StartsWith("Hidden/", StringComparison.Ordinal))
							{
								s_liveCreature = val;
								return s_liveCreature;
							}
						}
					}
				}
			}
			return null;
		}

		private static void Paint(Material material, Material live, Texture albedo)
		{
			//IL_0052: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)material == (Object)null)
			{
				return;
			}
			if ((Object)(object)live != (Object)null && (Object)(object)live.shader != (Object)null)
			{
				material.shader = live.shader;
				CopyFloat(live, material, "_Hue");
				CopyFloat(live, material, "_Saturation");
				CopyFloat(live, material, "_Value");
			}
			material.color = Color.white;
			if (material.HasProperty("_Color"))
			{
				material.SetColor("_Color", Color.white);
			}
			if (material.HasProperty("_BaseColor"))
			{
				material.SetColor("_BaseColor", Color.white);
			}
			if (material.HasProperty("_Metallic"))
			{
				material.SetFloat("_Metallic", 0f);
			}
			if (material.HasProperty("_Glossiness"))
			{
				material.SetFloat("_Glossiness", 0.15f);
			}
			if (material.HasProperty("_Smoothness"))
			{
				material.SetFloat("_Smoothness", 0.15f);
			}
			if (material.HasProperty("_Cull"))
			{
				material.SetFloat("_Cull", 0f);
			}
			if ((Object)(object)albedo != (Object)null)
			{
				material.mainTexture = albedo;
				if (material.HasProperty("_MainTex"))
				{
					material.SetTexture("_MainTex", albedo);
				}
				if (material.HasProperty("_BaseMap"))
				{
					material.SetTexture("_BaseMap", albedo);
				}
				if (material.HasProperty("_Albedo"))
				{
					material.SetTexture("_Albedo", albedo);
				}
			}
			if (material.HasProperty("_BumpMap"))
			{
				material.SetTexture("_BumpMap", (Texture)null);
			}
			material.DisableKeyword("_NORMALMAP");
			material.shaderKeywords = Array.Empty<string>();
			material.renderQueue = 2000;
		}

		private static int[] BothSides(int[] indices)
		{
			int[] array = new int[indices.Length * 2];
			for (int i = 0; i < indices.Length; i++)
			{
				array[i] = indices[i];
			}
			for (int j = 0; j < indices.Length; j += 3)
			{
				int num = indices.Length + j;
				array[num] = indices[j];
				array[num + 1] = indices[j + 2];
				array[num + 2] = indices[j + 1];
			}
			return array;
		}

		private static void CopyFloat(Material from, Material to, string property)
		{
			if ((Object)(object)from != (Object)null && (Object)(object)to != (Object)null && from.HasProperty(property) && to.HasProperty(property))
			{
				to.SetFloat(property, from.GetFloat(property));
			}
		}

		private static Material CreateMaterial(GLTFMaterial mat, Shader shader, Texture2D baseTexture)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			Material val = LiveCreature();
			Material val2 = (Material)((!((Object)(object)val != (Object)null) || !((Object)(object)val.shader != (Object)null)) ? ((object)CreateBaseMaterial(shader)) : ((object)new Material(val.shader)));
			((Object)val2).name = mat.name ?? "GLTFMaterial";
			Paint(val2, val, (Texture)(object)baseTexture);
			if ((Object)(object)val == (Object)null)
			{
				s_pending.Add(new PendingMaterial
				{
					Material = val2,
					Albedo = (Texture)(object)baseTexture
				});
			}
			return val2;
		}

		private static Material CreateFallbackMaterial(Shader shader)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			Material obj = CreateBaseMaterial(shader);
			((Object)obj).name = "Drakkar_FallbackMat";
			obj.color = Color.white;
			return obj;
		}

		private static Material CreateBaseMaterial(Shader shader)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			if ((Object)(object)shader == (Object)null)
			{
				shader = FindValidShader();
			}
			return new Material(shader);
		}
	}
	public static class TramBoard
	{
		private static Texture2D s_white;

		private static GUIStyle s_title;

		private static GUIStyle s_body;

		private static float s_at = -999f;

		private static string s_text = string.Empty;

		private static readonly List<ZDO> s_found = new List<ZDO>(16);

		public static object Target;

		public static void Draw(Rect area)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			EnsureStyles();
			Refresh();
			GUI.color = new Color(0.09f, 0.1f, 0.13f, 0.95f);
			GUI.DrawTexture(area, (Texture)(object)s_white);
			GUI.color = Color.white;
			float num = 18f;
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(((Rect)(ref area)).x + num, ((Rect)(ref area)).y + num, ((Rect)(ref area)).width - num * 2f, 28f);
			GUI.Label(val, "Dvergr Sky-Tram", s_title);
			GUI.Label(new Rect(((Rect)(ref area)).x + num, ((Rect)(ref val)).yMax + 6f, ((Rect)(ref area)).width - num * 2f, ((Rect)(ref area)).height - 70f), s_text, s_body);
		}

		private static void Refresh()
		{
			if (Time.unscaledTime - s_at < 0.5f && s_text.Length > 0)
			{
				return;
			}
			s_at = Time.unscaledTime;
			ZDOMan instance = ZDOMan.instance;
			if (instance == null)
			{
				s_text = "The world is not loaded, so there are no docks to list.";
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("Docks");
			if (0 + AppendNodes(instance, stringBuilder, "DFG_SkyTramDepot", "Tram Depot") + AppendNodes(instance, stringBuilder, "DFG_SkyTramDock4", "Sky-Tram Quay") == 0)
			{
				stringBuilder.AppendLine("  No depots placed.");
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("Airships");
			if (0 + AppendShips(instance, stringBuilder, "DFG_SkyTram") + AppendShips(instance, stringBuilder, "DFG_SkyTramBig") == 0)
			{
				stringBuilder.AppendLine("  No airships built.");
			}
			stringBuilder.AppendLine();
			stringBuilder.Append("E on a dock opens it. Board is a button on the Ship tab. The larger airship is a little slower, with two seats and three turret mounts. The smaller one has one seat and one turret mount.");
			s_text = stringBuilder.ToString();
		}

		private static int AppendNodes(ZDOMan manager, StringBuilder text, string prefab, string fallback)
		{
			//IL_006a: 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_00b8: 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)
			Pull(manager, prefab);
			int num = 0;
			for (int i = 0; i < s_found.Count; i++)
			{
				ZDO val = s_found[i];
				if (val == null)
				{
					continue;
				}
				string value = val.GetString("tram_name", string.Empty);
				if (string.IsNullOrEmpty(value))
				{
					value = fallback;
				}
				int num2 = TramNode.BerthCountOf(val);
				int num3 = 0;
				for (int j = 0; j < num2; j++)
				{
					if (TramNode.Occupant(val, j) != null)
					{
						num3++;
					}
				}
				Vector3 position = val.GetPosition();
				text.Append("  ").Append(value).Append("   ")
					.Append(num3)
					.Append("/")
					.Append(num2)
					.Append(" berthed")
					.Append("   ")
					.Append(Mathf.RoundToInt(position.x))
					.Append(", ")
					.Append(Mathf.RoundToInt(position.z))
					.AppendLine();
				num++;
			}
			return num;
		}

		private static int AppendShips(ZDOMan manager, StringBuilder text, string prefab)
		{
			Pull(manager, prefab);
			int num = 0;
			for (int i = 0; i < s_found.Count; i++)
			{
				ZDO val = s_found[i];
				if (val != null)
				{
					string value = val.GetString("tram_name", string.Empty);
					if (string.IsNullOrEmpty(value))
					{
						value = (TramShip.IsSmallHull(val) ? "Small airship" : "Airship");
					}
					string value2 = val.GetInt("tram_state", 0) switch
					{
						3 => "in storage", 
						2 => "flying to " + val.GetString("tram_dst_name", "the next dock"), 
						_ => "docked", 
					};
					string value3 = (TramShip.IsSmallHull(val) ? "small" : "large");
					text.Append("  ").Append(value).Append("   ")
						.Append(value3)
						.Append("   ")
						.Append(value2)
						.AppendLine();
					num++;
				}
			}
			return num;
		}

		private static void Pull(ZDOMan manager, string prefab)
		{
			s_found.Clear();
			int num = 0;
			for (int i = 0; i < 64; i++)
			{
				if (manager.GetAllZDOsWithPrefabIterative(prefab, s_found, ref num))
				{
					break;
				}
			}
		}

		private static void EnsureStyles()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)s_white == (Object)null)
			{
				s_white = new Texture2D(1, 1, (TextureFormat)4, false);
				s_white.SetPixel(0, 0, Color.white);
				s_white.Apply();
				((Object)s_white).hideFlags = (HideFlags)61;
			}
			if (s_title == null)
			{
				s_title = new GUIStyle(GUI.skin.label);
				s_title.fontSize = 22;
				s_title.fontStyle = (FontStyle)1;
				s_title.normal.textColor = new Color(1f, 0.84f, 0.38f);
				s_title.wordWrap = false;
			}
			if (s_body == null)
			{
				s_body = new GUIStyle(GUI.skin.label);
				s_body.fontSize = 15;
				s_body.normal.textColor = new Color(0.86f, 0.87f, 0.9f);
				s_body.wordWrap = true;
			}
		}
	}
	internal static class TramDockWeb
	{
		private static HttpListener s_listener;

		private static Thread s_thread;

		private static volatile bool s_running;

		private static readonly Queue<Action> s_main = new Queue<Action>();

		internal static void Start()
		{
			if (s_running)
			{
				return;
			}
			int num = ((DvergrSkyTramPlugin.WebPort != null) ? DvergrSkyTramPlugin.WebPort.Value : 8792);
			try
			{
				s_listener = new HttpListener();
				s_listener.Prefixes.Add("http://127.0.0.1:" + num + "/");
				s_listener.Start();
				s_running = true;
				s_thread = new Thread(Loop)
				{
					IsBackground = true,
					Name = "SkyTramDockWeb"
				};
				s_thread.Start();
				DvergrSkyTramPlugin.Log.LogInfo((object)("Sky-Tram: landing page at http://127.0.0.1:" + num + "/"));
			}
			catch (Exception ex)
			{
				s_running = false;
				DvergrSkyTramPlugin.Log.LogWarning((object)("Sky-Tram: landing page failed to bind port " + num + ": " + ex.Message));
			}
		}

		internal static void Stop()
		{
			s_running = false;
			try
			{
				if (s_listener != null)
				{
					s_listener.Stop();
					s_listener.Close();
				}
			}
			catch
			{
			}
			s_listener = null;
		}

		internal static void Drain()
		{
			while (true)
			{
				Action action = null;
				lock (s_main)
				{
					if (s_main.Count > 0)
					{
						action = s_main.Dequeue();
					}
				}
				if (action == null)
				{
					break;
				}
				try
				{
					action();
				}
				catch (Exception ex)
				{
					DvergrSkyTramPlugin.Log.LogWarning((object)("Sky-Tram: landing page update failed: " + ex.Message));
				}
			}
		}

		internal static void ApplySaved()
		{
			if (TryParse((DvergrSkyTramPlugin.DockPads != null) ? DvergrSkyTramPlugin.DockPads.Value : null, out var pads, out var yaws))
			{
				CopyToLive(pads, yaws);
			}
		}

		private static void Loop()
		{
			while (s_running && s_listener != null && s_listener.IsListening)
			{
				try
				{
					HttpListenerContext context = s_listener.GetContext();
					ThreadPool.QueueUserWorkItem(delegate
					{
						Handle(context);
					});
				}
				catch
				{
					if (s_running)
					{
						Thread.Sleep(50);
					}
				}
			}
		}

		private static void Handle(HttpListenerContext context)
		{
			try
			{
				context.Response.AddHeader("Cache-Control", "no-store");
				string absolutePath = context.Request.Url.AbsolutePath;
				switch (absolutePath)
				{
				case "/":
				case "/index.html":
					Serve(context, Encoding.UTF8.GetBytes(Page()), "text/html; charset=utf-8");
					return;
				case "/state":
					Serve(context, Encoding.UTF8.GetBytes(State()), "application/json");
					return;
				case "/apply":
				{
					if (!(context.Request.HttpMethod == "POST"))
					{
						break;
					}
					if (!TryParse(ReadBody(context), out var pads, out var yaws))
					{
						context.Response.StatusCode = 400;
						Serve(context, Encoding.UTF8.GetBytes("{\"status\":\"bad\"}"), "application/json");
						return;
					}
					lock (s_main)
					{
						s_main.Enqueue(delegate
						{
							CopyToLive(pads, yaws);
						});
					}
					Serve(context, Encoding.UTF8.GetBytes("{\"status\":\"ok\"}"), "application/json");
					return;
				}
				}
				if (absolutePath == "/save" && context.Request.HttpMethod == "POST")
				{
					lock (s_main)
					{
						s_main.Enqueue(Save);
					}
					Serve(context, Encoding.UTF8.GetBytes("{\"status\":\"saved\"}"), "application/json");
				}
				else if (absolutePath == "/reset" && context.Request.HttpMethod == "POST")
				{
					lock (s_main)
					{
						s_main.Enqueue(Reset);
					}
					Serve(context, Encoding.UTF8.GetBytes("{\"status\":\"reset\"}"), "application/json");
				}
				else
				{
					context.Response.StatusCode = 404;
					context.Response.Close();
				}
			}
			catch (Exception ex)
			{
				try
				{
					context.Response.StatusCode = 500;
					byte[] bytes = Encoding.UTF8.GetBytes(ex.Message);
					context.Response.OutputStream.Write(bytes, 0, bytes.Length);
					context.Response.Close();
				}
				catch
				{
				}
			}
		}

		private static string ReadBody(HttpListenerContext context)
		{
			using StreamReader streamReader = new StreamReader(context.Request.InputStream, Encoding.UTF8);
			return streamReader.ReadToEnd();
		}

		private static void Serve(HttpListenerContext context, byte[] bytes, string contentType)
		{
			context.Response.ContentType = contentType;
			context.Response.ContentLength64 = bytes.Length;
			context.Response.OutputStream.Write(bytes, 0, bytes.Length);
			context.Response.OutputStream.Flush();
			context.Response.Close();
		}

		private static void CopyToLive(Vector3[] pads, float[] yaws)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (pads != null)
			{
				for (int i = 0; i < 4 && i < pads.Length; i++)
				{
					TramPiece.Dock4Pads[i] = pads[i];
					TramPiece.Dock4Yaws[i] = ((yaws != null && i < yaws.Length) ? yaws[i] : 0f);
				}
			}
		}

		private static void Save()
		{
			if (DvergrSkyTramPlugin.DockPads == null)
			{
				return;
			}
			bool flag = false;
			for (int i = 0; i < 4; i++)
			{
				if (((Vector3)(ref TramPiece.Dock4Pads[i])).sqrMagnitude > 0.01f)
				{
					flag = true;
				}
			}
			if (flag)
			{
				DvergrSkyTramPlugin.DockPads.Value = Format(TramPiece.Dock4Pads);
			}
		}

		private static void Reset()
		{
			TramPiece.CopyMeasuredPads();
			if (DvergrSkyTramPlugin.DockPads != null)
			{
				DvergrSkyTramPlugin.DockPads.Value = "";
			}
		}

		internal static string Format(Vector3[] pads)
		{
			StringBuilder stringBuilder = new StringBuilder();
			int num = ((pads != null) ? pads.Length : 0);
			for (int i = 0; i < num; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(';');
				}
				stringBuilder.Append(pads[i].x.ToString("0.00", CultureInfo.InvariantCulture)).Append(',');
				stringBuilder.Append(pads[i].y.ToString("0.00", CultureInfo.InvariantCulture)).Append(',');
				stringBuilder.Append(pads[i].z.ToString("0.00", CultureInfo.InvariantCulture)).Append(',');
				stringBuilder.Append(((i < TramPiece.Dock4Yaws.Length) ? TramPiece.Dock4Yaws[i] : 0f).ToString("0.#", CultureInfo.InvariantCulture));
			}
			return stringBuilder.ToString();
		}

		private static bool TryParse(string text, out Vector3[] pads, out float[] yaws)
		{
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			pads = null;
			yaws = null;
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			text = text.Trim();
			string[] array;
			if (text.StartsWith("{"))
			{
				int num = text.IndexOf("[[", StringComparison.Ordinal);
				int num2 = text.LastIndexOf("]]", StringComparison.Ordinal);
				if (num < 0 || num2 <= num)
				{
					return false;
				}
				text = text.Substring(num + 2, num2 - (num + 2));
				array = text.Split(new string[1] { "],[" }, StringSplitOptions.None);
			}
			else
			{
				array = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			}
			if (array.Length < 4)
			{
				return false;
			}
			pads = (Vector3[])(object)new Vector3[4];
			yaws = new float[4];
			for (int i = 0; i < 4; i++)
			{
				string[] array2 = array[i].Split(',');
				if (array2.Length < 3)
				{
					return false;
				}
				float result = 0f;
				if (!float.TryParse(array2[0].Trim().Trim('"'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
				{
					return false;
				}
				if (!float.TryParse(array2[1].Trim().Trim('"'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
				{
					return false;
				}
				if (!float.TryParse(array2[2].Trim().Trim('"'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result4))
				{
					return false;
				}
				if (array2.Length >= 4 && !float.TryParse(array2[3].Trim().Trim('"'), NumberStyles.Float, CultureInfo.InvariantCulture, out result))
				{
					return false;
				}
				pads[i] = new Vector3(result2, result3, result4);
				yaws[i] = result;
			}
			return true;
		}

		private static string State()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("{\"pads\":[");
			for (int i = 0; i < 4; i++)
			{
				Vector3 val = TramPiece.Dock4Pads[i];
				if (i > 0)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.Append('[');
				stringBuilder.Append(val.x.ToString("0.00", CultureInfo.InvariantCulture)).Append(',');
				stringBuilder.Append(val.y.ToString("0.00", CultureInfo.InvariantCulture)).Append(',');
				stringBuilder.Append(val.z.ToString("0.00", CultureInfo.InvariantCulture)).Append(',');
				stringBuilder.Append(TramPiece.Dock4Yaws[i].ToString("0.#", CultureInfo.InvariantCulture));
				stringBuilder.Append(']');
			}
			stringBuilder.Append("]}");
			return stringBuilder.ToString();
		}

		private static string Page()
		{
			return "<!doctype html><html><head><meta charset='utf-8'><title>Sky-Tram landings</title><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}\nbody{margin:0;background:#12171e;color:#e6e1d6;font-family:Segoe UI,Arial,sans-serif}main{max-width:1080px;margin:0 auto;padding:18px}h1{font-size:20px;font-weight:600;margin:16px 0 6px}p{color:#b7c0c8;line-height:1.4}table{border-collapse:collapse;width:100%;margin-top:12px}th,td{text-align:left;padding:6px 8px}th{color:#9aa6b2;font-weight:600;font-size:13px}.cell{display:flex;align-items:center;gap:4px}input{width:84px;background:#1c232c;color:#f2efe6;border:1px solid #33404c;border-radius:4px;padding:6px 8px}button{background:#3a3424;color:#f0d78c;border:1px solid #6a5a32;border-radius:4px;cursor:pointer}button.nudge{width:28px;height:30px;padding:0;font-size:18px;line-height:1}button.act{margin:14px 8px 0 0;padding:8px 14px}</style></head><body>" + WebNav.Html("skytram") + "<main><h1>Quay landing spots</h1><p>Each spot is in metres, in the quay's own space. X and Z move it across the deck. Y is the height the airship's keel sits at. Yaw is degrees added to the quay's own facing. Position steps by half a metre and yaw by 15 degrees. Save keeps it for the next launch.</p><table><thead><tr><th>Berth</th><th>X</th><th>Y</th><th>Z</th><th>Yaw</th></tr></thead><tbody id='rows'></tbody></table><button id='save' class='act' type='button'>Save</button><button id='reset' class='act' type='button'>Measured spots</button><p id='note'></p></main><script>const rows=document.getElementById('rows');const note=document.getElementById('note');function nudge(input,dir,amount){const n=Number(input.value)||0;const v=Math.round((n+dir*amount)*10)/10;input.value=amount>=1?String(Math.round(v)):v.toFixed(1);send();}function field(td,value,amount){const cell=document.createElement('div');cell.className='cell';const minus=document.createElement('button');minus.type='button';minus.className='nudge';minus.textContent='\\u2212';const input=document.createElement('input');input.type='number';input.step=String(amount);input.value=value;const plus=document.createElement('button');plus.type='button';plus.className='nudge';plus.textContent='+';minus.addEventListener('click',()=>nudge(input,-1,amount));plus.addEventListener('click',()=>nudge(input,1,amount));input.addEventListener('change',send);cell.appendChild(minus);cell.appendChild(input);cell.appendChild(plus);td.appendChild(cell);}function paint(pads){rows.innerHTML='';pads.forEach((p,i)=>{const tr=document.createElement('tr');tr.innerHTML='<td>'+(i+1)+'</td>';[0.5,0.5,0.5,15].forEach((amount,a)=>{const td=document.createElement('td');field(td,p[a]||0,amount);tr.appendChild(td);});rows.appendChild(tr);});}async function load(){const r=await fetch('/state');paint((await r.json()).pads);}async function send(){const pads=[...rows.querySelectorAll('tr')].map(tr=>[...tr.querySelectorAll('input')].map(el=>Number(el.value)));await fetch('/apply',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({pads})});note.textContent='Moved. Save to keep it.';}document.getElementById('save').onclick=async()=>{await fetch('/save',{method:'POST'});note.textContent='Saved.';};document.getElementById('reset').onclick=async()=>{await fetch('/reset',{method:'POST'});note.textContent='Back to the measured apron.';setTimeout(load,200);};load();</script></body></html>";
		}
	}
	public static class TramFlight
	{
		public const string FuelPrefab = "SurtlingCore";

		private static readonly List<Vector3> s_points = new List<Vector3>(16);

		private static readonly List<bool> s_cable = new List<bool>(16);

		private static readonly float[] s_a = new float[3];

		private static readonly float[] s_b = new float[3];

		private static readonly float[] s_into = new float[3];

		private static readonly List<ZDO> s_ships = new List<ZDO>(8);

		private static float s_unloadedNext;

		internal static bool IsAway(TramNode depot)
		{
			ZDO val = ShipOf(depot);
			if (val != null)
			{
				return val.GetInt("tram_state", 0) == 2;
			}
			return false;
		}

		internal static string DestinationName(TramNode depot)
		{
			ZDO val = ShipOf(depot);
			if (val == null)
			{
				return "the next depot";
			}
			string text = val.GetString("tram_dst_name", string.Empty);
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return "the next depot";
		}

		internal static string DispatchOnServer(long sender, TramNode origin, ZDOID destId)
		{
			//IL_004c: 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_0071: 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_00a3: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)origin == (Object)null || !origin.IsDepot)
			{
				return "That depot is not loaded here.";
			}
			if (!origin.HasAirship)
			{
				return "This depot has no airship. B builds one.";
			}
			if (IsAway(origin))
			{
				return "The airship is already on its way to " + DestinationName(origin) + ".";
			}
			if (((ZDOID)(ref destId)).IsNone() || destId == origin.Id)
			{
				return "Pick another depot.";
			}
			ZDO val = ShipOf(origin);
			if (val == null)
			{
				return "The airship is no longer in the world.";
			}
			if (!TramGraph.TryPath(origin.Id, destId, out var path, out var metres, out var hops))
			{
				return "Those depots are not on the same cables.";
			}
			TramGraph.Stop stop = null;
			for (int i = 0; i < path.Length; i++)
			{
				TramGraph.Stop stop2 = TramGraph.At(path[i]);
				if (stop2 != null && stop2.Id == destId)
				{
					stop = stop2;
				}
			}
			if (stop == null || !stop.Depot)
			{
				return "That is not a depot.";
			}
			ZDOMan instance = ZDOMan.instance;
			ZDO val2 = ((instance != null) ? instance.GetZDO(destId) : null);
			if (val2 == null)
			{
				return "That depot is not in the world.";
			}
			int num = TramNode.ClaimBerth(val2, val.m_uid);
			if (num < 0)
			{
				return "Every berth at " + stop.Name + " is full.";
			}
			val.Set("tram_berth", num);
			if (!Bake(val, path, stop))
			{
				TramNode.ReleaseBerth(val2, val.m_uid);
				return "That route has nowhere for the airship to fly.";
			}
			float fuelPerKm = FuelRate();
			int num2 = TramRouter.CoresForMetres(metres, fuelPerKm);
			Player player = PlayerOf(sender);
			string text = TramHold.Take(val, player, num2);
			if (text != null)
			{
				val.Set("tram_path", string.Empty);
				TramNode.ReleaseBerth(val2, val.m_uid);
				return text;
			}
			val.Set("tram_dst", destId);
			val.Set("tram_dst_name", stop.Name);
			val.Set("tram_travel", 0f);
			val.Set("tram_state", 2);
			int num3 = Mathf.Max(1, Mathf.RoundToInt(metres));
			DvergrSkyTramPlugin.Log.LogInfo((object)("Sky-Tram: airship dispatched from " + origin.DisplayName + " to " + stop.Name + " (" + num3 + " m, " + hops + " hops, " + num2 + " cores)."));
			return "The airship is away to " + stop.Name + " (" + num3 + " m, " + num2 + " " + ((num2 == 1) ? "core" : "cores") + ").";
		}

		internal static string FlightStatus(TramNode depot)
		{
			ZDO val = ShipOf(depot);
			if (val == null || val.GetInt("tram_state", 0) != 2)
			{
				return null;
			}
			string text = DestinationName(depot);
			if (val.GetFloat("tram_travel", 0f) < 2f)
			{
				return "Sending the airship to " + text + ".";
			}
			return "Flying to " + text + ".";
		}

		internal static void Advance(TramShip ship, float dt)
		{
			if (OnServer() && !((Object)(object)ship == (Object)null))
			{
				ZDO zdo = ship.Zdo;
				if (zdo != null && zdo.GetInt("tram_state", 0) == 2)
				{
					Step(zdo, ship, dt);
				}
			}
		}

		internal static void HoldCourse(TramShip ship)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: 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_007b: 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_007d: 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)
			if (!((Object)(object)ship == (Object)null))
			{
				ZDO zdo = ship.Zdo;
				if (zdo != null && zdo.GetInt("tram_state", 0) == 2 && Parse(zdo.GetString("tram_path", string.Empty)))
				{
					float num = TotalLength();
					float num2 = Mathf.Clamp(zdo.GetFloat("tram_travel", 0f), 0f, num);
					Vector3 val = Sample(num2);
					Vector3 toward = Sample(Mathf.Min(num, num2 + 4f));
					Place(zdo, ship, val, Face(val, toward));
				}
			}
		}

		internal static void Tick()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			if (!OnServer())
			{
				return;
			}
			float deltaTime = Time.deltaTime;
			if (deltaTime > 0f)
			{
				TramShip.AdvanceAll(deltaTime);
			}
			if (Time.time >= s_unloadedNext)
			{
				s_unloadedNext = Time.time + 0.25f;
				if (ZDOMan.instance != null)
				{
					GatherShips(s_ships);
				}
			}
			if (deltaTime <= 0f)
			{
				return;
			}
			for (int i = 0; i < s_ships.Count; i++)
			{
				ZDO val = s_ships[i];
				if (val != null && val.GetInt("tram_state", 0) == 2 && !((Object)(object)TramShip.Find(val.m_uid) != (Object)null))
				{
					Step(val, null, deltaTime);
				}
			}
		}

		private static void Step(ZDO zdo, TramShip ship, float dt)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: 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)
			if (!Parse(zdo.GetString("tram_path", string.Empty)))
			{
				Recall(zdo);
				DvergrSkyTramPlugin.Log.LogWarning((object)"Sky-Tram: an airship was marked en route with no usable path, so it stays docked.");
				return;
			}
			float num = TotalLength();
			float num2 = zdo.GetFloat("tram_travel", 0f);
			float num3 = num2;
			if (num2 >= num - 0.05f)
			{
				Vector3 val = s_points[s_points.Count - 1];
				Place(zdo, ship, val, Face(val, val));
				if (TryDock(zdo))
				{
					AnnounceArrival(zdo);
				}
				else
				{
					zdo.Set("tram_travel", num);
				}
				return;
			}
			float num4 = Speed(zdo);
			float num5 = num - num2;
			float num6 = 1f;
			if (num2 < 8f)
			{
				num6 = Mathf.Max(0.25f, num2 / 8f);
			}
			if (num5 < 8f)
			{
				num6 = Mathf.Min(num6, Mathf.Max(0.25f, num5 / 8f));
			}
			num2 = Mathf.Min(num, num2 + num4 * num6 * Mathf.Max(0f, dt));
			zdo.Set("tram_travel", num2);
			if (num3 < 2f && num2 >= 2f)
			{
				string text = zdo.GetString("tram_dst_name", "the next depot");
				DvergrSkyTramPlugin.Log.LogInfo((object)("Sky-Tram: airship left the pad toward " + text + " (" + Mathf.RoundToInt(num) + " m)."));
			}
			Vector3 val2 = Sample(num2);
			Vector3 toward = Sample(Mathf.Min(num, num2 + 4f));
			Place(zdo, ship, val2, Face(val2, toward));
		}

		private static void Recall(ZDO zdo)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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)
			ZDOMan instance = ZDOMan.instance;
			ZDOID zDOID = zdo.GetZDOID("tram_dst");
			if (instance != null && !((ZDOID)(ref zDOID)).IsNone())
			{
				ZDO zDO = instance.GetZDO(zDOID);
				if (zDO != null)
				{
					TramNode.ReleaseBerth(zDO, zdo.m_uid);
				}
			}
			zdo.Set("tram_state", 0);
			zdo.Set("tram_path", string.Empty);
			zdo.Set("tram_travel", 0f);
			ZDOID zDOID2 = zdo.GetZDOID("tram_home");
			if (instance != null && !((ZDOID)(ref zDOID2)).IsNone())
			{
				ZDO zDO2 = instance.GetZDO(zDOID2);
				int num = ((zDO2 != null) ? TramNode.ClaimBerth(zDO2, zdo.m_uid) : (-1));
				if (num >= 0)
				{
					zdo.Set("tram_berth", num);
				}
			}
		}

		private static void AnnounceArrival(ZDO zdo)
		{
			string text = zdo.GetString("tram_dst_name", "the depot");
			string text2 = "The airship has arrived at " + text + ".";
			DvergrSkyTramPlugin.Log.LogInfo((object)("Sky-Tram: " + text2));
			Player val = Player.m_localPlayer;
			if ((Object)(object)val == (Object)null)
			{
				val = TramNode.LocalPlayer();
			}
			if ((Object)(object)val != (Object)null)
			{
				((Character)val).Message((MessageType)2, text2, 0, (Sprite)null, false);
			}
		}

		private static bool TryDock(ZDO ship)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			ZDOID zDOID = ship.GetZDOID("tram_dst");
			if (((ZDOID)(ref zDOID)).IsNone())
			{
				return false;
			}
			ZDOMan instance = ZDOMan.instance;
			if (instance == null)
			{
				return false;
			}
			ZDO zDO = instance.GetZDO(zDOID);
			if (zDO == null)
			{
				return false;
			}
			ZDOID zDOID2 = ship.GetZDOID("tram_home");
			if (zDOID2 != zDOID)
			{
				ZDO zDO2 = instance.GetZDO(zDOID2);
				if (zDO2 != null)
				{
					TramNode.ReleaseBerth(zDO2, ship.m_uid);
				}
			}
			int num = TramNode.ClaimBerth(zDO, ship.m_uid);
			if (num >= 0)
			{
				ship.Set("tram_berth", num);
			}
			ship.Set("tram_home", zDOID);
			ship.Set("tram_state", 0);
			ship.Set("tram_path", string.Empty);
			ship.Set("tram_travel", 0f);
			return true;
		}

		internal static string CallOnServer(long sender, TramNode here)
		{
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: 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_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)here == (Object)null || !here.IsDepot)
			{
				return "That depot is not loaded here.";
			}
			if (!here.HasRoom)
			{
				return "Every berth here is full.";
			}
			ZDOMan instance = ZDOMan.instance;
			if (instance == null)
			{
				return "The world is not ready.";
			}
			List<ZDO> list = new List<ZDO>(8);
			GatherShips(list);
			ZDO val = null;
			float num = float.MaxValue;
			int[] path = null;
			TramGraph.Stop goal = null;
			string text = null;
			for (int i = 0; i < list.Count; i++)
			{
				ZDO val2 = list[i];
				if (val2 == null || val2.GetInt("tram_state", 0) == 3)
				{
					continue;
				}
				if (val2.GetInt("tram_state", 0) == 2)
				{
					if (val2.GetZDOID("tram_dst") == here.Id)
					{
						text = "The airship is already on its way here.";
					}
					else if (text == null)
					{
						text = "The airship is already on its way to " + DestinationNameOf(val2) + ".";
					}
					continue;
				}
				ZDOID zDOID = val2.GetZDOID("tram_home");
				if (((ZDOID)(ref zDOID)).IsNone() || zDOID == here.Id || !TramGraph.TryPath(zDOID, here.Id, out var path2, out var metres, out var _) || metres >= num)
				{
					continue;
				}
				TramGraph.Stop stop = null;
				for (int j = 0; j < path2.Length; j++)
				{
					TramGraph.Stop stop2 = TramGraph.At(path2[j]);
					if (stop2 != null && stop2.Id == here.Id)
					{
						stop = stop2;
					}
				}
				if (stop != null)
				{
					val = val2;
					num = metres;
					path = path2;
					goal = stop;
				}
			}
			if (val == null)
			{
				if (text == null)
				{
					return "No airship on these cables can reach this depot.";
				}
				return text;
			}
			ZDO zDO = instance.GetZDO(here.Id);
			int num2 = ((zDO != null) ? TramNode.ClaimBerth(zDO, val.m_uid) : (-1));
			if (num2 < 0)
			{
				return "Every berth here is full.";
			}
			val.Set("tram_berth", num2);
			if (!Bake(val, path, goal))
			{
				if (zDO != null)
				{
					TramNode.ReleaseBerth(zDO, val.m_uid);
				}
				return "That route has nowhere for the airship to fly.";
			}
			int num3 = TramRouter.CoresForMetres(num, FuelRate());
			string text2 = TramHold.Take(val, PlayerOf(sender), num3);
			if (text2 != null)
			{
				val.Set("tram_path", string.Empty);
				if (zDO != null)
				{
					TramNode.ReleaseBerth(zDO, val.m_uid);
				}
				return text2;
			}
			val.Set("tram_dst", here.Id);
			val.Set("tram_dst_name", here.DisplayName);
			val.Set("tram_travel", 0f);
			val.Set("tram_state", 2);
			int num4 = Mathf.Max(1, Mathf.RoundToInt(num));
			DvergrSkyTramPlugin.Log.LogInfo((object)("Sky-Tram: airship called to " + here.DisplayName + " (" + num4 + " m, " + num3 + " cores)."));
			return "The airship is coming (" + num4 + " m, " + num3 + " " + ((num3 == 1) ? "core" : "cores") + ").";
		}

		private static string DestinationNameOf(ZDO ship)
		{
			string text = ((ship != null) ? ship.GetString("tram_dst_name", string.Empty) : string.Empty);
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return "another depot";
		}

		private static bool Bake(ZDO ship, int[] path, TramGraph.Stop goal)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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)
			//IL_007a: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			s_points.Clear();
			s_cable.Clear();
			TramShip tramShip = TramShip.Find(ship.m_uid);
			Vector3 val = (((Object)(object)tramShip != (Object)null) ? ((Component)tramShip).transform.position : ship.GetPosition());
			Add(val, cable: false);
			for (int i = 0; i < path.Length; i++)
			{
				TramGraph.Stop stop = TramGraph.At(path[i]);
				if (stop != null)
				{
					Add(stop.Anchor, cable: true);
				}
			}
			Vector3 approach = ((s_points.Count > 0) ? s_points[s_points.Count - 1] : val);
			Add(DockPoint(goal, approach, ship), cable: false);
			if (s_points.Count < 2)
			{
				return false;
			}
			ship.Set("tram_path", Encode());
			return true;
		}

		private sta