Decompiled source of Ravengates v1.3.0

plugins/Ravengates.Core.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Microsoft.CodeAnalysis;
using Ravengates.Core;
using Ravengates.Mathematics;
using Ravengates.Physics;

[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("Ravengates.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyInformationalVersion("1.3.0")]
[assembly: AssemblyProduct("Ravengates.Core")]
[assembly: AssemblyTitle("Ravengates.Core")]
[assembly: AssemblyVersion("1.3.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Ravengates.Streaming
{
	public enum RemoteInterestTier
	{
		RenderOnly,
		Interactive,
		CrossingReady
	}
	[Flags]
	public enum PortalRemoteSimulation
	{
		None = 0,
		ExistingCharacters = 1,
		ExistingRigidbodies = 2,
		Physics = 4,
		Projectiles = 8,
		AI = 0x10,
		Spawners = 0x20,
		Environment = 0x40,
		Full = 0x7F
	}
	public readonly struct ZoneCoordinate : IEquatable<ZoneCoordinate>
	{
		public int X { get; }

		public int Y { get; }

		public ZoneCoordinate(int x, int y)
		{
			X = x;
			Y = y;
		}

		public static ZoneCoordinate FromWorld(Vector3 position, float zoneSize = 64f)
		{
			if (zoneSize <= 0f)
			{
				throw new ArgumentOutOfRangeException("zoneSize");
			}
			float num = zoneSize * 0.5f;
			return new ZoneCoordinate((int)Math.Floor((position.X + num) / zoneSize), (int)Math.Floor((position.Z + num) / zoneSize));
		}

		public bool Equals(ZoneCoordinate other)
		{
			if (X == other.X)
			{
				return Y == other.Y;
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is ZoneCoordinate other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (X * 397) ^ Y;
		}

		public override string ToString()
		{
			return $"{X},{Y}";
		}
	}
	public readonly struct RemoteInterestRequest
	{
		public string OwnerKey { get; }

		public string SourcePortalId { get; }

		public string DestinationPortalId { get; }

		public Vector3 DestinationPosition { get; }

		public int Radius { get; }

		public RemoteInterestTier Tier { get; }

		public PortalRemoteSimulation Simulation { get; }

		public RemoteInterestRequest(string ownerKey, string sourcePortalId, string destinationPortalId, Vector3 destinationPosition, int radius, RemoteInterestTier tier, PortalRemoteSimulation simulation)
		{
			OwnerKey = ownerKey ?? throw new ArgumentNullException("ownerKey");
			SourcePortalId = sourcePortalId ?? throw new ArgumentNullException("sourcePortalId");
			DestinationPortalId = destinationPortalId ?? throw new ArgumentNullException("destinationPortalId");
			DestinationPosition = destinationPosition;
			Radius = Math.Max(0, radius);
			Tier = tier;
			Simulation = simulation;
		}
	}
	public sealed class RemoteZoneInterest
	{
		public ZoneCoordinate Zone { get; }

		public int ReferenceCount { get; internal set; }

		public RemoteInterestTier Tier { get; internal set; }

		public PortalRemoteSimulation Simulation { get; internal set; }

		public double UnloadAt { get; internal set; }

		public bool IsGraceUnloading
		{
			get
			{
				if (ReferenceCount == 0)
				{
					return !double.IsPositiveInfinity(UnloadAt);
				}
				return false;
			}
		}

		internal RemoteZoneInterest(ZoneCoordinate zone)
		{
			Zone = zone;
			UnloadAt = double.PositiveInfinity;
		}
	}
	public sealed class PortalInterestManager
	{
		private readonly Dictionary<ZoneCoordinate, RemoteZoneInterest> _zones = new Dictionary<ZoneCoordinate, RemoteZoneInterest>();

		private readonly Dictionary<string, HashSet<ZoneCoordinate>> _owners = new Dictionary<string, HashSet<ZoneCoordinate>>(StringComparer.Ordinal);

		private readonly Stack<HashSet<ZoneCoordinate>> _freeSets = new Stack<HashSet<ZoneCoordinate>>();

		private readonly HashSet<ZoneCoordinate> _requested = new HashSet<ZoneCoordinate>();

		private readonly List<ZoneCoordinate> _expired = new List<ZoneCoordinate>();

		public int MaxRemoteZones { get; private set; }

		public double UnloadGraceSeconds { get; private set; }

		public float ZoneSize { get; }

		public IEnumerable<RemoteZoneInterest> ActiveZones => _zones.Values;

		public PortalInterestManager(int maxRemoteZones = 49, double unloadGraceSeconds = 10.0, float zoneSize = 64f)
		{
			if (maxRemoteZones < 1)
			{
				throw new ArgumentOutOfRangeException("maxRemoteZones");
			}
			if (unloadGraceSeconds < 0.0)
			{
				throw new ArgumentOutOfRangeException("unloadGraceSeconds");
			}
			if (zoneSize <= 0f)
			{
				throw new ArgumentOutOfRangeException("zoneSize");
			}
			MaxRemoteZones = maxRemoteZones;
			UnloadGraceSeconds = unloadGraceSeconds;
			ZoneSize = zoneSize;
		}

		public void Configure(int maxRemoteZones, double unloadGraceSeconds)
		{
			if (maxRemoteZones < 1)
			{
				throw new ArgumentOutOfRangeException("maxRemoteZones");
			}
			if (double.IsNaN(unloadGraceSeconds) || double.IsInfinity(unloadGraceSeconds) || unloadGraceSeconds < 0.0)
			{
				throw new ArgumentOutOfRangeException("unloadGraceSeconds");
			}
			MaxRemoteZones = maxRemoteZones;
			UnloadGraceSeconds = unloadGraceSeconds;
		}

		public void Clear()
		{
			_owners.Clear();
			_zones.Clear();
		}

		public bool TryAcquire(RemoteInterestRequest request, double now)
		{
			HashSet<ZoneCoordinate> hashSet = CalculateZones(request, _requested);
			int num = hashSet.Count;
			if (_owners.TryGetValue(request.OwnerKey, out HashSet<ZoneCoordinate> value))
			{
				foreach (ZoneCoordinate item in value)
				{
					if (hashSet.Contains(item))
					{
						num--;
					}
				}
			}
			if (CountNonGraceZones() + Math.Max(0, num) > MaxRemoteZones)
			{
				return false;
			}
			Release(request.OwnerKey, now);
			HashSet<ZoneCoordinate> hashSet2 = ((_freeSets.Count > 0) ? _freeSets.Pop() : new HashSet<ZoneCoordinate>());
			foreach (ZoneCoordinate item2 in hashSet)
			{
				if (!_zones.TryGetValue(item2, out RemoteZoneInterest value2))
				{
					value2 = new RemoteZoneInterest(item2);
					_zones.Add(item2, value2);
				}
				bool flag = value2.ReferenceCount == 0;
				value2.ReferenceCount++;
				value2.Tier = (flag ? request.Tier : ((RemoteInterestTier)Math.Max((int)value2.Tier, (int)request.Tier)));
				value2.Simulation = (flag ? request.Simulation : (value2.Simulation | request.Simulation));
				value2.UnloadAt = double.PositiveInfinity;
				hashSet2.Add(item2);
			}
			_owners[request.OwnerKey] = hashSet2;
			return true;
		}

		public void Release(string ownerKey, double now)
		{
			if (!_owners.TryGetValue(ownerKey, out HashSet<ZoneCoordinate> value))
			{
				return;
			}
			foreach (ZoneCoordinate item in value)
			{
				if (_zones.TryGetValue(item, out RemoteZoneInterest value2))
				{
					value2.ReferenceCount = Math.Max(0, value2.ReferenceCount - 1);
					if (value2.ReferenceCount == 0)
					{
						value2.UnloadAt = now + UnloadGraceSeconds;
					}
				}
			}
			_owners.Remove(ownerKey);
			value.Clear();
			_freeSets.Push(value);
		}

		public int Tick(double now)
		{
			int num = 0;
			List<ZoneCoordinate> expired = _expired;
			expired.Clear();
			foreach (KeyValuePair<ZoneCoordinate, RemoteZoneInterest> zone in _zones)
			{
				if (zone.Value.ReferenceCount == 0 && zone.Value.UnloadAt <= now)
				{
					expired.Add(zone.Key);
				}
			}
			foreach (ZoneCoordinate item in expired)
			{
				_zones.Remove(item);
				num++;
			}
			return num;
		}

		public bool TryGet(ZoneCoordinate zone, out RemoteZoneInterest interest)
		{
			return _zones.TryGetValue(zone, out interest);
		}

		private HashSet<ZoneCoordinate> CalculateZones(RemoteInterestRequest request, HashSet<ZoneCoordinate> zones)
		{
			ZoneCoordinate zoneCoordinate = ZoneCoordinate.FromWorld(request.DestinationPosition, ZoneSize);
			zones.Clear();
			for (int i = zoneCoordinate.X - request.Radius; i <= zoneCoordinate.X + request.Radius; i++)
			{
				for (int j = zoneCoordinate.Y - request.Radius; j <= zoneCoordinate.Y + request.Radius; j++)
				{
					zones.Add(new ZoneCoordinate(i, j));
				}
			}
			return zones;
		}

		private int CountNonGraceZones()
		{
			int num = 0;
			foreach (RemoteZoneInterest value in _zones.Values)
			{
				if (value.ReferenceCount > 0)
				{
					num++;
				}
			}
			return num;
		}
	}
}
namespace Ravengates.Rendering
{
	public sealed class PortalRenderBudget
	{
		public int MaxPassesPerFrame { get; }

		public int MaxRecursionDepth { get; }

		public float MinPortalScreenPixels { get; }

		public int PassesUsed { get; private set; }

		public PortalRenderBudget(int maxPassesPerFrame = 6, int maxRecursionDepth = 2, float minPortalScreenPixels = 64f)
		{
			if (maxPassesPerFrame < 0)
			{
				throw new ArgumentOutOfRangeException("maxPassesPerFrame");
			}
			if (maxRecursionDepth < 0)
			{
				throw new ArgumentOutOfRangeException("maxRecursionDepth");
			}
			if (minPortalScreenPixels < 0f)
			{
				throw new ArgumentOutOfRangeException("minPortalScreenPixels");
			}
			MaxPassesPerFrame = maxPassesPerFrame;
			MaxRecursionDepth = maxRecursionDepth;
			MinPortalScreenPixels = minPortalScreenPixels;
		}

		public void BeginFrame()
		{
			PassesUsed = 0;
		}

		public bool TryAcquire(float projectedPixels, int requestedDepth, out int depth)
		{
			depth = 0;
			if (projectedPixels < MinPortalScreenPixels || PassesUsed >= MaxPassesPerFrame)
			{
				return false;
			}
			depth = Math.Max(0, Math.Min(MaxRecursionDepth, requestedDepth));
			PassesUsed++;
			return true;
		}
	}
	public readonly struct PortalRenderCandidate
	{
		public Portal Portal { get; }

		public float ProjectedPixels { get; }

		public int RequestedDepth { get; }

		public float Priority { get; }

		public PortalRenderCandidate(Portal portal, float projectedPixels, int requestedDepth, float priority)
		{
			Portal = portal;
			ProjectedPixels = projectedPixels;
			RequestedDepth = requestedDepth;
			Priority = priority;
		}
	}
	public readonly struct PortalRenderPass
	{
		public Portal Portal { get; }

		public int RecursionDepth { get; }

		public float ResolutionScale { get; }

		public PortalRenderPass(Portal portal, int recursionDepth, float resolutionScale)
		{
			Portal = portal;
			RecursionDepth = recursionDepth;
			ResolutionScale = resolutionScale;
		}
	}
	public sealed class PortalRenderManager
	{
		private readonly PortalRenderBudget _budget;

		public PortalRenderManager(PortalRenderBudget budget)
		{
			_budget = budget ?? throw new ArgumentNullException("budget");
		}

		public IReadOnlyList<PortalRenderPass> BuildPlan(IEnumerable<PortalRenderCandidate> candidates)
		{
			List<PortalRenderCandidate> list = new List<PortalRenderCandidate>(candidates);
			list.Sort((PortalRenderCandidate a, PortalRenderCandidate b) => b.Priority.CompareTo(a.Priority));
			List<PortalRenderPass> list2 = new List<PortalRenderPass>();
			_budget.BeginFrame();
			foreach (PortalRenderCandidate item in list)
			{
				if (_budget.TryAcquire(item.ProjectedPixels, item.RequestedDepth, out var depth))
				{
					float resolutionScale = ((depth == 0) ? 1f : Math.Max(0.35f, 1f - (float)depth * 0.2f));
					list2.Add(new PortalRenderPass(item.Portal, depth, resolutionScale));
				}
			}
			return list2;
		}
	}
	public sealed class PortalViewHistory
	{
		private readonly Dictionary<string, string> _signatures = new Dictionary<string, string>(StringComparer.Ordinal);

		public bool BeginView(string viewKey, PortalPath path)
		{
			if (!_signatures.TryGetValue(viewKey, out string value) || value != path.Signature)
			{
				_signatures[viewKey] = path.Signature;
				return true;
			}
			return false;
		}

		public void Invalidate(string viewKey)
		{
			_signatures.Remove(viewKey);
		}

		public void Clear()
		{
			_signatures.Clear();
		}
	}
	public enum PortalViewTier
	{
		High,
		Medium,
		Low,
		Minimal
	}
	public readonly struct PortalViewSchedule
	{
		public PortalViewTier Tier { get; }

		public float ResolutionScale { get; }

		public int FrameInterval { get; }

		public PortalViewSchedule(PortalViewTier tier, float resolutionScale, int frameInterval)
		{
			Tier = tier;
			ResolutionScale = resolutionScale;
			FrameInterval = Math.Max(1, frameInterval);
		}
	}
	public static class PortalViewScheduler
	{
		public const float HighCoverage = 0.35f;

		public const float MediumCoverage = 0.12f;

		public const float EveryFrameMediumCoverage = 0.2f;

		public const int LowTierInterval = 4;

		public const int MinimalTierInterval = 8;

		public static PortalViewSchedule Select(float screenCoverage, bool nearCamera, bool adaptive, float baseScale, bool belowMinimumSize = false)
		{
			baseScale = ((float.IsNaN(baseScale) || float.IsInfinity(baseScale)) ? 1f : Math.Max(0.05f, Math.Min(1f, baseScale)));
			if (belowMinimumSize && !nearCamera)
			{
				return new PortalViewSchedule(PortalViewTier.Minimal, baseScale, 8);
			}
			if (!adaptive || nearCamera || screenCoverage >= 0.35f)
			{
				return new PortalViewSchedule(PortalViewTier.High, baseScale, 1);
			}
			if (screenCoverage >= 0.12f)
			{
				return new PortalViewSchedule(PortalViewTier.Medium, baseScale, (screenCoverage >= 0.2f) ? 1 : 2);
			}
			return new PortalViewSchedule(PortalViewTier.Low, baseScale, 4);
		}

		public static void RaiseRatesWithinBudget(PortalViewSchedule[] schedules, int count, int rendersPerFrame)
		{
			if (schedules == null)
			{
				throw new ArgumentNullException("schedules");
			}
			count = Math.Min(count, schedules.Length);
			float num = 0f;
			for (int i = 0; i < count; i++)
			{
				num += 1f / (float)schedules[i].FrameInterval;
			}
			float num2 = (float)Math.Max(1, rendersPerFrame) + 0.0001f;
			for (int j = 0; j < count; j++)
			{
				PortalViewSchedule portalViewSchedule = schedules[j];
				if (portalViewSchedule.FrameInterval != 1)
				{
					float num3 = num - 1f / (float)portalViewSchedule.FrameInterval + 1f;
					if (!(num3 > num2))
					{
						schedules[j] = new PortalViewSchedule(portalViewSchedule.Tier, portalViewSchedule.ResolutionScale, 1);
						num = num3;
					}
				}
			}
		}

		public static bool IsDue(int frame, int lastRenderedFrame, int frameInterval)
		{
			if (lastRenderedFrame >= 0 && frame - lastRenderedFrame < Math.Max(1, frameInterval))
			{
				return frame < lastRenderedFrame;
			}
			return true;
		}

		public static int QuantizeTextureSize(float pixels, int quantum = 32, int minimum = 32, int maximum = 4096)
		{
			if (quantum <= 0)
			{
				throw new ArgumentOutOfRangeException("quantum");
			}
			if (float.IsNaN(pixels) || pixels <= 0f)
			{
				return minimum;
			}
			int val = (int)Math.Ceiling(Math.Min(pixels, maximum) / (float)quantum) * quantum;
			return Math.Max(minimum, Math.Min(maximum, val));
		}
	}
}
namespace Ravengates.Physics
{
	public readonly struct PortalHearingSettings
	{
		public float Multiplier { get; }

		public float MaxRange { get; }

		public float MetresLostPer100 { get; }

		public float MinimumRange { get; }

		public bool FollowsDirection { get; }

		public PortalHearingSettings(float multiplier, float maxRange, float metresLostPer100, float minimumRange = 0.5f, bool followsDirection = true)
		{
			Multiplier = multiplier;
			MaxRange = maxRange;
			MetresLostPer100 = metresLostPer100;
			MinimumRange = minimumRange;
			FollowsDirection = followsDirection;
		}
	}
	public static class PortalAcoustics
	{
		public static float RangeThrough(Portal entrance, Vector3 origin, float range, in PortalHearingSettings settings)
		{
			Portal pair = entrance.Pair;
			if (pair == null || range <= 0f || (settings.FollowsDirection && !entrance.AllowsTraversalToPair))
			{
				return 0f;
			}
			float linkLength = (pair.Pose.Position - entrance.Pose.Position).Length();
			return RangeThrough(range, (origin - entrance.Pose.Position).Length(), linkLength, in settings);
		}

		public static float RangeThrough(float range, float toEntrance, float linkLength, in PortalHearingSettings settings)
		{
			float num = range - toEntrance;
			if (num <= 0f)
			{
				return 0f;
			}
			num *= Math.Max(0f, settings.Multiplier);
			num -= Math.Max(0f, settings.MetresLostPer100) * linkLength / 100f;
			num = Math.Min(num, settings.MaxRange);
			if (!(num >= settings.MinimumRange))
			{
				return 0f;
			}
			return num;
		}

		public static bool Hears(float toExit, float hearRange, float rangeThrough, in PortalHearingSettings settings)
		{
			if (rangeThrough > 0f && toExit <= Math.Min(hearRange, settings.MaxRange))
			{
				return toExit < rangeThrough;
			}
			return false;
		}
	}
	public readonly struct PortalCrossing
	{
		public Portal Portal { get; }

		public PortalHit Hit { get; }

		public Vector3 TravelDirection { get; }

		public float Fraction => Hit.Fraction;

		public int SourceSide => Hit.SourceSide;

		public PortalCrossing(Portal portal, PortalHit hit, Vector3 travelDirection)
		{
			Portal = portal;
			Hit = hit;
			TravelDirection = travelDirection;
		}
	}
	public static class PortalCrossingSolver
	{
		public static bool TryFindCrossing(Portal portal, Vector3 previousPosition, Vector3 currentPosition, out PortalCrossing crossing)
		{
			if (portal.IsTraversable && portal.SegmentIntersects(previousPosition, currentPosition, out var hit))
			{
				crossing = new PortalCrossing(portal, hit, currentPosition - previousPosition);
				return true;
			}
			crossing = default(PortalCrossing);
			return false;
		}

		public static PortalTravellerState ApplyCrossing(PortalTravellerState stateAtCrossing, PortalCrossing crossing, PortalTransform transform, float remainingSeconds, float planeEpsilon)
		{
			if (remainingSeconds < 0f)
			{
				throw new ArgumentOutOfRangeException("remainingSeconds");
			}
			if (planeEpsilon < 0f)
			{
				throw new ArgumentOutOfRangeException("planeEpsilon");
			}
			PortalTravellerState result = transform.TransformState(stateAtCrossing);
			Vector3 value = transform.TransformDirection(crossing.TravelDirection);
			if (value.LengthSquared() > 1E-07f)
			{
				value = Vector3.Normalize(value);
				result.Position += value * planeEpsilon;
			}
			result.Position += result.Velocity * remainingSeconds;
			return result;
		}

		public static PortalTravellerState StateAtCrossing(PortalTravellerState previous, PortalTravellerState current, PortalCrossing crossing)
		{
			float amount = Math.Max(0f, Math.Min(1f, crossing.Fraction));
			return new PortalTravellerState(Vector3.Lerp(previous.Position, current.Position, amount), Quaternion.Slerp(previous.Rotation, current.Rotation, amount), Vector3.Lerp(previous.Velocity, current.Velocity, amount), Vector3.Lerp(previous.AngularVelocity, current.AngularVelocity, amount));
		}
	}
	public interface IPortalTraveller
	{
		string TravellerId { get; }

		PortalBounds GetPortalBounds();

		PortalTravellerState GetState();

		void OnPortalEnter(Portal portal);

		void OnPortalStraddle(Portal portal, float fraction);

		void OnPortalCross(Portal source, Portal destination, PortalTravellerState transformedState);

		void OnPortalExit(Portal portal);
	}
	public sealed class PortalTravellerRegistry
	{
		private readonly Dictionary<string, IPortalTraveller> _travellers = new Dictionary<string, IPortalTraveller>(StringComparer.Ordinal);

		public bool Register(IPortalTraveller traveller)
		{
			if (_travellers.ContainsKey(traveller.TravellerId))
			{
				return false;
			}
			_travellers.Add(traveller.TravellerId, traveller);
			return true;
		}

		public bool Unregister(string travellerId)
		{
			return _travellers.Remove(travellerId);
		}

		public bool TryGet(string travellerId, out IPortalTraveller traveller)
		{
			return _travellers.TryGetValue(travellerId, out traveller);
		}
	}
	public enum PortalRaycastTermination
	{
		Hit,
		NoHit,
		MaximumPortalTraversals,
		InvalidPortal
	}
	public readonly struct PortalRayHit
	{
		public Vector3 Point { get; }

		public Vector3 Normal { get; }

		public float Distance { get; }

		public string ColliderId { get; }

		public PortalRayHit(Vector3 point, Vector3 normal, float distance, string colliderId = "")
		{
			Point = point;
			Normal = normal;
			Distance = distance;
			ColliderId = colliderId ?? string.Empty;
		}
	}
	public interface IPortalRaycastWorld
	{
		IEnumerable<Portal> Portals { get; }

		bool TryRaycast(PortalRay ray, float maxDistance, out PortalRayHit hit);
	}
	public sealed class PortalRaycastResult
	{
		public bool Hit { get; internal set; }

		public PortalRayHit SurfaceHit { get; internal set; }

		public PortalPath Path { get; internal set; } = new PortalPath();

		public PortalRaycastTermination Termination { get; internal set; }
	}
	public static class PortalRaycast
	{
		public static PortalRaycastResult Cast(IPortalRaycastWorld world, PortalRay ray, float maxDistance, int maxPortalTraversals = 8, float planeEpsilon = 0.02f)
		{
			if (maxDistance < 0f)
			{
				throw new ArgumentOutOfRangeException("maxDistance");
			}
			if (maxPortalTraversals < 0)
			{
				throw new ArgumentOutOfRangeException("maxPortalTraversals");
			}
			PortalRaycastResult portalRaycastResult = new PortalRaycastResult
			{
				Path = new PortalPath(Math.Max(1, maxPortalTraversals))
			};
			PortalRay ray2 = ray;
			float num = maxDistance;
			float num2 = 0f;
			for (int i = 0; i <= maxPortalTraversals; i++)
			{
				Portal nearestPortal;
				PortalHit nearestHit;
				float num3 = FindNearestPortalDistance(world.Portals, ray2, num, out nearestPortal, out nearestHit);
				if (world.TryRaycast(ray2, num, out var hit))
				{
					if (nearestPortal == null || hit.Distance <= num3)
					{
						portalRaycastResult.Hit = true;
						portalRaycastResult.SurfaceHit = new PortalRayHit(hit.Point, portalRaycastResult.Path.TransformDirection(hit.Normal), num2 + hit.Distance, hit.ColliderId);
						portalRaycastResult.Termination = PortalRaycastTermination.Hit;
						portalRaycastResult.Path.AddLogicalDistance(hit.Distance);
						return portalRaycastResult;
					}
				}
				else if (nearestPortal == null)
				{
					portalRaycastResult.Termination = PortalRaycastTermination.NoHit;
					return portalRaycastResult;
				}
				if (nearestPortal == null || !nearestPortal.IsTraversable || nearestPortal.Pair == null)
				{
					portalRaycastResult.Termination = PortalRaycastTermination.InvalidPortal;
					return portalRaycastResult;
				}
				if (i == maxPortalTraversals)
				{
					portalRaycastResult.Termination = PortalRaycastTermination.MaximumPortalTraversals;
					return portalRaycastResult;
				}
				float num4 = nearestHit.Fraction * num;
				if (!portalRaycastResult.Path.TryAppend(nearestPortal, nearestPortal.Pair, num4))
				{
					portalRaycastResult.Termination = PortalRaycastTermination.InvalidPortal;
					return portalRaycastResult;
				}
				PortalTransform transformToPair = nearestPortal.GetTransformToPair();
				Vector3 vector = transformToPair.TransformPoint(nearestHit.Point);
				Vector3 vector2 = Vector3.Normalize(transformToPair.TransformDirection(ray2.Direction));
				ray2 = new PortalRay(vector + vector2 * planeEpsilon, vector2);
				num -= num4;
				num -= planeEpsilon;
				num2 += num4;
				if (num <= 0f)
				{
					portalRaycastResult.Termination = PortalRaycastTermination.NoHit;
					return portalRaycastResult;
				}
			}
			portalRaycastResult.Termination = PortalRaycastTermination.MaximumPortalTraversals;
			return portalRaycastResult;
		}

		private static float FindNearestPortalDistance(IEnumerable<Portal> portals, PortalRay ray, float maxDistance, out Portal? nearestPortal, out PortalHit nearestHit)
		{
			nearestPortal = null;
			nearestHit = default(PortalHit);
			float num = float.PositiveInfinity;
			Vector3 point = ray.GetPoint(maxDistance);
			foreach (Portal portal in portals)
			{
				if (portal.IsTraversable && portal.SegmentIntersects(ray.Origin, point, out var hit))
				{
					float num2 = hit.Fraction * maxDistance;
					if (num2 < num)
					{
						num = num2;
						nearestPortal = portal;
						nearestHit = hit;
					}
				}
			}
			return num;
		}
	}
	public struct PortalTraversalGuard
	{
		public string? PortalId { get; private set; }

		public int ExitSide { get; private set; }

		public bool IsActive => PortalId != null;

		public void Arm(Portal destination, Vector3 referencePoint)
		{
			if (destination == null)
			{
				throw new ArgumentNullException("destination");
			}
			float z = destination.Pose.ToLocalPoint(referencePoint).Z;
			PortalId = destination.Id;
			ExitSide = ((z >= 0f) ? 1 : (-1));
		}

		public void Refresh(Portal? guardedPortal, Vector3 referencePoint, float clearance)
		{
			if (PortalId != null)
			{
				if (guardedPortal == null || !string.Equals(guardedPortal.Id, PortalId, StringComparison.Ordinal))
				{
					Clear();
				}
				else if (guardedPortal.Pose.ToLocalPoint(referencePoint).Z * (float)ExitSide >= clearance)
				{
					Clear();
				}
			}
		}

		public bool ShouldDefer(PortalCrossing crossing, Vector3 currentPoint, float clearance)
		{
			if (PortalId == null || crossing.Portal == null)
			{
				return false;
			}
			if (!string.Equals(crossing.Portal.Id, PortalId, StringComparison.Ordinal))
			{
				return false;
			}
			if (crossing.SourceSide != ExitSide)
			{
				return false;
			}
			return Math.Abs(crossing.Portal.Pose.ToLocalPoint(currentPoint).Z) < clearance;
		}

		public void Clear()
		{
			PortalId = null;
			ExitSide = 0;
		}
	}
	public static class PortalTraversal
	{
		public static int ExitSide(PortalCrossing crossing)
		{
			if (crossing.SourceSide < 0)
			{
				return -1;
			}
			return 1;
		}

		public static Vector3 ExitCorrection(Portal destination, Vector3 mappedReferencePoint, int exitSide, float epsilon)
		{
			if (destination == null)
			{
				throw new ArgumentNullException("destination");
			}
			float num = ((exitSide >= 0) ? 1f : (-1f));
			float num2 = destination.Pose.ToLocalPoint(mappedReferencePoint).Z * num;
			if (num2 >= epsilon)
			{
				return Vector3.Zero;
			}
			return destination.Pose.ToWorldDirection(Vector3.UnitZ) * (num * (epsilon - num2));
		}
	}
}
namespace Ravengates.Networking
{
	public readonly struct PortalTraversalRequest
	{
		public string TravellerId { get; }

		public string SourcePortalId { get; }

		public string DestinationPortalId { get; }

		public int ConnectionRevision { get; }

		public Vector3 ClientPosition { get; }

		public Vector3 ClientVelocity { get; }

		public uint Sequence { get; }

		public PortalTraversalRequest(string travellerId, string sourcePortalId, string destinationPortalId, int connectionRevision, Vector3 clientPosition, Vector3 clientVelocity, uint sequence)
		{
			TravellerId = travellerId ?? throw new ArgumentNullException("travellerId");
			SourcePortalId = sourcePortalId ?? throw new ArgumentNullException("sourcePortalId");
			DestinationPortalId = destinationPortalId ?? throw new ArgumentNullException("destinationPortalId");
			ConnectionRevision = connectionRevision;
			ClientPosition = clientPosition;
			ClientVelocity = clientVelocity;
			Sequence = sequence;
		}
	}
	public readonly struct PortalTraversalTicket
	{
		public string TravellerId { get; }

		public Portal Source { get; }

		public Portal Destination { get; }

		public PortalTransform Transform { get; }

		public PortalCrossing Crossing { get; }

		public PortalTraversalTicket(string travellerId, Portal source, Portal destination, PortalTransform transform, PortalCrossing crossing)
		{
			TravellerId = travellerId;
			Source = source;
			Destination = destination;
			Transform = transform;
			Crossing = crossing;
		}
	}
	public sealed class PortalAuthority
	{
		private readonly PortalManager _portals;

		public float MaxTravellerSpeed { get; }

		public PortalAuthority(PortalManager portals, float maxTravellerSpeed = 250f)
		{
			_portals = portals ?? throw new ArgumentNullException("portals");
			if (maxTravellerSpeed <= 0f)
			{
				throw new ArgumentOutOfRangeException("maxTravellerSpeed");
			}
			MaxTravellerSpeed = maxTravellerSpeed;
		}

		public bool TryValidate(PortalTraversalRequest request, Vector3 serverPreviousPosition, Vector3 serverCurrentPosition, Vector3 serverVelocity, out PortalTraversalTicket ticket)
		{
			ticket = default(PortalTraversalTicket);
			if (!_portals.TryResolveLinkedPair(request.SourcePortalId, request.DestinationPortalId, out Portal source, out Portal destination))
			{
				return false;
			}
			if (request.ConnectionRevision != source.ConnectionRevision || !source.IsTraversable)
			{
				return false;
			}
			if (serverVelocity.Length() > MaxTravellerSpeed)
			{
				return false;
			}
			if (!PortalCrossingSolver.TryFindCrossing(source, serverPreviousPosition, serverCurrentPosition, out var crossing))
			{
				return false;
			}
			ticket = new PortalTraversalTicket(request.TravellerId, source, destination, source.GetTransformToPair(), crossing);
			return true;
		}
	}
}
namespace Ravengates.Mathematics
{
	public readonly struct Vector3 : IEquatable<Vector3>
	{
		public float X { get; }

		public float Y { get; }

		public float Z { get; }

		public static Vector3 Zero => new Vector3(0f, 0f, 0f);

		public static Vector3 One => new Vector3(1f, 1f, 1f);

		public static Vector3 UnitX => new Vector3(1f, 0f, 0f);

		public static Vector3 UnitY => new Vector3(0f, 1f, 0f);

		public static Vector3 UnitZ => new Vector3(0f, 0f, 1f);

		public Vector3(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		public float LengthSquared()
		{
			return X * X + Y * Y + Z * Z;
		}

		public float Length()
		{
			return (float)Math.Sqrt(LengthSquared());
		}

		public static float Dot(Vector3 left, Vector3 right)
		{
			return left.X * right.X + left.Y * right.Y + left.Z * right.Z;
		}

		public static Vector3 Normalize(Vector3 value)
		{
			float num = value.Length();
			if (!(num <= 1E-07f))
			{
				return value / num;
			}
			return Zero;
		}

		public static Vector3 Lerp(Vector3 a, Vector3 b, float amount)
		{
			return a + (b - a) * amount;
		}

		public static Vector3 Transform(Vector3 value, Quaternion rotation)
		{
			return Quaternion.Transform(value, rotation);
		}

		public bool Equals(Vector3 other)
		{
			if (X.Equals(other.X) && Y.Equals(other.Y))
			{
				return Z.Equals(other.Z);
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is Vector3 other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (X.GetHashCode() * 397) ^ (Y.GetHashCode() * 17) ^ Z.GetHashCode();
		}

		public override string ToString()
		{
			return $"({X}, {Y}, {Z})";
		}

		public static Vector3 operator +(Vector3 left, Vector3 right)
		{
			return new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
		}

		public static Vector3 operator -(Vector3 left, Vector3 right)
		{
			return new Vector3(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
		}

		public static Vector3 operator -(Vector3 value)
		{
			return new Vector3(0f - value.X, 0f - value.Y, 0f - value.Z);
		}

		public static Vector3 operator *(Vector3 value, float scalar)
		{
			return new Vector3(value.X * scalar, value.Y * scalar, value.Z * scalar);
		}

		public static Vector3 operator *(float scalar, Vector3 value)
		{
			return value * scalar;
		}

		public static Vector3 operator /(Vector3 value, float scalar)
		{
			return new Vector3(value.X / scalar, value.Y / scalar, value.Z / scalar);
		}
	}
	public readonly struct Quaternion : IEquatable<Quaternion>
	{
		public float X { get; }

		public float Y { get; }

		public float Z { get; }

		public float W { get; }

		public static Quaternion Identity => new Quaternion(0f, 0f, 0f, 1f);

		public Quaternion(float x, float y, float z, float w)
		{
			X = x;
			Y = y;
			Z = z;
			W = w;
		}

		public static Quaternion CreateFromAxisAngle(Vector3 axis, float angle)
		{
			Vector3 vector = Vector3.Normalize(axis);
			float num = angle * 0.5f;
			float num2 = (float)Math.Sin(num);
			return Normalize(new Quaternion(vector.X * num2, vector.Y * num2, vector.Z * num2, (float)Math.Cos(num)));
		}

		public static Quaternion Normalize(Quaternion value)
		{
			float num = (float)Math.Sqrt(value.X * value.X + value.Y * value.Y + value.Z * value.Z + value.W * value.W);
			if (!(num <= 1E-07f))
			{
				return new Quaternion(value.X / num, value.Y / num, value.Z / num, value.W / num);
			}
			return Identity;
		}

		public static Quaternion Inverse(Quaternion value)
		{
			float num = value.X * value.X + value.Y * value.Y + value.Z * value.Z + value.W * value.W;
			if (num <= 1E-07f)
			{
				return Identity;
			}
			return new Quaternion((0f - value.X) / num, (0f - value.Y) / num, (0f - value.Z) / num, value.W / num);
		}

		public static Quaternion Slerp(Quaternion a, Quaternion b, float amount)
		{
			a = Normalize(a);
			b = Normalize(b);
			float num = a.X * b.X + a.Y * b.Y + a.Z * b.Z + a.W * b.W;
			if (num < 0f)
			{
				b = new Quaternion(0f - b.X, 0f - b.Y, 0f - b.Z, 0f - b.W);
				num = 0f - num;
			}
			if (num > 0.9995f)
			{
				return Normalize(new Quaternion(a.X + amount * (b.X - a.X), a.Y + amount * (b.Y - a.Y), a.Z + amount * (b.Z - a.Z), a.W + amount * (b.W - a.W)));
			}
			float num2 = (float)Math.Acos(Math.Max(-1f, Math.Min(1f, num)));
			float num3 = (float)Math.Sin(num2);
			float num4 = (float)Math.Sin((1f - amount) * num2) / num3;
			float num5 = (float)Math.Sin(amount * num2) / num3;
			return new Quaternion(a.X * num4 + b.X * num5, a.Y * num4 + b.Y * num5, a.Z * num4 + b.Z * num5, a.W * num4 + b.W * num5);
		}

		public static Vector3 Transform(Vector3 value, Quaternion rotation)
		{
			Quaternion quaternion = Normalize(rotation);
			Vector3 left = new Vector3(quaternion.X, quaternion.Y, quaternion.Z);
			Vector3 vector = Cross(left, value);
			Vector3 vector2 = Cross(left, vector);
			return value + vector * (2f * quaternion.W) + vector2 * 2f;
		}

		private static Vector3 Cross(Vector3 left, Vector3 right)
		{
			return new Vector3(left.Y * right.Z - left.Z * right.Y, left.Z * right.X - left.X * right.Z, left.X * right.Y - left.Y * right.X);
		}

		public bool Equals(Quaternion other)
		{
			if (X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z))
			{
				return W.Equals(other.W);
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is Quaternion other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (((((X.GetHashCode() * 397) ^ Y.GetHashCode()) * 397) ^ Z.GetHashCode()) * 397) ^ W.GetHashCode();
		}

		public override string ToString()
		{
			return $"({X}, {Y}, {Z}, {W})";
		}

		public static Quaternion operator *(Quaternion left, Quaternion right)
		{
			return new Quaternion(left.W * right.X + left.X * right.W + left.Y * right.Z - left.Z * right.Y, left.W * right.Y - left.X * right.Z + left.Y * right.W + left.Z * right.X, left.W * right.Z + left.X * right.Y - left.Y * right.X + left.Z * right.W, left.W * right.W - left.X * right.X - left.Y * right.Y - left.Z * right.Z);
		}
	}
}
namespace Ravengates.Core
{
	public enum PortalTraversalDirection
	{
		Bidirectional,
		OutboundOnly,
		InboundOnly
	}
	public enum PortalSealedSide
	{
		None,
		Front,
		Back
	}
	[Flags]
	public enum PortalFaces
	{
		None = 0,
		Front = 1,
		Back = 2,
		Both = 3
	}
	public sealed class Portal
	{
		private Portal? _pair;

		public string Id { get; }

		public PortalPose Pose { get; private set; }

		public PortalAperture Aperture { get; private set; }

		public string Tag { get; private set; }

		public int ConnectionRevision { get; private set; }

		public int TransformRevision { get; private set; }

		public Portal? Pair => _pair;

		public bool IsLinked => _pair != null;

		public bool IsMutual
		{
			get
			{
				if (_pair != null)
				{
					return _pair._pair == this;
				}
				return false;
			}
		}

		public bool IsTraversable
		{
			get
			{
				if (IsLinked && Pose.HasFiniteValues() && Pair.Pose.HasFiniteValues() && Pose.HasUniformUnitScale())
				{
					return Pair.Pose.HasUniformUnitScale();
				}
				return false;
			}
		}

		public PortalTraversalDirection Direction { get; private set; }

		public bool AllowsTraversalToPair
		{
			get
			{
				if (IsLinked && Direction != PortalTraversalDirection.InboundOnly)
				{
					return Pair.Direction != PortalTraversalDirection.OutboundOnly;
				}
				return false;
			}
		}

		public PortalSealedSide SealedSide { get; private set; }

		public PortalFaces BlockedFaces { get; private set; }

		public Portal(string id, PortalPose pose, PortalAperture aperture, string? tag = null)
		{
			if (string.IsNullOrWhiteSpace(id))
			{
				throw new ArgumentException("A portal id is required.", "id");
			}
			Id = id;
			Pose = pose;
			Aperture = aperture;
			Tag = tag ?? string.Empty;
			ConnectionRevision = 1;
			TransformRevision = 1;
		}

		public void SetDirection(PortalTraversalDirection direction)
		{
			Direction = direction;
		}

		public void SetSealedSide(PortalSealedSide side)
		{
			SealedSide = side;
		}

		public bool IsSideOpen(int side)
		{
			if (IsLinked && !Closes(side))
			{
				return !Pair.Closes(side);
			}
			return false;
		}

		public void SetBlockedFaces(PortalFaces faces)
		{
			BlockedFaces = faces & PortalFaces.Both;
		}

		public bool IsSideBlocked(int side)
		{
			if (IsLinked)
			{
				if (!Blocks(BlockedFaces, side))
				{
					return Blocks(Pair.BlockedFaces, side);
				}
				return true;
			}
			return false;
		}

		private bool Closes(int side)
		{
			if (!Seals(SealedSide, side))
			{
				return Blocks(BlockedFaces, side);
			}
			return true;
		}

		private static bool Blocks(PortalFaces faces, int side)
		{
			if (side <= 0)
			{
				if (side < 0)
				{
					return (faces & PortalFaces.Back) != 0;
				}
				return false;
			}
			return (faces & PortalFaces.Front) != 0;
		}

		private static bool Seals(PortalSealedSide sealedSide, int side)
		{
			if (side <= 0)
			{
				if (side < 0)
				{
					return sealedSide == PortalSealedSide.Back;
				}
				return false;
			}
			return sealedSide == PortalSealedSide.Front;
		}

		public PortalTransform GetTransformToPair()
		{
			if (!IsLinked)
			{
				throw new InvalidOperationException("Portal '" + Id + "' is not linked.");
			}
			return new PortalTransform(Id, Pair.Id, Pose, Pair.Pose);
		}

		public bool ContainsWorldPoint(Vector3 point, float margin = 0f)
		{
			return Aperture.ContainsWorldPoint(Pose, point, margin);
		}

		public bool SegmentIntersects(Vector3 start, Vector3 end, out PortalHit hit)
		{
			return Aperture.SegmentIntersects(Pose, start, end, out hit);
		}

		public bool ShapeFits(PortalBounds bounds)
		{
			return Aperture.ShapeFits(Pose, bounds);
		}

		public void SetPose(PortalPose pose)
		{
			if (!pose.HasFiniteValues())
			{
				throw new ArgumentException("Portal pose must be finite.", "pose");
			}
			Pose = pose;
			TransformRevision++;
		}

		public void SetAperture(PortalAperture aperture)
		{
			Aperture = aperture;
			TransformRevision++;
		}

		public void SetTag(string tag)
		{
			Tag = tag ?? string.Empty;
			ConnectionRevision++;
		}

		internal void SetPair(Portal? pair)
		{
			_pair = pair;
			ConnectionRevision++;
		}

		public override string ToString()
		{
			return "Portal(" + Id + ", tag='" + Tag + "', pair='" + (Pair?.Id ?? "none") + "')";
		}
	}
	public sealed class PortalManager
	{
		private readonly Dictionary<string, Portal> _portals = new Dictionary<string, Portal>(StringComparer.Ordinal);

		public IEnumerable<Portal> Portals => _portals.Values;

		public bool Register(Portal portal)
		{
			if (_portals.ContainsKey(portal.Id))
			{
				return false;
			}
			_portals.Add(portal.Id, portal);
			return true;
		}

		public bool Unregister(string id)
		{
			if (!_portals.TryGetValue(id, out Portal value))
			{
				return false;
			}
			Unlink(value);
			foreach (Portal value2 in _portals.Values)
			{
				if (value2.Pair == value)
				{
					value2.SetPair(null);
				}
			}
			return _portals.Remove(id);
		}

		public bool TryGet(string id, out Portal portal)
		{
			return _portals.TryGetValue(id, out portal);
		}

		public bool TryLink(string firstId, string secondId)
		{
			if (!TryGet(firstId, out Portal portal) || !TryGet(secondId, out Portal portal2))
			{
				return false;
			}
			return TryLink(portal, portal2);
		}

		public bool TryLink(Portal first, Portal second)
		{
			if (first == second || !_portals.ContainsKey(first.Id) || !_portals.ContainsKey(second.Id))
			{
				return false;
			}
			if (first.IsMutual && first.Pair == second)
			{
				return true;
			}
			if (first.IsLinked)
			{
				Unlink(first);
			}
			if (second.IsLinked)
			{
				Unlink(second);
			}
			first.SetPair(second);
			second.SetPair(first);
			return true;
		}

		public bool TryLinkOneWay(Portal source, Portal exit)
		{
			if (source == exit || !_portals.ContainsKey(source.Id) || !_portals.ContainsKey(exit.Id))
			{
				return false;
			}
			if (source.Pair == exit)
			{
				return true;
			}
			if (source.IsLinked)
			{
				Unlink(source);
			}
			source.SetPair(exit);
			return true;
		}

		public void Unlink(Portal portal)
		{
			Portal pair = portal.Pair;
			portal.SetPair(null);
			if (pair != null && pair.Pair == portal)
			{
				pair.SetPair(null);
			}
		}

		public bool TryResolveLinkedPair(string sourceId, string destinationId, out Portal source, out Portal destination)
		{
			source = null;
			destination = null;
			if (!TryGet(sourceId, out source) || !TryGet(destinationId, out destination))
			{
				return false;
			}
			if (source.IsLinked)
			{
				return source.Pair == destination;
			}
			return false;
		}
	}
	public enum PortalApertureShape
	{
		Ellipse,
		RoundedRectangle,
		RadialProfile
	}
	public readonly struct PortalHit
	{
		public float Fraction { get; }

		public Vector3 Point { get; }

		public Vector3 LocalPoint { get; }

		public int SourceSide { get; }

		public PortalHit(float fraction, Vector3 point, Vector3 localPoint, int sourceSide)
		{
			Fraction = fraction;
			Point = point;
			LocalPoint = localPoint;
			SourceSide = sourceSide;
		}
	}
	public readonly struct PortalAperture
	{
		private readonly float[]? _radii;

		public float Width { get; }

		public float Height { get; }

		public PortalApertureShape Shape { get; }

		public float CornerRadius { get; }

		public int RadialSampleCount
		{
			get
			{
				float[]? radii = _radii;
				if (radii == null)
				{
					return 0;
				}
				return radii.Length;
			}
		}

		public PortalAperture(float width, float height, PortalApertureShape shape = PortalApertureShape.Ellipse, float cornerRadius = 0.25f)
		{
			if (width <= 0f)
			{
				throw new ArgumentOutOfRangeException("width");
			}
			if (height <= 0f)
			{
				throw new ArgumentOutOfRangeException("height");
			}
			if (shape == PortalApertureShape.RadialProfile)
			{
				throw new ArgumentException("Use the radial-profile constructor for measured openings.", "shape");
			}
			Width = width;
			Height = height;
			Shape = shape;
			CornerRadius = Math.Max(0f, cornerRadius);
			_radii = null;
		}

		public PortalAperture(float[] radialProfile)
		{
			if (radialProfile == null)
			{
				throw new ArgumentNullException("radialProfile");
			}
			if (radialProfile.Length < 8)
			{
				throw new ArgumentException("At least eight radial samples are required.", "radialProfile");
			}
			float[] array = new float[radialProfile.Length];
			float num = 0f;
			float num2 = 0f;
			float num3 = 0f;
			float num4 = 0f;
			for (int i = 0; i < array.Length; i++)
			{
				float num5 = radialProfile[i];
				if (float.IsNaN(num5) || float.IsInfinity(num5) || num5 <= 0f)
				{
					throw new ArgumentOutOfRangeException("radialProfile", "Radial samples must be finite and positive.");
				}
				array[i] = num5;
				double num6 = Math.PI * 2.0 * (double)i / (double)array.Length;
				float val = (float)(Math.Cos(num6) * (double)num5);
				float val2 = (float)(Math.Sin(num6) * (double)num5);
				num = Math.Min(num, val);
				num2 = Math.Max(num2, val);
				num3 = Math.Min(num3, val2);
				num4 = Math.Max(num4, val2);
			}
			_radii = array;
			Width = Math.Max(0.0001f, 2f * Math.Max(0f - num, num2));
			Height = Math.Max(0.0001f, 2f * Math.Max(0f - num3, num4));
			Shape = PortalApertureShape.RadialProfile;
			CornerRadius = 0f;
		}

		public float RadiusAtAngle(float angle)
		{
			if (_radii != null)
			{
				double num = (double)angle / (Math.PI * 2.0);
				double num2 = (num - Math.Floor(num)) * (double)_radii.Length;
				int num3 = (int)Math.Floor(num2);
				float num4 = (float)(num2 - (double)num3);
				float num5 = _radii[num3 % _radii.Length];
				float num6 = _radii[(num3 + 1) % _radii.Length];
				return num5 + (num6 - num5) * num4;
			}
			double num7 = Math.Cos(angle);
			double num8 = Math.Sin(angle);
			double num9 = (double)Width * 0.5;
			double num10 = (double)Height * 0.5;
			if (Shape == PortalApertureShape.RoundedRectangle)
			{
				double val = ((Math.Abs(num7) < 1E-06) ? double.MaxValue : (num9 / Math.Abs(num7)));
				double val2 = ((Math.Abs(num8) < 1E-06) ? double.MaxValue : (num10 / Math.Abs(num8)));
				return (float)Math.Min(val, val2);
			}
			double d = num7 * num7 / (num9 * num9) + num8 * num8 / (num10 * num10);
			return (float)(1.0 / Math.Sqrt(d));
		}

		public bool ContainsWorldPoint(PortalPose portalPose, Vector3 point, float margin = 0f)
		{
			Vector3 localPoint = portalPose.ToLocalPoint(point);
			return ContainsLocalPoint(localPoint, margin);
		}

		public bool ContainsLocalPoint(Vector3 localPoint, float margin = 0f)
		{
			if (Shape == PortalApertureShape.RadialProfile)
			{
				float num = (float)Math.Sqrt(localPoint.X * localPoint.X + localPoint.Y * localPoint.Y);
				float angle = (float)Math.Atan2(localPoint.Y, localPoint.X);
				if (num <= RadiusAtAngle(angle) + margin)
				{
					return Math.Abs(localPoint.Z) <= 0.05f;
				}
				return false;
			}
			float num2 = Math.Max(0.0001f, Width * 0.5f + margin);
			float num3 = Math.Max(0.0001f, Height * 0.5f + margin);
			if (Shape == PortalApertureShape.RoundedRectangle)
			{
				float num4 = Math.Min(CornerRadius + Math.Max(0f, margin), Math.Min(num2, num3));
				float num5 = Math.Max(Math.Abs(localPoint.X) - (num2 - num4), 0f);
				float num6 = Math.Max(Math.Abs(localPoint.Y) - (num3 - num4), 0f);
				if (num5 * num5 + num6 * num6 <= num4 * num4)
				{
					return Math.Abs(localPoint.Z) <= 0.05f;
				}
				return false;
			}
			float num7 = localPoint.X / num2;
			float num8 = localPoint.Y / num3;
			if (num7 * num7 + num8 * num8 <= 1f)
			{
				return Math.Abs(localPoint.Z) <= 0.05f;
			}
			return false;
		}

		public bool SegmentIntersects(PortalPose portalPose, Vector3 start, Vector3 end, out PortalHit hit)
		{
			Vector3 vector = portalPose.ToLocalPoint(start);
			Vector3 vector2 = portalPose.ToLocalPoint(end);
			float z = vector.Z;
			float z2 = vector2.Z;
			bool num = (z < -1E-05f && z2 > 1E-05f) || (z > 1E-05f && z2 < -1E-05f);
			float num2 = 0f;
			if (num)
			{
				num2 = z / (z - z2);
			}
			else
			{
				if (!(Math.Abs(z) <= 1E-05f) || !(Math.Abs(z2) > 1E-05f))
				{
					hit = default(PortalHit);
					return false;
				}
				num2 = 0f;
			}
			num2 = Math.Max(0f, Math.Min(1f, num2));
			Vector3 localPoint = vector + (vector2 - vector) * num2;
			if (!ContainsLocalPoint(localPoint))
			{
				hit = default(PortalHit);
				return false;
			}
			int num3 = Math.Sign(z);
			if (num3 == 0)
			{
				num3 = Math.Sign(z2);
			}
			hit = new PortalHit(num2, portalPose.ToWorldPoint(localPoint), localPoint, num3);
			return true;
		}

		public bool FitsUprightCapsule(float radius, float height, float margin = 0f)
		{
			radius = Math.Max(0f, radius);
			height = Math.Max(height, radius * 2f);
			float num = 0f - RadiusAtAngle(4.712389f);
			float y = num + radius;
			float y2 = num + height - radius;
			if (ContainsLocalPoint(new Vector3(0f - radius, y, 0f), 0f - margin) && ContainsLocalPoint(new Vector3(radius, y, 0f), 0f - margin) && ContainsLocalPoint(new Vector3(0f - radius, y2, 0f), 0f - margin) && ContainsLocalPoint(new Vector3(radius, y2, 0f), 0f - margin))
			{
				return ContainsLocalPoint(new Vector3(0f, num + height, 0f), 0f - margin);
			}
			return false;
		}

		public bool ShapeFits(PortalPose portalPose, PortalBounds bounds)
		{
			Vector3 vector = portalPose.ToLocalPoint(bounds.Center);
			Vector3 vector2 = new Vector3(Math.Abs(bounds.Extents.X) + Math.Abs(bounds.Extents.Y) + Math.Abs(bounds.Extents.Z), Math.Abs(bounds.Extents.X) + Math.Abs(bounds.Extents.Y) + Math.Abs(bounds.Extents.Z), 0f);
			if (Shape == PortalApertureShape.RadialProfile)
			{
				for (int i = -1; i <= 1; i += 2)
				{
					for (int j = -1; j <= 1; j += 2)
					{
						if (!ContainsLocalPoint(new Vector3(vector.X + (float)i * vector2.X, vector.Y + (float)j * vector2.Y, 0f)))
						{
							return false;
						}
					}
				}
				return true;
			}
			if (Shape == PortalApertureShape.RoundedRectangle)
			{
				if (Math.Abs(vector.X) + vector2.X <= Width * 0.5f)
				{
					return Math.Abs(vector.Y) + vector2.Y <= Height * 0.5f;
				}
				return false;
			}
			float num = Math.Max(0.0001f, Width * 0.5f);
			float num2 = Math.Max(0.0001f, Height * 0.5f);
			float num3 = (Math.Abs(vector.X) + vector2.X) / num;
			float num4 = (Math.Abs(vector.Y) + vector2.Y) / num2;
			return num3 * num3 + num4 * num4 <= 1f;
		}
	}
	public readonly struct PortalPathStep
	{
		public PortalTransform Transform { get; }

		public int SourceConnectionRevision { get; }

		public int DestinationConnectionRevision { get; }

		public PortalPathStep(PortalTransform transform, int sourceConnectionRevision, int destinationConnectionRevision)
		{
			Transform = transform;
			SourceConnectionRevision = sourceConnectionRevision;
			DestinationConnectionRevision = destinationConnectionRevision;
		}
	}
	public sealed class PortalPath
	{
		private readonly List<PortalPathStep> _steps = new List<PortalPathStep>();

		public int MaxTraversals { get; }

		public int Count => _steps.Count;

		public float LogicalDistance { get; private set; }

		public IReadOnlyList<PortalPathStep> Steps => _steps;

		public string Signature
		{
			get
			{
				if (_steps.Count == 0)
				{
					return "root";
				}
				string text = string.Empty;
				for (int i = 0; i < _steps.Count; i++)
				{
					PortalPathStep portalPathStep = _steps[i];
					text = text + ((i == 0) ? string.Empty : "/") + portalPathStep.Transform.SourcePortalId + ">" + portalPathStep.Transform.DestinationPortalId + ":" + portalPathStep.SourceConnectionRevision + ":" + portalPathStep.DestinationConnectionRevision;
				}
				return text;
			}
		}

		public PortalPath(int maxTraversals = 8)
		{
			if (maxTraversals < 1)
			{
				throw new ArgumentOutOfRangeException("maxTraversals");
			}
			MaxTraversals = maxTraversals;
		}

		public bool TryAppend(Portal source, Portal destination, float distanceBeforeCrossing = 0f)
		{
			if (_steps.Count >= MaxTraversals || !source.IsLinked || source.Pair != destination)
			{
				return false;
			}
			_steps.Add(new PortalPathStep(new PortalTransform(source.Id, destination.Id, source.Pose, destination.Pose), source.ConnectionRevision, destination.ConnectionRevision));
			LogicalDistance += Math.Max(0f, distanceBeforeCrossing);
			return true;
		}

		public void AddLogicalDistance(float distance)
		{
			LogicalDistance += Math.Max(0f, distance);
		}

		public Vector3 TransformPoint(Vector3 point)
		{
			for (int i = 0; i < _steps.Count; i++)
			{
				point = _steps[i].Transform.TransformPoint(point);
			}
			return point;
		}

		public Vector3 TransformDirection(Vector3 direction)
		{
			for (int i = 0; i < _steps.Count; i++)
			{
				direction = _steps[i].Transform.TransformDirection(direction);
			}
			return direction;
		}

		public PortalRay TransformRay(PortalRay ray)
		{
			for (int i = 0; i < _steps.Count; i++)
			{
				ray = _steps[i].Transform.TransformRay(ray);
			}
			return ray;
		}

		public bool HasCurrentRevisions()
		{
			for (int i = 0; i < _steps.Count; i++)
			{
				if (_steps[i].Transform.SourcePortalId == null)
				{
					return false;
				}
			}
			return true;
		}
	}
	public readonly struct PortalPose
	{
		public Vector3 Position { get; }

		public Quaternion Rotation { get; }

		public Vector3 Scale { get; }

		public static PortalPose Identity => new PortalPose(Vector3.Zero, Quaternion.Identity, Vector3.One);

		public PortalPose(Vector3 position, Quaternion rotation)
			: this(position, rotation, Vector3.One)
		{
		}

		public PortalPose(Vector3 position, Quaternion rotation, Vector3 scale)
		{
			Position = position;
			Rotation = Quaternion.Normalize(rotation);
			Scale = scale;
		}

		public bool HasFiniteValues()
		{
			if (IsFinite(Position) && IsFinite(Scale) && IsFinite(Rotation.X) && IsFinite(Rotation.Y) && IsFinite(Rotation.Z) && IsFinite(Rotation.W) && Scale.X != 0f && Scale.Y != 0f)
			{
				return Scale.Z != 0f;
			}
			return false;
		}

		public bool HasUniformUnitScale(float tolerance = 0.0001f)
		{
			if (Math.Abs(Scale.X - 1f) <= tolerance && Math.Abs(Scale.Y - 1f) <= tolerance)
			{
				return Math.Abs(Scale.Z - 1f) <= tolerance;
			}
			return false;
		}

		public Vector3 ToLocalPoint(Vector3 worldPoint)
		{
			Vector3 vector = Vector3.Transform(worldPoint - Position, Quaternion.Inverse(Rotation));
			return new Vector3(vector.X / Scale.X, vector.Y / Scale.Y, vector.Z / Scale.Z);
		}

		public Vector3 ToWorldPoint(Vector3 localPoint)
		{
			Vector3 value = new Vector3(localPoint.X * Scale.X, localPoint.Y * Scale.Y, localPoint.Z * Scale.Z);
			return Position + Vector3.Transform(value, Rotation);
		}

		public Vector3 ToLocalDirection(Vector3 worldDirection)
		{
			return Vector3.Transform(worldDirection, Quaternion.Inverse(Rotation));
		}

		public Vector3 ToWorldDirection(Vector3 localDirection)
		{
			return Vector3.Transform(localDirection, Rotation);
		}

		private static bool IsFinite(Vector3 value)
		{
			if (IsFinite(value.X) && IsFinite(value.Y))
			{
				return IsFinite(value.Z);
			}
			return false;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	public readonly struct PortalRay
	{
		public Vector3 Origin { get; }

		public Vector3 Direction { get; }

		public PortalRay(Vector3 origin, Vector3 direction)
		{
			Origin = origin;
			Direction = ((direction.LengthSquared() > 0f) ? Vector3.Normalize(direction) : Vector3.Zero);
		}

		public Vector3 GetPoint(float distance)
		{
			return Origin + Direction * distance;
		}
	}
	public readonly struct PortalPlane
	{
		public Vector3 Normal { get; }

		public float Distance { get; }

		public PortalPlane(Vector3 normal, float distance)
		{
			float num = normal.Length();
			if (num <= 0f)
			{
				throw new ArgumentException("A portal plane needs a non-zero normal.", "normal");
			}
			Normal = normal / num;
			Distance = distance / num;
		}

		public float SignedDistanceTo(Vector3 point)
		{
			return Vector3.Dot(Normal, point) + Distance;
		}
	}
	public readonly struct PortalBounds
	{
		public Vector3 Center { get; }

		public Vector3 Extents { get; }

		public PortalBounds(Vector3 center, Vector3 extents)
		{
			if (extents.X < 0f || extents.Y < 0f || extents.Z < 0f)
			{
				throw new ArgumentOutOfRangeException("extents", "Bounds extents cannot be negative.");
			}
			Center = center;
			Extents = extents;
		}
	}
	public struct PortalTravellerState
	{
		public Vector3 Position;

		public Quaternion Rotation;

		public Vector3 Velocity;

		public Vector3 AngularVelocity;

		public PortalTravellerState(Vector3 position, Quaternion rotation, Vector3 velocity, Vector3 angularVelocity)
		{
			Position = position;
			Rotation = Quaternion.Normalize(rotation);
			Velocity = velocity;
			AngularVelocity = angularVelocity;
		}
	}
	public readonly struct PortalTransform
	{
		private static readonly Quaternion HalfTurn = Quaternion.CreateFromAxisAngle(Vector3.UnitY, MathF.PI);

		public string SourcePortalId { get; }

		public string DestinationPortalId { get; }

		public PortalPose Source { get; }

		public PortalPose Destination { get; }

		public Quaternion Rotation { get; }

		public PortalTransform(string sourcePortalId, string destinationPortalId, PortalPose source, PortalPose destination)
		{
			if (string.IsNullOrWhiteSpace(sourcePortalId))
			{
				throw new ArgumentException("A source portal id is required.", "sourcePortalId");
			}
			if (string.IsNullOrWhiteSpace(destinationPortalId))
			{
				throw new ArgumentException("A destination portal id is required.", "destinationPortalId");
			}
			if (!source.HasFiniteValues() || !destination.HasFiniteValues())
			{
				throw new ArgumentException("Portal poses must contain finite, non-zero values.");
			}
			SourcePortalId = sourcePortalId;
			DestinationPortalId = destinationPortalId;
			Source = source;
			Destination = destination;
			Rotation = Quaternion.Normalize(destination.Rotation * HalfTurn * Quaternion.Inverse(source.Rotation));
		}

		public Vector3 TransformPoint(Vector3 worldPoint)
		{
			Vector3 vector = Source.ToLocalPoint(worldPoint);
			Vector3 localPoint = new Vector3(0f - vector.X, vector.Y, 0f - vector.Z);
			return Destination.ToWorldPoint(localPoint);
		}

		public Vector3 TransformDirection(Vector3 worldDirection)
		{
			return Vector3.Transform(worldDirection, Rotation);
		}

		public Quaternion TransformRotation(Quaternion worldRotation)
		{
			return Quaternion.Normalize(Rotation * worldRotation);
		}

		public PortalTravellerState TransformState(PortalTravellerState state)
		{
			return new PortalTravellerState(TransformPoint(state.Position), TransformRotation(state.Rotation), TransformDirection(state.Velocity), TransformDirection(state.AngularVelocity));
		}

		public PortalRay TransformRay(PortalRay ray)
		{
			return new PortalRay(TransformPoint(ray.Origin), TransformDirection(ray.Direction));
		}

		public PortalPlane TransformPlane(PortalPlane plane)
		{
			Vector3 worldPoint = plane.Normal * (0f - plane.Distance);
			Vector3 right = TransformPoint(worldPoint);
			Vector3 vector = Vector3.Normalize(TransformDirection(plane.Normal));
			return new PortalPlane(vector, 0f - Vector3.Dot(vector, right));
		}

		public Vector3 TransformNormal(Vector3 worldNormal)
		{
			return Vector3.Normalize(TransformDirection(worldNormal));
		}

		public int TransformSide(int sourceSide)
		{
			if (sourceSide == 0)
			{
				return 0;
			}
			return -Math.Sign(sourceSide);
		}

		public override string ToString()
		{
			return SourcePortalId + "->" + DestinationPortalId;
		}
	}
}
namespace Ravengates.Configuration
{
	public enum PortalFallbackMode
	{
		Block,
		UnstableEffect,
		VanillaTeleport
	}
	public sealed class PortalConfiguration
	{
		public bool PortalRenderEnabled { get; set; } = true;

		public bool EnablePlayerTraversal { get; set; } = true;

		public int MaxRecursionDepth { get; set; } = 2;

		public int MaxPortalRenderPassesPerFrame { get; set; } = 6;

		public float PortalRenderScale { get; set; } = 0.75f;

		public float MaxPortalViewDistance { get; set; } = 400f;

		public float MinPortalScreenPixels { get; set; } = 64f;

		public int RenderZoneRadius { get; set; } = 1;

		public int InteractiveZoneRadius { get; set; } = 2;

		public int CrossingReadyZoneRadius { get; set; } = 3;

		public double RemoteUnloadGraceSeconds { get; set; } = 10.0;

		public int MaxRemoteInterestRegionsPerPlayer { get; set; } = 4;

		public int MaxRemoteZonesPerPlayer { get; set; } = 98;

		public float PortalInterestActivationDistance { get; set; } = 128f;

		public int MaxCrossingsPerFixedStep { get; set; } = 8;

		public float PortalPlaneEpsilon { get; set; } = 0.02f;

		public bool EnableVisualStraddling { get; set; } = true;

		public bool EnableRemoteProxyCollision { get; set; }

		public bool RespectVanillaPortalRestrictions { get; set; } = true;

		public bool AllowInteractionThroughPortal { get; set; }

		public bool AllowAITraversal { get; set; }

		public bool ServerAuthoritativeTraversal { get; set; } = true;

		public bool ClientPrediction { get; set; } = true;

		public int MaxRemoteSimulatedCharacters { get; set; } = 50;

		public PortalFallbackMode FallbackMode { get; set; }

		public void Validate()
		{
			MaxRecursionDepth = Clamp(MaxRecursionDepth, 0, 5);
			MaxPortalRenderPassesPerFrame = Clamp(MaxPortalRenderPassesPerFrame, 0, 16);
			PortalRenderScale = ClampFinite(PortalRenderScale, 0.25f, 1f, 0.75f);
			MaxPortalViewDistance = ClampFinite(MaxPortalViewDistance, 50f, 1000f, 400f);
			MinPortalScreenPixels = ClampFinite(MinPortalScreenPixels, 0f, 512f, 64f);
			RenderZoneRadius = Clamp(RenderZoneRadius, 0, 6);
			InteractiveZoneRadius = Clamp(InteractiveZoneRadius, 0, 6);
			CrossingReadyZoneRadius = Clamp(CrossingReadyZoneRadius, 0, 6);
			if (InteractiveZoneRadius < RenderZoneRadius)
			{
				InteractiveZoneRadius = RenderZoneRadius;
			}
			if (CrossingReadyZoneRadius < InteractiveZoneRadius)
			{
				CrossingReadyZoneRadius = InteractiveZoneRadius;
			}
			RemoteUnloadGraceSeconds = ClampFinite(RemoteUnloadGraceSeconds, 0.0, 60.0, 10.0);
			MaxRemoteInterestRegionsPerPlayer = Clamp(MaxRemoteInterestRegionsPerPlayer, 1, 32);
			MaxRemoteZonesPerPlayer = Clamp(MaxRemoteZonesPerPlayer, 1, 512);
			PortalInterestActivationDistance = ClampFinite(PortalInterestActivationDistance, 0f, 512f, 128f);
			MaxCrossingsPerFixedStep = Clamp(MaxCrossingsPerFixedStep, 1, 32);
			PortalPlaneEpsilon = ClampFinite(PortalPlaneEpsilon, 0f, 0.25f, 0.02f);
			MaxRemoteSimulatedCharacters = Clamp(MaxRemoteSimulatedCharacters, 0, 500);
		}

		private static int Clamp(int value, int min, int max)
		{
			return Math.Max(min, Math.Min(max, value));
		}

		private static float ClampFinite(float value, float min, float max, float fallback)
		{
			if (!float.IsNaN(value) && !float.IsInfinity(value))
			{
				return Math.Max(min, Math.Min(max, value));
			}
			return fallback;
		}

		private static double ClampFinite(double value, double min, double max, double fallback)
		{
			if (!double.IsNaN(value) && !double.IsInfinity(value))
			{
				return Math.Max(min, Math.Min(max, value));
			}
			return fallback;
		}
	}
}

plugins/Ravengates.Valheim.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Ravengates.Configuration;
using Ravengates.Core;
using Ravengates.Mathematics;
using Ravengates.Physics;
using Ravengates.Rendering;
using Ravengates.Streaming;
using SoftReferenceableAssets;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Experimental.Rendering;
using UnityEngine.PostProcessing;
using UnityEngine.Rendering;
using UnityEngine.UI;

[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("Ravengates.Valheim")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyInformationalVersion("1.3.0")]
[assembly: AssemblyProduct("Ravengates.Valheim")]
[assembly: AssemblyTitle("Ravengates.Valheim")]
[assembly: AssemblyVersion("1.3.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
public sealed class VrtNoLightProxy : MonoBehaviour
{
}
namespace Ravengates.Valheim
{
	internal static class MjolnirPortalCompat
	{
		internal static readonly Type? ActorType = AccessTools.TypeByName("ValheimMjolnir.Gameplay.MjolnirWorldActor");

		private static readonly Type? DriverType = AccessTools.TypeByName("ValheimMjolnir.Presentation.MjolnirAnimationDriver");

		private static readonly FieldInfo? PreviousPosition = Field("previousPosition");

		private static readonly FieldInfo? ReleasePosition = Field("releasePosition");

		private static readonly FieldInfo? OutboundVelocity = Field("outboundVelocityBeforePhysics");

		private static readonly FieldInfo? OutboundAngularVelocity = Field("outboundAngularVelocityBeforePhysics");

		private static readonly FieldInfo? RecallVelocity = Field("recallVelocity");

		private static readonly FieldInfo? AnimationDriver = Field("animationDriver");

		private static readonly PropertyInfo? OwnerProperty = ((ActorType == null) ? null : AccessTools.Property(ActorType, "Owner"));

		private static readonly PropertyInfo? HandPositionProperty = ((DriverType == null) ? null : AccessTools.Property(DriverType, "HandPosition"));

		[ThreadStatic]
		private static Component? _returningActor;

		internal static bool Available => ActorType != null;

		internal static MethodInfo? DistanceToOwnerGetter
		{
			get
			{
				if (!(ActorType == null))
				{
					return AccessTools.PropertyGetter(ActorType, "DistanceToOwner");
				}
				return null;
			}
		}

		internal static MethodInfo? TickReturnMethod
		{
			get
			{
				if (!(ActorType == null))
				{
					return AccessTools.Method(ActorType, "TickReturn", (Type[])null, (Type[])null);
				}
				return null;
			}
		}

		internal static MethodInfo? HandPositionGetter => HandPositionProperty?.GetGetMethod(nonPublic: true);

		private static FieldInfo? Field(string name)
		{
			if (!(ActorType == null))
			{
				return AccessTools.Field(ActorType, name);
			}
			return null;
		}

		internal static bool IsActor(GameObject gameObject)
		{
			if (ActorType != null)
			{
				return (Object)(object)gameObject.GetComponent(ActorType) != (Object)null;
			}
			return false;
		}

		internal static void OnTraversed(GameObject gameObject, Matrix4x4 pointMap, Quaternion turn)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			Component val = ((ActorType == null) ? null : gameObject.GetComponent(ActorType));
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				PreviousPosition?.SetValue(val, gameObject.transform.position);
				if (ReleasePosition?.GetValue(val) is Vector3 val2)
				{
					ReleasePosition.SetValue(val, ((Matrix4x4)(ref pointMap)).MultiplyPoint3x4(val2));
				}
				Rotate(OutboundVelocity, val, turn);
				Rotate(OutboundAngularVelocity, val, turn);
				Rotate(RecallVelocity, val, turn);
			}
			catch (Exception ex)
			{
				RavengatesPlugin.LogWarning("Mjolnir portal support failed after a crossing: " + ex.GetBaseException().Message);
			}
		}

		private static void Rotate(FieldInfo? field, Component actor, Quaternion turn)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (field?.GetValue(actor) is Vector3 val)
			{
				field.SetValue(actor, turn * val);
			}
		}

		internal static bool TryRouteThroughPortal(Component actor, Vector3 point, out Vector3 mapped, out float routeDistance)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: 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)
			mapped = point;
			routeDistance = 0f;
			RavengateManager manager = RavengatesPlugin.Manager;
			RavengateTraveller component = actor.GetComponent<RavengateTraveller>();
			if ((Object)(object)manager == (Object)null || (Object)(object)component == (Object)null || component.LastSourcePortalId == null || component.LastDestinationPortalId == null)
			{
				return false;
			}
			if (!manager.TryGetPortal(component.LastSourcePortalId, out Portal portal) || !portal.IsTraversable || portal.Pair == null || portal.Pair.Id != component.LastDestinationPortalId)
			{
				return false;
			}
			Vector3 position = actor.transform.position;
			PortalPose pose = portal.Pose;
			Vector3 val = ValheimPortalMath.ToUnity(((PortalPose)(ref pose)).Position);
			pose = portal.Pair.Pose;
			Vector3 val2 = ValheimPortalMath.ToUnity(((PortalPose)(ref pose)).Position);
			routeDistance = Vector3.Distance(position, val2) + Vector3.Distance(val, point);
			if (routeDistance >= Vector3.Distance(position, point))
			{
				return false;
			}
			mapped = ValheimPortalMath.TransformPoint(portal.GetTransformToPair(), point);
			return true;
		}

		internal static void DistanceToOwnerPostfix(Component __instance, ref float __result)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (RavengatesPlugin.IsEnabled)
			{
				object? obj = OwnerProperty?.GetValue(__instance);
				Player val = (Player)((obj is Player) ? obj : null);
				if (val != null && !((Object)(object)val == (Object)null) && TryRouteThroughPortal(__instance, ((Component)val).transform.position, out var _, out var routeDistance) && routeDistance < __result)
				{
					__result = routeDistance;
				}
			}
		}

		internal static void TickReturnPrefix(Component __instance)
		{
			_returningActor = __instance;
		}

		internal static Exception? TickReturnFinalizer(Exception? __exception)
		{
			_returningActor = null;
			return __exception;
		}

		internal static void HandPositionPostfix(Component __instance, ref Vector3 __result)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			Component returningActor = _returningActor;
			if (!((Object)(object)returningActor == (Object)null) && RavengatesPlugin.IsEnabled && AnimationDriver?.GetValue(returningActor) == __instance && TryRouteThroughPortal(returningActor, __result, out var mapped, out var _))
			{
				__result = mapped;
			}
		}
	}
	[HarmonyPatch]
	internal static class MjolnirDistanceToOwnerPatch
	{
		private static bool Prepare()
		{
			return MjolnirPortalCompat.DistanceToOwnerGetter != null;
		}

		private static MethodBase TargetMethod()
		{
			return MjolnirPortalCompat.DistanceToOwnerGetter;
		}

		private static void Postfix(Component __instance, ref float __result)
		{
			MjolnirPortalCompat.DistanceToOwnerPostfix(__instance, ref __result);
		}
	}
	[HarmonyPatch]
	internal static class MjolnirTickReturnPatch
	{
		private static bool Prepare()
		{
			if (MjolnirPortalCompat.TickReturnMethod != null)
			{
				return MjolnirPortalCompat.HandPositionGetter != null;
			}
			return false;
		}

		private static MethodBase TargetMethod()
		{
			return MjolnirPortalCompat.TickReturnMethod;
		}

		private static void Prefix(Component __instance)
		{
			MjolnirPortalCompat.TickReturnPrefix(__instance);
		}

		private static Exception? Finalizer(Exception? __exception)
		{
			return MjolnirPortalCompat.TickReturnFinalizer(__exception);
		}
	}
	[HarmonyPatch]
	internal static class MjolnirHandPositionPatch
	{
		private static bool Prepare()
		{
			if (MjolnirPortalCompat.TickReturnMethod != null)
			{
				return MjolnirPortalCompat.HandPositionGetter != null;
			}
			return false;
		}

		private static MethodBase TargetMethod()
		{
			return MjolnirPortalCompat.HandPositionGetter;
		}

		private static void Postfix(Component __instance, ref Vector3 __result)
		{
			MjolnirPortalCompat.HandPositionPostfix(__instance, ref __result);
		}
	}
	internal static class OdinPortalObjMesh
	{
		internal sealed class Part
		{
			internal string Name = string.Empty;

			internal Mesh Mesh;

			internal string[] Materials = Array.Empty<string>();
		}

		private sealed class Builder
		{
			internal readonly string Name;

			internal readonly List<Vector3> Positions = new List<Vector3>();

			internal readonly List<Vector3> Normals = new List<Vector3>();

			internal readonly List<Vector2> Uvs = new List<Vector2>();

			internal readonly Dictionary<string, int> Remap = new Dictionary<string, int>(StringComparer.Ordinal);

			internal readonly List<string> Materials = new List<string>();

			internal readonly List<List<int>> Indices = new List<List<int>>();

			internal Builder(string name)
			{
				Name = name;
			}

			internal int Material(string name)
			{
				int num = Materials.IndexOf(name);
				if (num >= 0)
				{
					return num;
				}
				Materials.Add(name);
				Indices.Add(new List<int>());
				return Materials.Count - 1;
			}

			internal int Vertex(string token, List<Vector3> positions, List<Vector3> normals, List<Vector2> uvs)
			{
				//IL_0058: Unknown result type (might be due to invalid IL or missing references)
				//IL_004f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0075: 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_0093: Unknown result type (might be due to invalid IL or missing references)
				//IL_0089: Unknown result type (might be due to invalid IL or missing references)
				if (Remap.TryGetValue(token, out var value))
				{
					return value;
				}
				string[] fields = token.Split('/');
				int num = Index(fields, 0, positions.Count);
				int num2 = Index(fields, 1, uvs.Count);
				int num3 = Index(fields, 2, normals.Count);
				Positions.Add((num >= 0) ? positions[num] : Vector3.zero);
				Normals.Add((num3 >= 0) ? normals[num3] : Vector3.up);
				Uvs.Add((num2 >= 0) ? uvs[num2] : Vector2.zero);
				int num4 = Positions.Count - 1;
				Remap.Add(token, num4);
				return num4;
			}

			internal Part? Finish()
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Expected O, but got Unknown
				if (Positions.Count == 0 || Indices.Count == 0)
				{
					return null;
				}
				Mesh val = new Mesh
				{
					name = Name
				};
				if (Positions.Count > 65000)
				{
					val.indexFormat = (IndexFormat)1;
				}
				val.SetVertices(Positions);
				val.SetNormals(Normals);
				val.SetUVs(0, Uvs);
				val.subMeshCount = Indices.Count;
				for (int i = 0; i < Indices.Count; i++)
				{
					val.SetTriangles(Indices[i], i);
				}
				val.RecalculateBounds();
				return new Part
				{
					Name = Name,
					Mesh = val,
					Materials = Materials.ToArray()
				};
			}
		}

		private static readonly char[] Spaces = new char[3] { ' ', '\t', '\r' };

		internal static List<Part> Build(string text)
		{
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			List<Vector3> list2 = new List<Vector3>();
			List<Vector2> list3 = new List<Vector2>();
			List<Part> list4 = new List<Part>();
			Builder builder = null;
			int num = -1;
			string[] array = text.Split('\n');
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length == 0 || text2[0] == '#')
				{
					continue;
				}
				string[] array2 = text2.Split(Spaces, StringSplitOptions.RemoveEmptyEntries);
				switch (array2[0])
				{
				case "v":
					if (array2.Length >= 4)
					{
						list.Add(new Vector3(0f - Number(array2[1]), Number(array2[2]), Number(array2[3])));
					}
					break;
				case "vn":
					if (array2.Length >= 4)
					{
						list2.Add(new Vector3(0f - Number(array2[1]), Number(array2[2]), Number(array2[3])));
					}
					break;
				case "vt":
					if (array2.Length >= 3)
					{
						list3.Add(new Vector2(Number(array2[1]), Number(array2[2])));
					}
					break;
				case "g":
					if (array2.Length >= 2)
					{
						Part part = builder?.Finish();
						if (part != null)
						{
							list4.Add(part);
						}
						builder = new Builder(array2[1]);
						num = -1;
					}
					break;
				case "usemtl":
					if (array2.Length >= 2)
					{
						if (builder == null)
						{
							builder = new Builder("OdinPortal_Unnamed");
						}
						num = builder.Material(array2[1]);
					}
					break;
				case "f":
					if (array2.Length >= 4)
					{
						if (builder == null)
						{
							builder = new Builder("OdinPortal_Unnamed");
						}
						if (num < 0)
						{
							num = builder.Material("MAT_Wood");
						}
						int[] array3 = new int[array2.Length - 1];
						for (int j = 1; j < array2.Length; j++)
						{
							array3[j - 1] = builder.Vertex(array2[j], list, list2, list3);
						}
						List<int> list5 = builder.Indices[num];
						for (int k = 1; k + 1 < array3.Length; k++)
						{
							list5.Add(array3[0]);
							list5.Add(array3[k + 1]);
							list5.Add(array3[k]);
						}
					}
					break;
				}
			}
			Part part2 = builder?.Finish();
			if (part2 != null)
			{
				list4.Add(part2);
			}
			return list4;
		}

		private static int Index(string[] fields, int slot, int count)
		{
			if (slot >= fields.Length || !int.TryParse(fields[slot], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return -1;
			}
			int num = ((result > 0) ? (result - 1) : (count + result));
			if (num < 0 || num >= count)
			{
				return -1;
			}
			return num;
		}

		private static float Number(string text)
		{
			return float.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture);
		}
	}
	internal static class OdinPortalAsset
	{
		internal const float VisualScale = 1.1f;

		private const string ObjResource = "Ravengates.Assets.OdinPortal.obj";

		private const string MtlResource = "Ravengates.Assets.OdinPortal.mtl";

		private const string WoodResource = "Ravengates.Assets.OdinWood.png";

		private const string StoneResource = "Ravengates.Assets.OdinStone.png";

		private static bool _attempted;

		private static List<OdinPortalObjMesh.Part>? _parts;

		private static Dictionary<string, Color>? _colors;

		private static Texture2D? _wood;

		private static Texture2D? _stone;

		private static readonly Dictionary<string, Texture2D> RavenEyeMasks = new Dictionary<string, Texture2D>(StringComparer.Ordinal);

		private static readonly Dictionary<string, Material> CarvedRavenMaterials = new Dictionary<string, Material>(StringComparer.Ordinal);

		internal static readonly Vector3 HuginPerch = new Vector3(0.84f, 2.15f, 0.4f);

		internal static readonly Vector3 MuninPerch = new Vector3(-0.8f, 2.24f, -0.435f);

		internal const float HuginYaw = 0f;

		internal static readonly float MuninYaw = FacingYaw(-Vector3.forward);

		internal const float HuginLean = 5f;

		internal const float MuninLean = 3f;

		internal static readonly Vector2 HuginReach = new Vector2(-95f, 70f);

		internal static readonly Vector2 MuninReach = new Vector2(-40f, 40f);

		internal static readonly Vector3 OpeningCenter = new Vector3(0f, 1.12f, 0.035f);

		internal const float OpeningHalfWidth = 0.93f;

		internal const float OpeningHalfHeight = 1.01f;

		private static float FacingYaw(Vector3 direction)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return Mathf.Atan2(direction.x, direction.z) * 57.29578f;
		}

		internal static bool TryApply(GameObject prefab, Ravengate portal, ManualLogSource? logger)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Expected O, but got Unknown
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_039a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0412: Unknown result type (might be due to invalid IL or missing references)
			//IL_0461: Unknown result type (might be due to invalid IL or missing references)
			//IL_0475: Unknown result type (might be due to invalid IL or missing references)
			if (!Load(logger) || _parts == null)
			{
				return false;
			}
			GameObject val = null;
			List<(Collider, bool)> list = new List<(Collider, bool)>();
			List<(MeshRenderer, bool)> list2 = new List<(MeshRenderer, bool)>();
			try
			{
				Material donor = FindFrameMaterial(prefab);
				Material val2 = FindNativeMaterial("darkwood_beam", "DarkWood_mat");
				Material val3 = FindNativeMaterial("stone_wall_2x1", "stone_mat");
				Dictionary<string, Material> cache = new Dictionary<string, Material>(StringComparer.Ordinal);
				val = new GameObject("OdinPortalVisual");
				val.transform.SetParent(prefab.transform, false);
				val.transform.localScale = Vector3.one * 1.1f;
				OdinPortalVisual odinVisual = val.AddComponent<OdinPortalVisual>();
				Transform val4 = AddGroup(val.transform, "Raven_Huginn", HuginPerch);
				Transform val5 = AddGroup(val.transform, "Raven_Muninn", MuninPerch);
				Material material = GetMaterial("MAT_Raven_CarvedWood", donor, val2, val3, cache);
				if (material.HasProperty("_Cull"))
				{
					material.SetInt("_Cull", 0);
				}
				if (material.HasProperty("_CullMode"))
				{
					material.SetInt("_CullMode", 0);
				}
				Texture grain = (Texture)(((Object)(object)val2 != (Object)null && val2.HasProperty("_MainTex")) ? (((object)val2.GetTexture("_MainTex")) ?? ((object)_wood)) : _wood);
				bool flag = TryAddNativeRaven(val4, "Hugin", 0f, material, grain, logger);
				bool flag2 = TryAddNativeRaven(val5, "Munin", MuninYaw, material, grain, logger);
				foreach (OdinPortalObjMesh.Part part in _parts)
				{
					if ((!flag || !part.Name.StartsWith("Huginn_", StringComparison.Ordinal)) && (!flag2 || !part.Name.StartsWith("Muninn_", StringComparison.Ordinal)))
					{
						GameObject val6 = new GameObject(part.Name);
						Transform val7 = (part.Name.StartsWith("Huginn_", StringComparison.Ordinal) ? val4 : (part.Name.StartsWith("Muninn_", StringComparison.Ordinal) ? val5 : val.transform));
						val6.transform.SetParent(val7, false);
						if ((Object)(object)val7 != (Object)(object)val.transform)
						{
							val6.transform.localPosition = -val7.localPosition;
						}
						val6.AddComponent<MeshFilter>().sharedMesh = part.Mesh;
						MeshRenderer val8 = val6.AddComponent<MeshRenderer>();
						Material[] array = (Material[])(object)new Material[part.Materials.Length];
						for (int i = 0; i < array.Length; i++)
						{
							array[i] = GetMaterial(part.Materials[i], donor, val2, val3, cache);
						}
						((Renderer)val8).sharedMaterials = array;
					}
				}
				int layer = prefab.layer;
				Collider[] componentsInChildren = prefab.GetComponentsInChildren<Collider>(true);
				foreach (Collider val9 in componentsInChildren)
				{
					list.Add((val9, val9.enabled));
					if (!val9.isTrigger)
					{
						layer = ((Component)val9).gameObject.layer;
					}
					val9.enabled = false;
				}
				MeshRenderer[] componentsInChildren2 = prefab.GetComponentsInChildren<MeshRenderer>(true);
				foreach (MeshRenderer val10 in componentsInChildren2)
				{
					if (!((Component)val10).transform.IsChildOf(val.transform))
					{
						list2.Add((val10, ((Renderer)val10).enabled));
						((Renderer)val10).enabled = false;
					}
				}
				AddBox(val.transform, "COL_Left", new Vector3(-1.18f, 0.94f, 0f), new Vector3(0.39f, 1.62f, 0.62f), layer, trigger: false);
				AddBox(val.transform, "COL_Right", new Vector3(1.18f, 0.94f, 0f), new Vector3(0.39f, 1.62f, 0.62f), layer, trigger: false);
				AddBox(val.transform, "COL_Top", new Vector3(0f, 2.33f, 0f), new Vector3(1.35f, 0.24f, 0.52f), layer, trigger: false);
				AddRamp(val.transform, "COL_Base", 1.8f, 0.11f, 0.3f, 0.78f, layer);
				portal.m_openingVolume = AddBox(val.transform, "OdinOpeningVolume", new Vector3(0f, 1.12f, 0f), new Vector3(1.86f, 2.02f, 0.3f), layer, trigger: true);
				portal.m_odinVisual = odinVisual;
				portal.m_model = null;
				int num = _parts.Count - (flag ? 1 : 0) - (flag2 ? 1 : 0);
				if (logger != null)
				{
					logger.LogInfo((object)($"Applied Odin portal model: {num} authored mesh groups, " + $"{(flag ? 1 : 0) + (flag2 ? 1 : 0)} native wooden ravens; " + "wood material=" + (((val2 != null) ? ((Object)val2).name : null) ?? "embedded fallback") + ", stone material=" + (((val3 != null) ? ((Object)val3).name : null) ?? "embedded fallback") + "."));
				}
				return true;
			}
			catch (Exception arg)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
				foreach (var item in list)
				{
					if ((Object)(object)item.Item1 != (Object)null)
					{
						item.Item1.enabled = item.Item2;
					}
				}
				foreach (var item2 in list2)
				{
					if ((Object)(object)item2.Item1 != (Object)null)
					{
						((Renderer)item2.Item1).enabled = item2.Item2;
					}
				}
				if (logger != null)
				{
					logger.LogWarning((object)$"Odin portal asset could not be applied; keeping cloned frame: {arg}");
				}
				return false;
			}
		}

		private static Transform AddGroup(Transform parent, string name, Vector3 localPosition)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = new GameObject(name).transform;
			transform.SetParent(parent, false);
			transform.localPosition = localPosition;
			return transform;
		}

		private static bool TryAddNativeRaven(Transform parent, string prefabName, float yaw, Material fallbackWood, Texture? grain, ManualLogSource? logger)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_030e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_0387: Unknown result type (might be due to invalid IL or missing references)
			//IL_038c: Unknown result type (might be due to invalid IL or missing references)
			//IL_038e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0418: Unknown result type (might be due to invalid IL or missing references)
			//IL_041d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_0424: Unknown result type (might be due to invalid IL or missing references)
			//IL_0438: Unknown result type (might be due to invalid IL or missing references)
			//IL_043d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0442: Unknown result type (might be due to invalid IL or missing references)
			//IL_044a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0457: Unknown result type (might be due to invalid IL or missing references)
			//IL_045e: Unknown result type (might be due to invalid IL or missing references)
			//IL_046e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0470: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Unknown result type (might be due to invalid IL or missing references)
			//IL_0479: Unknown result type (might be due to invalid IL or missing references)
			//IL_047e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = null;
			try
			{
				GameObject obj = PrefabManager.Instance.GetPrefab(prefabName) ?? Cache.GetPrefab<GameObject>(prefabName);
				Transform obj2 = ((obj != null) ? obj.transform.Find("model") : null);
				if ((Object)(object)obj2 == (Object)null)
				{
					throw new InvalidOperationException(prefabName + " model prefab is unavailable");
				}
				val = new GameObject(prefabName + "_CarvedWood");
				val.transform.SetParent(parent, false);
				val.SetActive(false);
				GameObject val2 = Object.Instantiate<GameObject>(((Component)obj2).gameObject, val.transform, false);
				((Object)val2).name = prefabName + "_PerchedModel";
				Component[] componentsInChildren = val2.GetComponentsInChildren<Component>(true);
				foreach (Component val3 in componentsInChildren)
				{
					if (!(val3 is Transform) && !(val3 is SkinnedMeshRenderer) && !(val3 is Animator))
					{
						Object.DestroyImmediate((Object)(object)val3);
					}
				}
				Animator component = val2.GetComponent<Animator>();
				AnimationClip val4 = null;
				if ((Object)(object)((component != null) ? component.runtimeAnimatorController : null) != (Object)null)
				{
					AnimationClip[] animationClips = component.runtimeAnimatorController.animationClips;
					foreach (AnimationClip val5 in animationClips)
					{
						if ((Object)(object)val5 != (Object)null && ((Object)val5).name == "HuginIdle")
						{
							val4 = val5;
							break;
						}
					}
				}
				if ((Object)(object)val4 == (Object)null)
				{
					throw new InvalidOperationException(prefabName + " idle clip is unavailable");
				}
				val.SetActive(true);
				float num = PickFoldedIdleTime(val2, val.transform, val4);
				val4.SampleAnimation(val2, num);
				if ((Object)(object)component != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)component);
				}
				Vector3 val6 = default(Vector3);
				((Vector3)(ref val6))..ctor(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
				Vector3 val7 = default(Vector3);
				((Vector3)(ref val7))..ctor(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
				int num2 = 0;
				SkinnedMeshRenderer[] componentsInChildren2 = val2.GetComponentsInChildren<SkinnedMeshRenderer>(true);
				foreach (SkinnedMeshRenderer val8 in componentsInChildren2)
				{
					Material[] sharedMaterials = ((Renderer)val8).sharedMaterials;
					Material[] array = (Material[])(object)new Material[Math.Max(1, sharedMaterials.Length)];
					for (int j = 0; j < array.Length; j++)
					{
						array[j] = GetCarvedRavenMaterial(prefabName, (j < sharedMaterials.Length) ? sharedMaterials[j] : null, grain, logger) ?? fallbackWood;
					}
					((Renderer)val8).sharedMaterials = array;
					((Renderer)val8).shadowCastingMode = (ShadowCastingMode)1;
					((Renderer)val8).receiveShadows = true;
					val8.updateWhenOffscreen = true;
					Mesh sharedMesh = val8.sharedMesh;
					if ((Object)(object)sharedMesh == (Object)null)
					{
						continue;
					}
					num2 += sharedMesh.vertexCount;
					Bounds bounds = sharedMesh.bounds;
					Matrix4x4 val9 = val.transform.worldToLocalMatrix * ((Component)val8).transform.localToWorldMatrix;
					for (int k = -1; k <= 1; k += 2)
					{
						for (int l = -1; l <= 1; l += 2)
						{
							for (int m = -1; m <= 1; m += 2)
							{
								Vector3 val10 = ((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3((float)k, (float)l, (float)m));
								Vector3 val11 = ((Matrix4x4)(ref val9)).MultiplyPoint3x4(val10);
								val6 = Vector3.Min(val6, val11);
								val7 = Vector3.Max(val7, val11);
							}
						}
					}
				}
				float num3 = val7.y - val6.y;
				if (num2 < 100 || num3 < 0.05f || num3 > 100f)
				{
					throw new InvalidOperationException(prefabName + " mesh could not be measured");
				}
				float num4 = 0.55f / num3;
				if (num4 < 0.01f || num4 > 20f)
				{
					throw new InvalidOperationException($"{prefabName} mesh scale is out of range: {num4}");
				}
				Quaternion val12 = Quaternion.Euler(0f, yaw, 0f);
				Vector3 val13 = Vector3.zero;
				int num5 = 0;
				Transform[] componentsInChildren3 = val2.GetComponentsInChildren<Transform>(true);
				foreach (Transform val14 in componentsInChildren3)
				{
					if (((Object)val14).name == "Mid3.l" || ((Object)val14).name == "Mid3.r")
					{
						val13 += val.transform.InverseTransformPoint(val14.position);
						num5++;
					}
				}
				if (num5 != 2)
				{
					throw new InvalidOperationException(prefabName + " toe bones are unavailable");
				}
				val13 /= (float)num5;
				Vector3 val15 = val13 + new Vector3(0f, 0.015f / num4, 0f);
				val.transform.localRotation = val12;
				val.transform.localScale = Vector3.one * num4;
				val.transform.localPosition = -(val12 * (val15 * num4));
				if (logger != null)
				{
					logger.LogInfo((object)("Mounted carved " + prefabName + " from the installed game mesh " + $"({num2} vertices, idle t={num:0.00}s, mesh height {num3:0.00} m, " + $"target height {0.55f:0.00} m, scale {num4:0.000})."));
				}
				return true;
			}
			catch (Exception ex)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
				if (logger != null)
				{
					logger.LogWarning((object)("Could not mount native " + prefabName + "; keeping authored crow: " + ex.Message));
				}
				return false;
			}
		}

		private static float PickFoldedIdleTime(GameObject model, Transform mount, AnimationClip idle)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			if (idle.length <= 0f)
			{
				return 0f;
			}
			SkinnedMeshRenderer[] componentsInChildren = model.GetComponentsInChildren<SkinnedMeshRenderer>(true);
			Mesh val = new Mesh();
			float result = 0f;
			float num = float.PositiveInfinity;
			try
			{
				for (int i = 0; i < 24; i++)
				{
					float num2 = idle.length * (float)i / 24f;
					idle.SampleAnimation(model, num2);
					float num3 = float.PositiveInfinity;
					float num4 = float.NegativeInfinity;
					float num5 = float.PositiveInfinity;
					float num6 = float.NegativeInfinity;
					SkinnedMeshRenderer[] array = componentsInChildren;
					foreach (SkinnedMeshRenderer val2 in array)
					{
						if (!((Object)(object)val2.sharedMesh == (Object)null))
						{
							val2.BakeMesh(val);
							Matrix4x4 val3 = mount.worldToLocalMatrix * ((Component)val2).transform.localToWorldMatrix;
							Vector3[] vertices = val.vertices;
							for (int k = 0; k < vertices.Length; k += 2)
							{
								Vector3 val4 = ((Matrix4x4)(ref val3)).MultiplyPoint3x4(vertices[k]);
								num3 = Math.Min(num3, val4.x);
								num4 = Math.Max(num4, val4.x);
								num5 = Math.Min(num5, val4.y);
								num6 = Math.Max(num6, val4.y);
							}
						}
					}
					float num7 = num6 - num5;
					if (!(num7 <= 0f))
					{
						float num8 = (num4 - num3) / num7;
						if (!(num8 >= num))
						{
							num = num8;
							result = num2;
						}
					}
				}
				return result;
			}
			finally
			{
				Object.DestroyImmediate((Object)(object)val);
			}
		}

		private static Material? GetCarvedRavenMaterial(string ravenName, Material? native, Texture? grain, ManualLogSource? logger)
		{
			//IL_004c: 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: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)native == (Object)null) && native.HasProperty("_MainTex"))
			{
				Texture texture = native.GetTexture("_MainTex");
				if (texture != null)
				{
					string key = ravenName + "/" + ((Object)native).name;
					if (CarvedRavenMaterials.TryGetValue(key, out Material value))
					{
						return value;
					}
					try
					{
						Material val = new Material(native)
						{
							name = "Odin_" + ravenName + "_CarvedWood"
						};
						val.SetTexture("_MainTex", (Texture)(object)BuildCarvedAlbedo(texture, grain, ravenName));
						if (val.HasProperty("_Color"))
						{
							val.SetColor("_Color", Color.white);
						}
						if (val.HasProperty("_Metallic"))
						{
							val.SetFloat("_Metallic", 0f);
						}
						if (val.HasProperty("_Glossiness"))
						{
							val.SetFloat("_Glossiness", 0.14f);
						}
						if (val.HasProperty("_BumpScale"))
						{
							val.SetFloat("_BumpScale", 1.35f);
						}
						if (val.HasProperty("_EmissionColor"))
						{
							val.SetColor("_EmissionColor", Color.black);
						}
						val.DisableKeyword("_EMISSION");
						CarvedRavenMaterials.Add(key, val);
						return val;
					}
					catch (Exception ex)
					{
						if (logger != null)
						{
							logger.LogWarning((object)("Could not carve " + ravenName + " material; using darkwood: " + ex.Message));
						}
						return null;
					}
				}
			}
			return null;
		}

		private static Texture2D BuildCarvedAlbedo(Texture albedo, Texture? grain, string ravenName)
		{
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Expected O, but got Unknown
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			int width;
			int height;
			Color32[] array = ReadPixels(albedo, 1024, out width, out height);
			Color32[] array2 = null;
			int width2 = 0;
			int height2 = 0;
			float num = 96f;
			float num2 = 64f;
			float num3 = 40f;
			float num4 = 1f;
			Color32[] array3;
			if ((Object)(object)grain != (Object)null)
			{
				array2 = ReadPixels(grain, 512, out width2, out height2);
				double num5 = 0.0;
				double num6 = 0.0;
				double num7 = 0.0;
				array3 = array2;
				foreach (Color32 val in array3)
				{
					num5 += (double)(int)val.r;
					num6 += (double)(int)val.g;
					num7 += (double)(int)val.b;
				}
				num = (float)(num5 / (double)array2.Length);
				num2 = (float)(num6 / (double)array2.Length);
				num3 = (float)(num7 / (double)array2.Length);
				num4 = Math.Max(1f, 0.2126f * num + 0.7152f * num2 + 0.0722f * num3);
			}
			int[] array4 = new int[256];
			array3 = array;
			foreach (Color32 p in array3)
			{
				array4[Luma(p)]++;
			}
			int num8 = Percentile(array4, array.Length, 0.04f);
			int num9 = Math.Max(num8 + 8, Percentile(array4, array.Length, 0.96f));
			Color32[] array5 = (Color32[])(object)new Color32[array.Length];
			for (int j = 0; j < height; j++)
			{
				for (int k = 0; k < width; k++)
				{
					int num10 = j * width + k;
					float num11 = Mathf.Clamp01((float)(Luma(array[num10]) - num8) / (float)(num9 - num8));
					num11 = Mathf.Pow(num11, 0.85f);
					float num12 = Mathf.Lerp(0.46f, 1.42f, num11);
					if (array2 != null)
					{
						int num13 = (int)((long)k * 3L * width2 / width % width2);
						int num14 = (int)((long)j * 3L * height2 / height % height2);
						Color32 val2 = array2[num14 * width2 + num13];
						num12 *= Mathf.Clamp((0.2126f * (float)(int)val2.r + 0.7152f * (float)(int)val2.g + 0.0722f * (float)(int)val2.b) / num4, 0.7f, 1.3f);
					}
					array5[num10] = new Color32((byte)Mathf.Clamp(num * num12, 0f, 255f), (byte)Mathf.Clamp(num2 * num12, 0f, 255f), (byte)Mathf.Clamp(num3 * num12, 0f, 255f), array[num10].a);
				}
			}
			Texture2D val3 = new Texture2D(width, height, (TextureFormat)4, true)
			{
				name = "Odin_" + ravenName + "_CarvedAlbedo",
				wrapMode = albedo.wrapMode,
				filterMode = (FilterMode)2,
				anisoLevel = 4
			};
			val3.SetPixels32(array5);
			val3.Apply(true, true);
			return val3;
		}

		private static int Luma(Color32 p)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			return 54 * p.r + 183 * p.g + 19 * p.b >> 8;
		}

		private static int Percentile(int[] histogram, int total, float fraction)
		{
			int num = (int)((float)total * fraction);
			int num2 = 0;
			for (int i = 0; i < histogram.Length; i++)
			{
				if ((num2 += histogram[i]) > num)
				{
					return i;
				}
			}
			return histogram.Length - 1;
		}

		private static Color32[] ReadPixels(Texture texture, int maxSize, out int width, out int height)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			width = Math.Min(texture.width, maxSize);
			height = Math.Min(texture.height, maxSize);
			RenderTexture temporary = RenderTexture.GetTemporary(width, height, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2);
			RenderTexture active = RenderTexture.active;
			Texture2D val = null;
			try
			{
				Graphics.Blit(texture, temporary);
				RenderTexture.active = temporary;
				val = new Texture2D(width, height, (TextureFormat)4, false);
				val.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0, false);
				return val.GetPixels32();
			}
			finally
			{
				RenderTexture.active = active;
				RenderTexture.ReleaseTemporary(temporary);
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}

		private static BoxCollider AddBox(Transform parent, string name, Vector3 center, Vector3 size, int layer, bool trigger)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name)
			{
				layer = layer
			};
			val.transform.SetParent(parent, false);
			val.transform.localPosition = center;
			BoxCollider obj = val.AddComponent<BoxCollider>();
			obj.size = size;
			((Collider)obj).isTrigger = trigger;
			return obj;
		}

		private static void AddRamp(Transform parent, string name, float width, float height, float topDepth, float baseDepth, int layer)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name)
			{
				layer = layer
			};
			val.transform.SetParent(parent, false);
			float num = width * 0.5f;
			float num2 = topDepth * 0.5f;
			float num3 = baseDepth * 0.5f;
			Mesh val2 = new Mesh();
			((Object)val2).name = name;
			val2.vertices = (Vector3[])(object)new Vector3[8]
			{
				new Vector3(0f - num, 0f, 0f - num3),
				new Vector3(num, 0f, 0f - num3),
				new Vector3(num, 0f, num3),
				new Vector3(0f - num, 0f, num3),
				new Vector3(0f - num, height, 0f - num2),
				new Vector3(num, height, 0f - num2),
				new Vector3(num, height, num2),
				new Vector3(0f - num, height, num2)
			};
			val2.triangles = new int[36]
			{
				0, 1, 2, 0, 2, 3, 4, 6, 5, 4,
				7, 6, 0, 4, 5, 0, 5, 1, 2, 6,
				7, 2, 7, 3, 3, 7, 4, 3, 4, 0,
				1, 5, 6, 1, 6, 2
			};
			Mesh val3 = val2;
			val3.RecalculateBounds();
			MeshCollider obj = val.AddComponent<MeshCollider>();
			obj.convex = true;
			obj.sharedMesh = val3;
		}

		internal static Texture2D? GetRavenEyeMask(string ravenName)
		{
			if (RavenEyeMasks.TryGetValue(ravenName, out Texture2D value))
			{
				return value;
			}
			try
			{
				Material ravenSourceMaterial = GetRavenSourceMaterial(ravenName);
				if ((Object)(object)ravenSourceMaterial == (Object)null || !ravenSourceMaterial.HasProperty("_EmissionMap"))
				{
					return null;
				}
				Texture texture = ravenSourceMaterial.GetTexture("_EmissionMap");
				Texture2D val = (Texture2D)(object)((texture is Texture2D) ? texture : null);
				if (val == null)
				{
					return null;
				}
				Texture2D val2 = MakeGrayscaleMask(val, ravenName);
				RavenEyeMasks.Add(ravenName, val2);
				return val2;
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("Could not prepare " + ravenName + " eye mask: " + ex.Message));
			}
			return null;
		}

		internal static Material? GetRavenSourceMaterial(string ravenName)
		{
			GameObject obj = PrefabManager.Instance.GetPrefab(ravenName) ?? Cache.GetPrefab<GameObject>(ravenName);
			Transform val = ((obj != null) ? obj.transform.Find("model") : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			SkinnedMeshRenderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<SkinnedMeshRenderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Material[] sharedMaterials = ((Renderer)componentsInChildren[i]).sharedMaterials;
				foreach (Material val2 in sharedMaterials)
				{
					if ((Object)(object)val2 != (Object)null && val2.HasProperty("_EmissionMap"))
					{
						return val2;
					}
				}
			}
			return null;
		}

		private static Texture2D MakeGrayscaleMask(Texture2D source, string ravenName)
		{
			//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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: 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)
			RenderTexture temporary = RenderTexture.GetTemporary(((Texture)source).width, ((Texture)source).height, 0, (RenderTextureFormat)0);
			RenderTexture active = RenderTexture.active;
			try
			{
				Graphics.Blit((Texture)(object)source, temporary);
				RenderTexture.active = temporary;
				Texture2D val = new Texture2D(((Texture)source).width, ((Texture)source).height, (TextureFormat)4, false)
				{
					name = "Odin_" + ravenName + "_EyeMask",
					wrapMode = ((Texture)source).wrapMode,
					filterMode = ((Texture)source).filterMode
				};
				val.ReadPixels(new Rect(0f, 0f, (float)((Texture)source).width, (float)((Texture)source).height), 0, 0, false);
				val.Apply(false, false);
				Color32[] pixels = val.GetPixels32();
				for (int i = 0; i < pixels.Length; i++)
				{
					byte b = Math.Max(pixels[i].r, Math.Max(pixels[i].g, pixels[i].b));
					pixels[i] = new Color32(b, b, b, pixels[i].a);
				}
				val.SetPixels32(pixels);
				val.Apply(false, true);
				return val;
			}
			finally
			{
				RenderTexture.active = active;
				RenderTexture.ReleaseTemporary(temporary);
			}
		}

		internal static bool IsActiveRunePart(string name)
		{
			if (!name.StartsWith("Rune_Wood_Active", StringComparison.Ordinal) && !name.StartsWith("Rune_Stone_Active", StringComparison.Ordinal) && !name.StartsWith("OdinEye_Carving", StringComparison.Ordinal))
			{
				return name.StartsWith("OdinEye_Pupil", StringComparison.Ordinal);
			}
			return true;
		}

		private static Material FindFrameMaterial(GameObject prefab)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			MeshRenderer[] componentsInChildren = prefab.GetComponentsInChildren<MeshRenderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Material sharedMaterial = ((Renderer)componentsInChildren[i]).sharedMaterial;
				if ((Object)(object)sharedMaterial != (Object)null && sharedMaterial.renderQueue < 3000)
				{
					return sharedMaterial;
				}
			}
			Shader obj = Shader.Find("Standard");
			if ((Object)(object)obj == (Object)null)
			{
				throw new InvalidOperationException("No usable frame shader found.");
			}
			return new Material(obj);
		}

		private static Material? FindNativeMaterial(string prefabName, string materialName)
		{
			GameObject prefab = PrefabManager.Instance.GetPrefab(prefabName);
			if ((Object)(object)prefab == (Object)null)
			{
				return null;
			}
			MeshRenderer[] componentsInChildren = prefab.GetComponentsInChildren<MeshRenderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				Material[] sharedMaterials = ((Renderer)componentsInChildren[i]).sharedMaterials;
				foreach (Material val in sharedMaterials)
				{
					if ((Object)(object)val != (Object)null && ((Object)val).name == materialName)
					{
						return val;
					}
				}
			}
			return null;
		}

		private static Material GetMaterial(string name, Material donor, Material? nativeWood, Material? nativeStone, Dictionary<string, Material> cache)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			if (cache.TryGetValue(name, out Material value))
			{
				return value;
			}
			bool flag = name.StartsWith("MAT_Wood", StringComparison.Ordinal) || name.StartsWith("MAT_Raven_CarvedWood", StringComparison.Ordinal) || name == "MAT_Rope";
			bool flag2 = name.StartsWith("MAT_Stone", StringComparison.Ordinal);
			object obj = (flag ? (nativeWood ?? donor) : (flag2 ? ((object)(nativeStone ?? donor)) : ((object)donor)));
			Material val = new Material((Material)obj)
			{
				name = "Odin_" + name
			};
			Color value2;
			Color val2 = ((_colors != null && _colors.TryGetValue(name, out value2)) ? value2 : Color.white);
			Texture2D val3 = ((flag && (Object)(object)nativeWood == (Object)null) ? _wood : (flag2 ? _stone : null));
			if ((Object)(object)val3 != (Object)null)
			{
				float num = name switch
				{
					"MAT_Rope" => 0.42f, 
					"MAT_Raven_CarvedWood_Cut" => 0.91f, 
					"MAT_Raven_CarvedWood" => 0.59f, 
					"MAT_Stone_Light" => 0.72f, 
					"MAT_Stone_Dark" => 0.46f, 
					"MAT_Wood_Cut" => 1.42f, 
					"MAT_Wood_Worn" => 1.13f, 
					"MAT_Wood_Dark" => 0.55f, 
					_ => flag2 ? 0.58f : 0.92f, 
				};
				((Color)(ref val2))..ctor(num, num, num);
			}
			if (val.HasProperty("_MainTex") && ((Object)(object)val3 != (Object)null || (!flag && !flag2)))
			{
				val.SetTexture("_MainTex", (Texture)(object)val3);
			}
			if ((flag && (Object)(object)nativeWood != (Object)null) || (flag2 && (Object)(object)nativeStone != (Object)null))
			{
				float num2 = name switch
				{
					"MAT_Stone_Light" => 0.72f, 
					"MAT_Stone_Dark" => 0.46f, 
					"MAT_Rope" => 0.42f, 
					"MAT_Raven_CarvedWood_Cut" => 1.12f, 
					"MAT_Raven_CarvedWood" => 0.76f, 
					"MAT_Wood_Cut" => 1.32f, 
					"MAT_Wood_Worn" => 1.13f, 
					"MAT_Wood_Dark" => 0.56f, 
					_ => 0.58f, 
				};
				((Color)(ref val2))..ctor(num2, num2, num2);
			}
			if (flag2)
			{
				if (val.HasProperty("_BumpMap"))
				{
					val.SetTexture("_BumpMap", (Texture)null);
				}
				if (val.HasProperty("_BumpScale"))
				{
					val.SetFloat("_BumpScale", 0f);
				}
				if (val.HasProperty("_ValueNoise"))
				{
					val.SetFloat("_ValueNoise", 0f);
				}
			}
			if ((Object)obj == (Object)(object)donor)
			{
				if (val.HasProperty("_BumpMap"))
				{
					val.SetTexture("_BumpMap", (Texture)null);
				}
				if (val.HasProperty("_EmissionMap"))
				{
					val.SetTexture("_EmissionMap", (Texture)(object)Texture2D.whiteTexture);
				}
			}
			if (val.HasProperty("_Color"))
			{
				val.SetColor("_Color", val2);
			}
			if (val.HasProperty("_EmissionColor"))
			{
				val.SetColor("_EmissionColor", Color.black);
			}
			val.DisableKeyword("_EMISSION");
			cache.Add(name, val);
			return val;
		}

		private static bool Load(ManualLogSource? logger)
		{
			if (_attempted)
			{
				return _parts != null;
			}
			_attempted = true;
			try
			{
				Assembly assembly = typeof(OdinPortalAsset).Assembly;
				string text = ReadText(assembly, "Ravengates.Assets.OdinPortal.obj");
				string text2 = ReadText(assembly, "Ravengates.Assets.OdinPortal.mtl");
				if (text == null || text2 == null)
				{
					throw new FileNotFoundException("Embedded Odin OBJ/MTL missing.");
				}
				_parts = OdinPortalObjMesh.Build(text);
				if (_parts.Count == 0)
				{
					throw new InvalidDataException("Odin OBJ has no triangles.");
				}
				_colors = ParseColors(text2);
				_wood = ReadTexture(assembly, "Ravengates.Assets.OdinWood.png");
				_stone = ReadTexture(assembly, "Ravengates.Assets.OdinStone.png");
				if ((Object)(object)_wood == (Object)null || (Object)(object)_stone == (Object)null)
				{
					throw new InvalidDataException("Odin textures are missing.");
				}
				return true;
			}
			catch (Exception arg)
			{
				_parts = null;
				if (logger != null)
				{
					logger.LogWarning((object)$"Could not load embedded Odin portal model: {arg}");
				}
				return false;
			}
		}

		private static string? ReadText(Assembly assembly, string resource)
		{
			using Stream stream = assembly.GetManifestResourceStream(resource);
			if (stream == null)
			{
				return null;
			}
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}

		private static Texture2D? ReadTexture(Assembly assembly, string resource)
		{
			using Stream stream = assembly.GetManifestResourceStream(resource);
			if (stream == null)
			{
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			Texture2D obj = AssetUtils.LoadImage(memoryStream.ToArray());
			((Object)obj).name = resource;
			((Texture)obj).wrapMode = (TextureWrapMode)0;
			((Texture)obj).anisoLevel = 2;
			return obj;
		}

		private static Dictionary<string, Color> ParseColors(string mtl)
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, Color> dictionary = new Dictionary<string, Color>(StringComparer.Ordinal);
			string text = null;
			string[] array = mtl.Split('\n');
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Trim().Split(new char[3] { ' ', '\t', '\r' }, StringSplitOptions.RemoveEmptyEntries);
				if (array2.Length >= 2 && array2[0] == "newmtl")
				{
					text = array2[1];
				}
				if (text != null && array2.Length >= 4 && !(array2[0] != "Kd") && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array2[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
				{
					dictionary[text] = new Color(result, result2, result3);
				}
			}
			return dictionary;
		}
	}
	internal sealed class OdinPortalVisual : MonoBehaviour
	{
		private const float ActiveDistance = 40f;

		private static readonly Color HuginEye = new Color(5.2f, 0.06f, 0.02f);

		private static readonly Color MuninEye = new Color(0.05f, 4.6f, 5.2f);

		private const float EyeLightRange = 0.45f;

		private readonly List<(MeshRenderer Renderer, Material[] Dark, Material[] Lit)> _runes = new List<(MeshRenderer, Material[], Material[])>();

		private readonly List<(Renderer Renderer, Material[] Dark, Material[] Lit)> _eyes = new List<(Renderer, Material[], Material[])>();

		private readonly List<RavenSentinel> _ravens = new List<RavenSentinel>();

		private RavenSentinel? _hugin;

		private RavenSentinel? _munin;

		private readonly List<Material> _owned = new List<Material>();

		private bool _linked;

		private bool _initialized;

		private float _phase;

		private float _reactionEnd = -10f;

		private static Material? _sharedGlow;

		private void EnsureRunes()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			if (_initialized)
			{
				return;
			}
			_initialized = true;
			_runes.Clear();
			_eyes.Clear();
			_phase = Mathf.Abs(((Component)this).transform.position.x * 13.1f + ((Component)this).transform.position.z * 7.7f) % 100f;
			MeshRenderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren)
			{
				if (!OdinPortalAsset.IsActiveRunePart(((Object)val).name))
				{
					continue;
				}
				Material[] sharedMaterials = ((Renderer)val).sharedMaterials;
				if (sharedMaterials.Length == 0 || (Object)(object)sharedMaterials[0] == (Object)null)
				{
					continue;
				}
				if ((Object)(object)_sharedGlow == (Object)null)
				{
					_sharedGlow = new Material(sharedMaterials[0])
					{
						name = "Odin_MAT_Rune_Emissive"
					};
					if (_sharedGlow.HasProperty("_MainTex"))
					{
						_sharedGlow.SetTexture("_MainTex", (Texture)null);
					}
					if (_sharedGlow.HasProperty("_Color"))
					{
						_sharedGlow.SetColor("_Color", new Color(0.65f, 0.23f, 0.055f));
					}
					if (_sharedGlow.HasProperty("_EmissionColor"))
					{
						_sharedGlow.SetColor("_EmissionColor", new Color(1.9f, 0.55f, 0.09f));
					}
					_sharedGlow.EnableKeyword("_EMISSION");
				}
				Material[] array = (Material[])(object)new Material[sharedMaterials.Length];
				for (int j = 0; j < array.Length; j++)
				{
					array[j] = _sharedGlow;
				}
				_runes.Add((val, sharedMaterials, array));
				RavengateRayTracingInterop.MarkNoLightProxy((Renderer)(object)val);
			}
			_hugin = AddRaven("Raven_Huginn", "Hugin", RavenRole.WatchesTraveller, HuginEye, 0f, 5f, OdinPortalAsset.HuginReach);
			_munin = AddRaven("Raven_Muninn", "Munin", RavenRole.WatchesGate, MuninEye, OdinPortalAsset.MuninYaw, 3f, OdinPortalAsset.MuninReach);
		}

		private RavenSentinel? AddRaven(string groupName, string ravenName, RavenRole role, Color eyeColor, float restYaw, float restLean, Vector2 reach)
		{
			//IL_0024: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: 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_00b2: 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)
			Transform val = ((Component)this).transform.Find(groupName);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			List<Material> list = new List<Material>();
			RavenSentinel ravenSentinel = new RavenSentinel(val, role, restYaw, restLean, reach, (float)_ravens.Count * 1.37f + _phase)
			{
				EyeColor = eyeColor,
				Eyes = list
			};
			Transform val2 = val.Find(ravenName + "_CarvedWood");
			if ((Object)(object)val2 != (Object)null)
			{
				ravenSentinel.FindBones(val2);
				AddNativeEyes(val2, ravenName, eyeColor, list);
			}
			else
			{
				AddFallbackEyes(val, ravenName, eyeColor, list);
			}
			if (list.Count > 0)
			{
				ravenSentinel.EyeLight = (((Object)(object)ravenSentinel.Head != (Object)null) ? AddEyeLight(ravenSentinel.Head, Vector3.zero, ravenName, eyeColor) : AddEyeLight(val, Vector3.up * 0.42f, ravenName, eyeColor));
			}
			_owned.AddRange(list);
			_ravens.Add(ravenSentinel);
			return ravenSentinel;
		}

		private void AddNativeEyes(Transform mount, string ravenName, Color glowColor, List<Material> glows)
		{
			//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_008f: Expected O, but got Unknown
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			Texture2D ravenEyeMask = OdinPortalAsset.GetRavenEyeMask(ravenName);
			if ((Object)(object)ravenEyeMask == (Object)null)
			{
				return;
			}
			SkinnedMeshRenderer[] componentsInChildren = ((Component)mount).GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer val in componentsInChildren)
			{
				Material[] sharedMaterials = ((Renderer)val).sharedMaterials;
				if (sharedMaterials.Length == 0 || (Object)(object)sharedMaterials[0] == (Object)null)
				{
					continue;
				}
				Material val2 = sharedMaterials[0];
				if (!val2.HasProperty("_EmissionMap"))
				{
					Material ravenSourceMaterial = OdinPortalAsset.GetRavenSourceMaterial(ravenName);
					if ((Object)(object)ravenSourceMaterial == (Object)null)
					{
						continue;
					}
					val2 = ravenSourceMaterial;
				}
				Material val3 = new Material(val2)
				{
					name = "Odin_" + ravenName + "_EyeGlow"
				};
				if ((Object)(object)val2 != (Object)(object)sharedMaterials[0])
				{
					if (val3.HasProperty("_MainTex") && sharedMaterials[0].HasProperty("_MainTex"))
					{
						val3.SetTexture("_MainTex", sharedMaterials[0].GetTexture("_MainTex"));
					}
					if (val3.HasProperty("_BumpMap") && sharedMaterials[0].HasProperty("_BumpMap"))
					{
						val3.SetTexture("_BumpMap", sharedMaterials[0].GetTexture("_BumpMap"));
					}
					if (val3.HasProperty("_Color") && sharedMaterials[0].HasProperty("_Color"))
					{
						val3.SetColor("_Color", sharedMaterials[0].GetColor("_Color"));
					}
					if (val3.HasProperty("_MetallicGlossMap"))
					{
						val3.SetTexture("_MetallicGlossMap", (Texture)null);
					}
				}
				val3.SetTexture("_EmissionMap", (Texture)(object)ravenEyeMask);
				val3.SetTextureScale("_EmissionMap", Vector2.one);
				val3.SetTextureOffset("_EmissionMap", Vector2.zero);
				if (val3.HasProperty("_EmissionColor"))
				{
					val3.SetColor("_EmissionColor", glowColor);
				}
				val3.EnableKeyword("_EMISSION");
				Material[] array = (Material[])(object)new Material[sharedMaterials.Length];
				for (int j = 0; j < array.Length; j++)
				{
					array[j] = val3;
				}
				_eyes.Add(((Renderer)(object)val, sharedMaterials, array));
				glows.Add(val3);
				RavengateRayTracingInterop.MarkNoLightProxy((Renderer)(object)val);
			}
		}

		private Light AddEyeLight(Transform parent, Vector3 localOffset, string ravenName, Color eyeColor)
		{
			//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)
			//IL_0022: 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_003b: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Odin_" + ravenName + "_EyeLight");
			val.transform.SetParent(parent, false);
			val.transform.localPosition = localOffset;
			Light obj = val.AddComponent<Light>();
			obj.type = (LightType)2;
			obj.color = eyeColor / Mathf.Max(eyeColor.r, Mathf.Max(eyeColor.g, eyeColor.b));
			obj.range = 0.45f;
			obj.intensity = 0.55f;
			obj.shadows = (LightShadows)0;
			obj.renderMode = (LightRenderMode)1;
			((Behaviour)obj).enabled = _linked;
			return obj;
		}

		private void AddFallbackEyes(Transform pivot, string ravenName, Color glowColor, List<Material> glows)
		{
			//IL_0067: 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_0083: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			Material val = null;
			MeshRenderer[] componentsInChildren = ((Component)pivot).GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val2 in componentsInChildren)
			{
				Material[] sharedMaterials = ((Renderer)val2).sharedMaterials;
				Material[] array = (Material[])sharedMaterials.Clone();
				bool flag = false;
				for (int j = 0; j < array.Length; j++)
				{
					if ((Object)(object)array[j] == (Object)null || !((Object)array[j]).name.Contains("Raven_Eye"))
					{
						continue;
					}
					if ((Object)(object)val == (Object)null)
					{
						val = new Material(array[j])
						{
							name = "Odin_" + ravenName + "_FallbackEyeGlow"
						};
						if (val.HasProperty("_EmissionColor"))
						{
							val.SetColor("_EmissionColor", glowColor);
						}
						val.EnableKeyword("_EMISSION");
						glows.Add(val);
					}
					array[j] = val;
					flag = true;
				}
				if (flag)
				{
					_eyes.Add(((Renderer)(object)val2, sharedMaterials, array));
					RavengateRayTracingInterop.MarkNoLightProxy((Renderer)(object)val2);
				}
			}
		}

		internal void SetLinked(bool linked)
		{
			EnsureRunes();
			if (_linked == linked)
			{
				return;
			}
			_linked = linked;
			foreach (var rune in _runes)
			{
				if ((Object)(object)rune.Renderer != (Object)null)
				{
					((Renderer)rune.Renderer).sharedMaterials = (linked ? rune.Lit : rune.Dark);
				}
			}
			foreach (var eye in _eyes)
			{
				if ((Object)(object)eye.Renderer != (Object)null)
				{
					eye.Renderer.sharedMaterials = (linked ? eye.Lit : eye.Dark);
				}
			}
			foreach (RavenSentinel raven in _ravens)
			{
				if ((Object)(object)raven.EyeLight != (Object)null)
				{
					((Behaviour)raven.EyeLight).enabled = linked;
				}
			}
		}

		private void Update()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (!_initialized || _ravens.Count == 0)
			{
				return;
			}
			Camera mainCamera = Utils.GetMainCamera();
			if ((Object)(object)mainCamera == (Object)null)
			{
				return;
			}
			Vector3 val = ((Component)mainCamera).transform.position - ((Component)this).transform.position;
			if (((Vector3)(ref val)).sqrMagnitude > 1600f)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			Vector3? traveller = null;
			if ((Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead())
			{
				traveller = ((Component)localPlayer).transform.position + Vector3.up * 1.55f;
			}
			float time = Time.time;
			RavenContext context = new RavenContext(((Component)this).transform, _linked, traveller, time < _reactionEnd + 1.8f, time, Time.deltaTime);
			_hugin?.Tick(in context);
			_munin?.Tick(in context);
			if (!_linked)
			{
				return;
			}
			foreach (RavenSentinel raven in _ravens)
			{
				raven.SetEyeGlow(raven.EyeLevel);
			}
		}

		internal void ReactToTeleport()
		{
			EnsureRunes();
			_reactionEnd = Time.time + 0.42f;
		}

		private void OnDestroy()
		{
			foreach (Material item in _owned)
			{
				if ((Object)(object)item != (Object)null)
				{
					Object.Destroy((Object)(object)item);
				}
			}
			_owned.Clear();
		}
	}
	internal static class PortalAim
	{
		private static BaseAI? _current;

		internal static void Begin(BaseAI ai)
		{
			_current = ai;
		}

		internal static void End()
		{
			_current = null;
		}

		internal static Vector3 ApparentPosition(Character target)
		{
			//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)
			return Apparent(_current, target, ((Component)target).transform.position);
		}

		internal static Vector3 ApparentTopPoint(Character target)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return Apparent(_current, target, target.GetTopPoint());
		}

		internal static Vector3 ApparentCenterPoint(Character target)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return Apparent(_current, target, target.GetCenterPoint());
		}

		internal static Vector3 ApparentVelocity(Character target)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			Vector3 velocity = target.GetVelocity();
			if (!TryGetMap(_current, target, out var _, out var turn))
			{
				return velocity;
			}
			return turn * velocity;
		}

		internal static Vector3 Apparent(BaseAI? ai, Character target, Vector3 world)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (!TryGetMap(ai, target, out var toApparent, out var _))
			{
				return world;
			}
			return ((Matrix4x4)(ref toApparent)).MultiplyPoint3x4(world);
		}

		private static bool TryGetMap(BaseAI? ai, Character target, out Matrix4x4 toApparent, out Quaternion turn)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//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_0069: 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)
			toApparent = Matrix4x4.identity;
			turn = Quaternion.identity;
			if ((Object)(object)ai == (Object)null || (Object)(object)target == (Object)null || !PortalSense.Enabled || !PortalSense.TryGetView(((Component)ai).transform, target, out Portal portal))
			{
				return false;
			}
			PortalTransform transformToPair = portal.GetTransformToPair();
			Matrix4x4 val = ValheimPortalMath.ToMatrix(transformToPair);
			toApparent = ((Matrix4x4)(ref val)).inverse;
			turn = Quaternion.Inverse(ValheimPortalMath.ToUnity(((PortalTransform)(ref transformToPair)).Rotation));
			return true;
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
	internal static class MonsterAIApparentTargetPatch
	{
		private static void Prefix(MonsterAI __instance)
		{
			PortalAim.Begin((BaseAI)(object)__instance);
		}

		private static void Finalizer()
		{
			PortalAim.End();
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(MonsterAI), "m_targetCreature");
			MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(Component), "transform");
			MethodInfo methodInfo2 = AccessTools.PropertyGetter(typeof(Transform), "position");
			Dictionary<MethodInfo, MethodInfo> dictionary = new Dictionary<MethodInfo, MethodInfo>
			{
				[AccessTools.Method(typeof(Character), "GetTopPoint", (Type[])null, (Type[])null)] = AccessTools.Method(typeof(PortalAim), "ApparentTopPoint", (Type[])null, (Type[])null),
				[AccessTools.Method(typeof(Character), "GetCenterPoint", (Type[])null, (Type[])null)] = AccessTools.Method(typeof(PortalAim), "ApparentCenterPoint", (Type[])null, (Type[])null),
				[AccessTools.Method(typeof(Character), "GetVelocity", (Type[])null, (Type[])null)] = AccessTools.Method(typeof(PortalAim), "ApparentVelocity", (Type[])null, (Type[])null)
			};
			MethodInfo operand = AccessTools.Method(typeof(PortalAim), "ApparentPosition", (Type[])null, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(typeof(EffectArea), "IsPointInsideNoMonsterArea", (Type[])null, (Type[])null);
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			int num = 0;
			int num2 = 0;
			while (fieldInfo != null && num2 + 1 < list.Count)
			{
				if (!(list[num2].opcode != OpCodes.Ldfld) && object.Equals(list[num2].operand, fieldInfo) && list[num2 + 1].operand is MethodInfo methodInfo4)
				{
					MethodInfo value;
					if (methodInfo4 == methodInfo && num2 + 2 < list.Count && list[num2 + 2].operand is MethodInfo methodInfo5 && methodInfo5 == methodInfo2)
					{
						if (num2 + 3 >= list.Count || !(list[num2 + 3].operand is MethodInfo methodInfo6) || !(methodInfo6 == methodInfo3))
						{
							list[num2 + 1].opcode = OpCodes.Call;
							list[num2 + 1].operand = operand;
							list[num2 + 2].opcode = OpCodes.Nop;
							list[num2 + 2].operand = null;
							num++;
						}
					}
					else if (dictionary.TryGetValue(methodInfo4, out value))
					{
						list[num2 + 1].opcode = OpCodes.Call;
						list[num2 + 1].operand = value;
						num++;
					}
				}
				num2++;
			}
			if (num == 0)
			{
				RavengatesPlugin.LogWarning("MonsterAI.UpdateAI: target reads not found; creatures that sense a target through a Ravengate aim at its real position.");
			}
			return list;
		}
	}
	[HarmonyPatch(typeof(Attack), "GetProjectileSpawnPoint")]
	internal static class AttackProjectileAimPatch
	{
		private static void Postfix(ref Vector3 spawnPoint, ref Vector3 aimDir, BaseAI? ___m_baseAI, Humanoid? ___m_character, bool ___m_useCharacterFacing)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: 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)
			if ((Object)(object)___m_baseAI == (Object)null || (Object)(object)___m_character == (Object)null || ___m_useCharacterFacing)
			{
				return;
			}
			Character targetCreature = ___m_baseAI.GetTargetCreature();
			if (!((Object)(object)targetCreature == (Object)null))
			{
				Vector3 centerPoint = targetCreature.GetCenterPoint();
				Vector3 val = PortalAim.Apparent(___m_baseAI, targetCreature, centerPoint);
				if (!(val == centerPoint))
				{
					Vector3 val2 = val - spawnPoint;
					Vector3 normalized = ((Vector3)(ref val2)).normalized;
					aimDir = Vector3.RotateTowards(((Component)___m_character).transform.forward, normalized, MathF.PI / 2f, 1f);
				}
			}
		}
	}
	internal static class PortalExposureCatchUp
	{
		private const float CatchUpSpeed = 8f;

		private const float CatchUpSeconds = 1.5f;

		private static readonly Dictionary<PostProcessingProfile, (float Up, float Down)> Original = new Dictionary<PostProcessingProfile, (float, float)>();

		private static float _until;

		internal static void Begin()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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_00c2: 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_0078: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			if (!PortalManagerConfig.ExposureCatchUp.Value)
			{
				return;
			}
			PostProcessingBehaviour[] array = Object.FindObjectsOfType<PostProcessingBehaviour>();
			for (int i = 0; i < array.Length; i++)
			{
				PostProcessingProfile profile = array[i].profile;
				if ((Object)(object)profile == (Object)null || !((PostProcessingModel)profile.eyeAdaptation).enabled)
				{
					continue;
				}
				Settings settings = profile.eyeAdaptation.settings;
				if (!Original.ContainsKey(profile))
				{
					if (settings.speedUp == 8f && settings.speedDown == 8f)
					{
						continue;
					}
					Original[profile] = (settings.speedUp, settings.speedDown);
				}
				settings.speedUp = Mathf.Max(settings.speedUp, 8f);
				settings.speedDown = Mathf.Max(settings.speedDown, 8f);
				profile.eyeAdaptation.settings = settings;
			}
			if (Original.Count > 0)
			{
				_until = Time.unscaledTime + 1.5f;
			}
		}

		internal static void Tick()
		{
			if (Original.Count > 0 && Time.unscaledTime >= _until)
			{
				Restore();
			}
		}

		internal static void Restore()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			if (Original.Count == 0)
			{
				return;
			}
			(float, float)? tuple = null;
			foreach (KeyValuePair<PostProcessingProfile, (float, float)> item in Original)
			{
				(float, float) valueOrDefault = tuple.GetValueOrDefault();
				if (!tuple.HasValue)
				{
					valueOrDefault = item.Value;
					tuple = valueOrDefault;
				}
				if (!((Object)(object)item.Key == (Object)null))
				{
					Settings settings = item.Key.eyeAdaptation.settings;
					settings.speedUp = item.Value.Item1;
					settings.speedDown = item.Value.Item2;
					item.Key.eyeAdaptation.settings = settings;
				}
			}
			PostProcessingBehaviour[] array = Object.FindObjectsOfType<PostProcessingBehaviour>();
			for (int i = 0; i < array.Length; i++)
			{
				PostProcessingProfile profile = array[i].profile;
				if (!((Object)(object)profile == (Object)null) && !Original.ContainsKey(profile))
				{
					Settings settings2 = profile.eyeAdaptation.settings;
					if (settings2.speedUp == 8f && settings2.speedDown == 8f)
					{
						settings2.speedUp = tuple.Value.Item1;
						settings2.speedDown = tuple.Value.Item2;
						profile.eyeAdaptation.settings = settings2;
					}
				}
			}
			Original.Clear();
		}
	}
	internal static class PortalManagerConfig
	{
		internal static ConfigEntry<bool> EnableRavengates = null;

		internal static ConfigEntry<bool> EnablePortalRendering = null;

		internal static ConfigEntry<bool> EnablePlayerTraversal = null;

		internal static ConfigEntry<bool> EnableObjectTraversal = null;

		internal static ConfigEntry<bool> DefaultPortalTravel = null;

		internal static ConfigEntry<float> ReentryClearance = null;

		internal static ConfigEntry<KeyboardShortcut> ToggleKey = null;

		internal static ConfigEntry<float> MenuScale = null;

		internal static ConfigEntry<bool> AllowCreatureTraversal = null;

		internal static ConfigEntry<float> MaxCreatureHeight = null;

		internal static ConfigEntry<float> MaxCreatureWidth = null;

		internal static ConfigEntry<float> CreatureTraversalCooldown = null;

		internal static ConfigEntry<float> CreaturePortalSearchRadius = null;

		internal static ConfigEntry<float> PortalRouteAdvantage = null;

		internal static ConfigEntry<string> ExcludedCreatures = null;

		internal static ConfigEntry<bool> AllowVanillaLinks = null;

		internal static ConfigEntry<bool> ApplyVanillaItemRules = null;

		internal static ConfigEntry<bool> ReachThroughPortals = null;

		internal static ConfigEntry<bool> CreaturesSenseThroughPortals = null;

		internal static ConfigEntry<bool> PortalHearing = null;

		internal static ConfigEntry<float> PortalHearingMultiplier = null;

		internal static ConfigEntry<float> PortalHearingMaxRange = null;

		internal static ConfigEntry<float> PortalHearingDistanceFactor = null;

		internal static ConfigEntry<bool> AllowTiltedPlacement = null;

		internal static ConfigEntry<bool> UseVanillaRecipe = null;

		internal static ConfigEntry<bool> CloseWalledSides = null;

		internal static ConfigEntry<KeyboardShortcut> TiltKey = null;

		internal static ConfigEntry<bool> EnableTemporaryLinks = null;

		internal static ConfigEntry<float> TemporaryLinkSeconds = null;

		internal static ConfigEntry<RavengateTravellerKinds> OneWayAppliesTo = null;

		internal static ConfigEntry<float> PortalRenderScale = null;

		internal static ConfigEntry<int> MaxPortalViews = null;

		internal static ConfigEntry<float> MinPortalScreenPixels = null;

		internal static ConfigEntry<float> MaxPortalViewDistance = null;

		internal static ConfigEntry<bool> AdaptiveQuality = null;

		internal static ConfigEntry<RavengateCompositeStage> CompositeStage = null;

		internal static ConfigEntry<bool> WritePortalDepth = null;

		internal static ConfigEntry<float> CompositeExposure = null;

		internal static ConfigEntry<bool> ExposureCatchUp = null;

		internal static ConfigEntry<float> OpeningInset = null;

		internal static ConfigEntry<bool> EnableVisualStraddling = null;

		internal static ConfigEntry<bool> EnableAtmosphere = null;

		internal static ConfigEntry<float> AtmosphereDensity = null;

		internal static ConfigEntry<bool> EnableClimateSpread = null;

		internal static ConfigEntry<float> ImpressionGlowDay = null;

		internal static ConfigEntry<float> ImpressionGlowNight = null;

		internal static ConfigEntry<bool> EnableFrozenViews = null;

		internal static ConfigEntry<bool> EnableWeatherSounds = null;

		internal static ConfigEntry<bool> EnableTransitionEffects = null;

		internal static ConfigEntry<bool> EnableLightLeakage = null;

		internal static ConfigEntry<float> LightLeakageIntensity = null;

		internal static ConfigEntry<float> LightLeakageRange = null;

		internal static ConfigEntry<bool> EnablePortalTransition = null;

		internal static ConfigEntry<bool> TransitionRefraction = null;

		internal static ConfigEntry<bool> TransitionEnvironmentBlend = null;

		internal static ConfigEntry<bool> TransitionDepthStretch = null;

		internal static ConfigEntry<bool> TransitionPull = null;

		internal static ConfigEntry<float> PullDuration = null;

		internal static ConfigEntry<float> PullStrength = null;

		internal static ConfigEntry<float> RefractionDuration = null;

		internal static ConfigEntry<float> RefractionStrength = null;

		internal static ConfigEntry<float> EnvironmentBlendDuration = null;

		internal static ConfigEntry<float> DepthStretchDuration = null;

		internal static ConfigEntry<float> DepthStretchStrength = null;

		internal static ConfigEntry<bool> EnableGlassEdge = null;

		internal static ConfigEntry<float> GlassRefraction = null;

		internal static ConfigEntry<float> GlassHighlight = null;

		internal static ConfigEntry<bool> DisableForeignCameraComponents = null;

		internal static ConfigEntry<string> IsolationDisabledKeywords = null;

		internal static ConfigEntry<string> IsolationWhiteTextures = null;

		internal static ConfigEntry<string> IsolationBlackTextures = null;

		internal static ConfigEntry<int> RemoteZoneRadius = null;

		internal static ConfigEntry<int> MaxRemoteZones = null;

		internal static ConfigEntry<float> PortalInterestActivationDistance = null;

		internal static ConfigEntry<float> RemoteUnloadGraceSeconds = null;

		internal static ConfigEntry<float> DepartedAreaRetainSeconds = null;

		internal static ConfigEntry<bool> LogStreamingStats = null;

		private static bool _batching;

		private static string _excludedSource = string.Empty;

		private static readonly HashSet<string> ExcludedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static readonly string[] PresetNames = new string[3] { "Conservative", "Balanced", "High Fidelity" };

		private static readonly (float Scale, int Views, float MinPixels, float Distance, int Radius, int Zones, float Activation, float Grace)[] Presets = new(float, int, float, float, int, int, float, float)[3]
		{
			(0.5f, 2, 96f, 200f, 0, 49, 72f, 5f),
			(1f, 4, 64f, 400f, 1, 98, 128f, 10f),
			(1f, 8, 24f, 500f, 2, 196, 192f, 15f)
		};

		internal static bool IsCreatureTraversalEnabled
		{
			get
			{
				if (IsEnabled && AllowCreatureTraversal != null)
				{
					return AllowCreatureTraversal.Value;
				}
				return false;
			}
		}

		internal static bool IsEnabled
		{
			get
			{
				if (EnableRavengates != null)
				{
					return EnableRavengates.Value;
				}
				return false;
			}
		}

		internal static bool IsRenderingEnabled
		{
			get
			{
				if (IsEnabled && EnablePortalRendering != null)
				{
					return EnablePortalRendering.Value;
				}
				return false;
			}
		}

		internal static bool CanEditServerSettings
		{
			get
			{
				if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
				{
					return true;
				}
				try
				{
					return SynchronizationManager.Instance.PlayerIsAdmin;
				}
				catch
				{
					return true;
				}
			}
		}

		internal static event Action? Changed;

		internal static void Bind(ConfigFile config)
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: 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_014b: Expected O, but got Unknown
			EnableRavengates = BindEntry(config, "General", "EnableSeamlessPortals", defaultValue: true, "Master switch for Ravengate pieces (Ravengate_Wood). When off they stay as inert frames: no traversal, views, or remote streaming. Vanilla portals are never affected.");
			EnablePlayerTraversal = BindEntry(config, "General", "EnablePlayerTraversal", defaultValue: true, "Allow the local player and camera to cross linked Ravengates while the master switch is on.");
			EnableObjectTraversal = BindEntry(config, "General", "EnableObjectTraversal", defaultValue: true, "Allow dropped and thrown items, projectiles, and other rigidbodies owned by this peer to cross linked Ravengates.");
			DefaultPortalTravel = BindEntry(config, "General", "DefaultPortalTravel", defaultValue: false, "For this player, Ravengates that follow the default use Valheim's normal loading-screen teleport: no live view, remote streaming or seamless crossing. A gate set Live or classic in the Ravengates list keeps its own setting.");
			ReentryClearance = BindRange(config, "General", "ReentryClearance", 0.1f, 0.02f, 0.5f, "After emerging from a portal, a return crossing is deferred until the traveller is this far (metres) from the exit plane on either side.");
			ToggleKey = BindEntry<KeyboardShortcut>(config, "General", "ToggleKey", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Opens or closes the in-game portal manager while in a world.");
			TiltKey = BindEntry<KeyboardShortcut>(config, "General", "TiltKey", new KeyboardShortcut((KeyCode)116, Array.Empty<KeyCode>()), "While placing a Ravengate with AllowTiltedPlacement on, tilts it a quarter turn: upright, lying flat (for floors and ceilings), upside down, flat the other way round.");
			MenuScale = BindRange(config, "General", "MenuScale", 1.5f, 1f, 2f, "Size of the in-game portal manager. It is shrunk further only when it would not fit the screen.");
			AllowCreatureTraversal = BindEntry(config, "Gameplay", "AllowCreatureTraversal", defaultValue: true, "Allows small enemies and animals to physically pass through connected Ravengates. Off, creatures neither cross nor route toward gates; players, items and projectiles are unaffected.", adminOnly: true);
			MigrateRenamed<bool>(config, new ConfigDefinition("General", "EnableCreatureTraversal"), AllowCreatureTraversal);
			MaxCreatureHeight = BindRange(config, "Gameplay", "MaxCreatureHeight", 2.6f, 0.5f, 6f, "Tallest creature collider (metres) that may cross. A creature must also fit the gate's measured opening (about 2.05 x 2.22 m).", adminOnly: true);
			MaxCreatureWidth = BindRange(config, "Gameplay", "MaxCreatureWidth", 1.8f, 0.2f, 6f, "Widest creature collider (metres) that may cross. A creature must also fit the gate's measured opening.", adminOnly: true);
			CreatureTraversalCooldown = BindRange(config, "Gameplay", "CreatureTraversalCooldown", 0.75f, 0f, 10f, "After crossing, a creature cannot go back through the exit gate for this many secon