Decompiled source of ForcedFriendship v0.3.1

ForcedFriendship.dll

Decompiled 17 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ForcedFriendship")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.3.1.0")]
[assembly: AssemblyInformationalVersion("0.3.1+ceac12ea65e5ec71605892d0a9ca796bc6a5a2fe")]
[assembly: AssemblyProduct("ForcedFriendship")]
[assembly: AssemblyTitle("ForcedFriendship")]
[assembly: AssemblyVersion("0.3.1.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 ForcedFriendship
{
	internal static class BeamColors
	{
		internal static Color For(BeamZone zone, bool colorblind)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_0044: 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)
			if (colorblind)
			{
				return (Color)(zone switch
				{
					BeamZone.Danger => new Color(0.84f, 0.15f, 0.2f), 
					BeamZone.Warn => new Color(0.95f, 0.85f, 0.15f), 
					_ => new Color(0.15f, 0.5f, 0.85f), 
				});
			}
			return (Color)(zone switch
			{
				BeamZone.Danger => new Color(0.85f, 0.2f, 0.16f), 
				BeamZone.Warn => new Color(0.85f, 0.7f, 0.15f), 
				_ => new Color(0.25f, 0.75f, 0.3f), 
			});
		}
	}
	internal class BeamRenderer : MonoBehaviour
	{
		private const float BeamHeight = 1f;

		private readonly Dictionary<PlayerAvatar, LineRenderer> _lines = new Dictionary<PlayerAvatar, LineRenderer>();

		private readonly List<PlayerAvatar> _current = new List<PlayerAvatar>();

		private readonly List<PlayerAvatar> _stale = new List<PlayerAvatar>();

		private Material? _material;

		private void Update()
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.ActiveEnabled || !Plugin.BeamsEnabled.Value || !Plugin.IsInGameplay())
			{
				HideAll();
				return;
			}
			StatusHud instance = StatusHud.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				HideAll();
				return;
			}
			PlayerAvatar instance2 = PlayerAvatar.instance;
			bool value = Plugin.BeamsShowAll.Value;
			bool value2 = Plugin.BeamsAlwaysShow.Value;
			bool value3 = Plugin.BeamsColorblind.Value;
			float beamWidthWorld = Plugin.BeamWidthWorld;
			float beamOpacity = Plugin.BeamOpacity;
			_current.Clear();
			int playerCount = instance.PlayerCount;
			for (int i = 0; i < playerCount; i++)
			{
				PlayerAvatar val = instance.AvatarAt(i);
				if (!((Object)(object)val == (Object)null))
				{
					_current.Add(val);
					AnchorResult anchorResult = instance.AnchorAt(i);
					BeamZone zone = instance.ZoneAt(i);
					if (!DamageCalculator.ShouldDrawBeam(zone, anchorResult.HasAnchor, value2) || (!value && !((Object)(object)val == (Object)(object)instance2)))
					{
						HideLine(val);
						continue;
					}
					LineRenderer line = GetLine(val);
					Vector3 val2 = instance.PlayerPosAt(i) + Vector3.up * 1f;
					Vector3 val3 = new Vector3(anchorResult.X, anchorResult.Y, anchorResult.Z) + Vector3.up * 1f;
					((Renderer)line).enabled = true;
					line.startWidth = beamWidthWorld;
					line.endWidth = beamWidthWorld;
					line.SetPosition(0, val2);
					line.SetPosition(1, val3);
					Color val4 = BeamColors.For(zone, value3);
					val4.a = beamOpacity;
					line.startColor = val4;
					line.endColor = val4;
				}
			}
			CleanupDeparted();
		}

		private void OnDisable()
		{
			HideAll();
		}

		private void OnDestroy()
		{
			if ((Object)(object)_material != (Object)null)
			{
				Object.Destroy((Object)(object)_material);
			}
		}

		private LineRenderer GetLine(PlayerAvatar pa)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (_lines.TryGetValue(pa, out LineRenderer value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			GameObject val = new GameObject("FF_Beam");
			val.transform.SetParent(((Component)this).transform, false);
			LineRenderer val2 = val.AddComponent<LineRenderer>();
			((Renderer)val2).material = BeamMaterial();
			val2.positionCount = 2;
			val2.numCapVertices = 2;
			val2.numCornerVertices = 2;
			val2.useWorldSpace = true;
			val2.textureMode = (LineTextureMode)0;
			val2.alignment = (LineAlignment)0;
			((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)val2).receiveShadows = false;
			((Renderer)val2).lightProbeUsage = (LightProbeUsage)0;
			_lines[pa] = val2;
			return val2;
		}

		private void HideLine(PlayerAvatar pa)
		{
			if (_lines.TryGetValue(pa, out LineRenderer value) && (Object)(object)value != (Object)null)
			{
				((Renderer)value).enabled = false;
			}
		}

		private void HideAll()
		{
			foreach (LineRenderer value in _lines.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					((Renderer)value).enabled = false;
				}
			}
		}

		private void CleanupDeparted()
		{
			_stale.Clear();
			foreach (PlayerAvatar key in _lines.Keys)
			{
				if ((Object)(object)key == (Object)null || !_current.Contains(key))
				{
					_stale.Add(key);
				}
			}
			foreach (PlayerAvatar item in _stale)
			{
				if (_lines.TryGetValue(item, out LineRenderer value) && (Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)value).gameObject);
				}
				_lines.Remove(item);
			}
		}

		private Material BeamMaterial()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			if ((Object)(object)_material == (Object)null)
			{
				Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("Unlit/Transparent");
				_material = new Material(val);
			}
			return _material;
		}
	}
	internal static class CartLocator
	{
		internal static List<PhysGrabCart> FindMainCarts(List<PhysGrabCart> buffer)
		{
			buffer.Clear();
			PhysGrabCart[] array = Object.FindObjectsOfType<PhysGrabCart>();
			PhysGrabCart val = null;
			PhysGrabCart[] array2 = array;
			foreach (PhysGrabCart val2 in array2)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					if (!val2.isSmallCart)
					{
						buffer.Add(val2);
					}
					else if (val == null)
					{
						val = val2;
					}
				}
			}
			if (buffer.Count == 0 && (Object)(object)val != (Object)null)
			{
				buffer.Add(val);
			}
			return buffer;
		}
	}
	public readonly struct PlayerState
	{
		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public readonly bool Alive;

		public readonly bool InTruck;

		public PlayerState(float x, float y, float z, bool alive, bool inTruck = false)
		{
			X = x;
			Y = y;
			Z = z;
			Alive = alive;
			InTruck = inTruck;
		}
	}
	public readonly struct DamageSettings
	{
		public readonly bool Enabled;

		public readonly float SafeDistance;

		public readonly float BandWidth;

		public readonly int DamagePerBand;

		public DamageSettings(bool enabled, float safeDistance, float bandWidth, int damagePerBand)
		{
			Enabled = enabled;
			SafeDistance = safeDistance;
			BandWidth = bandWidth;
			DamagePerBand = damagePerBand;
		}
	}
	public enum AnchorMode
	{
		Buddy,
		Cart
	}
	public enum BeamZone
	{
		Safe,
		Warn,
		Danger
	}
	public readonly struct Vec3
	{
		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public Vec3(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}
	}
	public readonly struct AnchorResult
	{
		public readonly float X;

		public readonly float Y;

		public readonly float Z;

		public readonly float Distance;

		public readonly bool HasAnchor;

		public readonly bool Safe;

		public static AnchorResult None => new AnchorResult(0f, 0f, 0f, 0f, hasAnchor: false);

		public AnchorResult(float x, float y, float z, float distance, bool hasAnchor, bool safe = false)
		{
			X = x;
			Y = y;
			Z = z;
			Distance = distance;
			HasAnchor = hasAnchor;
			Safe = safe;
		}
	}
	public static class DamageCalculator
	{
		public static float Distance(in PlayerState a, in PlayerState b)
		{
			return Distance(in a, in b, includeHeight: true);
		}

		public static float Distance(in PlayerState a, in PlayerState b, bool includeHeight)
		{
			float num = a.X - b.X;
			float num2 = (includeHeight ? (a.Y - b.Y) : 0f);
			float num3 = a.Z - b.Z;
			return (float)Math.Sqrt(num * num + num2 * num2 + num3 * num3);
		}

		private static float Distance(in PlayerState a, in Vec3 b, bool includeHeight)
		{
			float num = a.X - b.X;
			float num2 = (includeHeight ? (a.Y - b.Y) : 0f);
			float num3 = a.Z - b.Z;
			return (float)Math.Sqrt(num * num + num2 * num2 + num3 * num3);
		}

		public static int Band(float distance, float safeDistance, float bandWidth)
		{
			if (distance <= safeDistance)
			{
				return 0;
			}
			if (bandWidth <= 0f)
			{
				return 1;
			}
			return (int)Math.Floor((distance - safeDistance) / bandWidth) + 1;
		}

		public static AnchorResult[] ResolveAnchors(IReadOnlyList<PlayerState> players, AnchorMode mode, IReadOnlyList<Vec3> carts, bool includeHeight = true)
		{
			AnchorResult[] array = new AnchorResult[players.Count];
			bool flag = mode == AnchorMode.Cart && carts != null && carts.Count > 0;
			for (int i = 0; i < players.Count; i++)
			{
				PlayerState a = players[i];
				if (!a.Alive)
				{
					array[i] = AnchorResult.None;
					continue;
				}
				if (flag)
				{
					float num = float.PositiveInfinity;
					int index = -1;
					for (int j = 0; j < carts.Count; j++)
					{
						float num2 = Distance(in a, carts[j], includeHeight);
						if (num2 < num)
						{
							num = num2;
							index = j;
						}
					}
					Vec3 vec = carts[index];
					array[i] = new AnchorResult(vec.X, vec.Y, vec.Z, num, hasAnchor: true, a.InTruck);
					continue;
				}
				float num3 = float.PositiveInfinity;
				int num4 = -1;
				bool flag2 = false;
				for (int k = 0; k < players.Count; k++)
				{
					if (k == i)
					{
						continue;
					}
					PlayerState b = players[k];
					if (b.Alive)
					{
						if (!b.InTruck)
						{
							flag2 = true;
						}
						float num5 = Distance(in a, in b, includeHeight);
						if (num5 < num3)
						{
							num3 = num5;
							num4 = k;
						}
					}
				}
				if (num4 < 0)
				{
					array[i] = AnchorResult.None;
					continue;
				}
				bool safe = a.InTruck || !flag2;
				PlayerState playerState = players[num4];
				array[i] = new AnchorResult(playerState.X, playerState.Y, playerState.Z, num3, hasAnchor: true, safe);
			}
			return array;
		}

		public static BeamZone Classify(float distance, float safeDistance, float warnPercent)
		{
			if (distance > safeDistance)
			{
				return BeamZone.Danger;
			}
			if (warnPercent <= 0f)
			{
				return BeamZone.Safe;
			}
			float num = ((warnPercent > 1f) ? 1f : warnPercent);
			float num2 = safeDistance * (1f - num);
			if (!(distance >= num2))
			{
				return BeamZone.Safe;
			}
			return BeamZone.Warn;
		}

		public static int[] EvaluateDamage(IReadOnlyList<AnchorResult> anchors, in DamageSettings s)
		{
			int[] array = new int[anchors.Count];
			if (!s.Enabled)
			{
				return array;
			}
			for (int i = 0; i < anchors.Count; i++)
			{
				AnchorResult anchorResult = anchors[i];
				if (anchorResult.HasAnchor && !anchorResult.Safe)
				{
					int num = Band(anchorResult.Distance, s.SafeDistance, s.BandWidth);
					array[i] = num * s.DamagePerBand;
				}
			}
			return array;
		}

		public static BeamZone ZoneForAnchor(in AnchorResult a, float safeDistance, float warnPercent)
		{
			if (!a.Safe)
			{
				return Classify(a.Distance, safeDistance, warnPercent);
			}
			return BeamZone.Safe;
		}

		public static bool ShouldDrawBeam(BeamZone zone, bool hasAnchor, bool alwaysShow)
		{
			if (hasAnchor)
			{
				if (!alwaysShow)
				{
					return zone != BeamZone.Safe;
				}
				return true;
			}
			return false;
		}

		public static int[] Evaluate(IReadOnlyList<PlayerState> players, in DamageSettings s)
		{
			return EvaluateDamage(ResolveAnchors(players, AnchorMode.Buddy, Array.Empty<Vec3>()), in s);
		}
	}
	internal class ForcedFriendshipDriver : MonoBehaviour
	{
		private float _accum;

		private readonly List<PlayerState> _states = new List<PlayerState>();

		private readonly List<PlayerAvatar> _avatars = new List<PlayerAvatar>();

		private readonly List<PhysGrabCart> _carts = new List<PhysGrabCart>();

		private readonly List<Vec3> _cartPositions = new List<Vec3>();

		private void Update()
		{
			//IL_009b: 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_00ba: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.ActiveEnabled || (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) || !Plugin.IsInGameplay())
			{
				return;
			}
			_accum += Time.deltaTime;
			if (_accum < (float)Plugin.ActiveTickInterval)
			{
				return;
			}
			_accum = 0f;
			List<PlayerAvatar> list = GameDirector.instance?.PlayerList;
			if (list == null)
			{
				return;
			}
			_states.Clear();
			_avatars.Clear();
			foreach (PlayerAvatar item in list)
			{
				if (!((Object)(object)item == (Object)null))
				{
					Vector3 position = ((Component)item).transform.position;
					bool alive = PlayerLiveness.IsAlive(item);
					bool inTruck = PlayerLiveness.IsInTruck(item);
					_states.Add(new PlayerState(position.x, position.y, position.z, alive, inTruck));
					_avatars.Add(item);
				}
			}
			DamageSettings s = new DamageSettings(enabled: true, Plugin.ActiveSafeDistance, Plugin.ActiveBandWidth, Plugin.ActiveDamagePerBand);
			_cartPositions.Clear();
			if (Plugin.ActiveMode == AnchorMode.Cart)
			{
				CartLocator.FindMainCarts(_carts);
				foreach (PhysGrabCart cart in _carts)
				{
					if (!((Object)(object)cart == (Object)null))
					{
						Vector3 position2 = ((Component)cart).transform.position;
						_cartPositions.Add(new Vec3(position2.x, position2.y, position2.z));
					}
				}
			}
			int[] array = DamageCalculator.EvaluateDamage(DamageCalculator.ResolveAnchors(_states, Plugin.ActiveMode, _cartPositions, Plugin.ActiveIncludeHeight), in s);
			StatusHud instance = StatusHud.Instance;
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] > 0)
				{
					PlayerAvatar val = _avatars[i];
					if ((!((Object)(object)instance != (Object)null) || !instance.IsInGrace(val)) && !((Object)(object)val.playerHealth == (Object)null))
					{
						val.playerHealth.HurtOther(array[i], Vector3.zero, false, -1, false);
					}
				}
			}
		}
	}
	internal static class PlayerLiveness
	{
		private static readonly FieldRef<PlayerAvatar, bool> DeadSetRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("deadSet");

		private static readonly FieldRef<PlayerAvatar, bool> IsDisabledRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isDisabled");

		private static readonly FieldRef<RoomVolumeCheck, bool> InTruckRef = AccessTools.FieldRefAccess<RoomVolumeCheck, bool>("inTruck");

		internal static bool IsAlive(PlayerAvatar pa)
		{
			if (!DeadSetRef.Invoke(pa))
			{
				return !IsDisabledRef.Invoke(pa);
			}
			return false;
		}

		internal static bool IsInTruck(PlayerAvatar pa)
		{
			RoomVolumeCheck roomVolumeCheck = pa.RoomVolumeCheck;
			if ((Object)(object)roomVolumeCheck != (Object)null)
			{
				return InTruckRef.Invoke(roomVolumeCheck);
			}
			return false;
		}
	}
	[BepInPlugin("darkharasho.ForcedFriendship", "ForcedFriendship", "0.3.1")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<int> SafeDistance;

		internal static ConfigEntry<int> BandWidth;

		internal static ConfigEntry<int> DamagePerBand;

		internal static ConfigEntry<int> TickInterval;

		internal static ConfigEntry<bool> IncludeHeight;

		internal static ConfigEntry<int> GracePeriod;

		internal static ConfigEntry<AnchorMode> Mode;

		internal static ConfigEntry<bool> BeamsEnabled;

		internal static ConfigEntry<bool> BeamsShowAll;

		internal static ConfigEntry<bool> BeamsAlwaysShow;

		internal static ConfigEntry<int> BeamsWarnPercent;

		internal static ConfigEntry<int> BeamsWidth;

		internal static ConfigEntry<int> BeamsOpacity;

		internal static ConfigEntry<bool> BeamsColorblind;

		internal static ConfigEntry<bool> StatusIndicator;

		internal static bool ActiveEnabled;

		internal static AnchorMode ActiveMode;

		internal static int ActiveSafeDistance;

		internal static int ActiveBandWidth;

		internal static int ActiveDamagePerBand;

		internal static int ActiveTickInterval;

		internal static bool ActiveIncludeHeight;

		internal static int ActiveGracePeriod;

		internal static float BeamWidthWorld => (float)BeamsWidth.Value * 0.01f;

		internal static float WarnFraction => (float)BeamsWarnPercent.Value / 100f;

		internal static float BeamOpacity => (float)BeamsOpacity.Value / 100f;

		private void Awake()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Expected O, but got Unknown
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Expected O, but got Unknown
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Expected O, but got Unknown
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Expected O, but got Unknown
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Expected O, but got Unknown
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master on/off switch for Forced Friendship.");
			SafeDistance = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SafeDistance", 20, new ConfigDescription("Units within which your anchor (nearest player or cart) keeps you safe.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			BandWidth = ((BaseUnityPlugin)this).Config.Bind<int>("General", "BandWidth", 8, new ConfigDescription("Units per additional damage band beyond the safe radius.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			DamagePerBand = ((BaseUnityPlugin)this).Config.Bind<int>("General", "DamagePerBand", 5, new ConfigDescription("HP per tick, multiplied by the band number.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			TickInterval = ((BaseUnityPlugin)this).Config.Bind<int>("General", "TickInterval", 2, new ConfigDescription("Seconds between damage evaluations.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 30), Array.Empty<object>()));
			IncludeHeight = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeHeight", false, "If true, vertical distance counts toward the safe radius. Default false so being on a different floor of the same tall room doesn't trigger damage.");
			GracePeriod = ((BaseUnityPlugin)this).Config.Bind<int>("General", "GracePeriod", 30, new ConfigDescription("Seconds of safety after leaving the truck before damage can start. 0 disables.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 60), Array.Empty<object>()));
			Mode = ((BaseUnityPlugin)this).Config.Bind<AnchorMode>("General", "AnchorMode", AnchorMode.Buddy, "Buddy = stay near the nearest living player (default). Cart = stay near the nearest hauling cart instead.");
			BeamsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Beams", "Enabled", true, "Draw a tether beam from each player to their anchor.");
			BeamsShowAll = ((BaseUnityPlugin)this).Config.Bind<bool>("Beams", "ShowAllPlayers", true, "Show beams for every living player. If false, only your own beam is drawn.");
			BeamsAlwaysShow = ((BaseUnityPlugin)this).Config.Bind<bool>("Beams", "AlwaysShow", true, "Always draw the tether (color conveys safety). If false, the beam hides while you're safe and only appears in the warn/danger zone.");
			BeamsWarnPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Beams", "WarnPercent", 25, new ConfigDescription("Percent of SafeDistance, at the outer edge, where the beam turns yellow before it turns red. 0 disables the yellow zone.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			BeamsWidth = ((BaseUnityPlugin)this).Config.Bind<int>("Beams", "Width", 2, new ConfigDescription("Tether thickness (1 = thinnest). The default approximates the game's grab beam.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			BeamsOpacity = ((BaseUnityPlugin)this).Config.Bind<int>("Beams", "Opacity", 40, new ConfigDescription("Beam opacity percent (1 = faint, 100 = solid). Lower is more translucent.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			BeamsColorblind = ((BaseUnityPlugin)this).Config.Bind<bool>("Beams", "Colorblind", false, "Use a colorblind-friendly palette (blue safe / yellow warn / red danger) instead of green/yellow/red. Local to you — not synced from the host.");
			StatusIndicator = ((BaseUnityPlugin)this).Config.Bind<bool>("Beams", "StatusIndicator", false, "Show a subtle screen-edge border tinted by your current safety color (green/yellow/red) so you always know your status. Local to you.");
			ResetToLocalConfig();
			new Harmony("darkharasho.ForcedFriendship").PatchAll();
			Log.LogInfo((object)"ForcedFriendship v0.3.1 loaded.");
			((Component)this).gameObject.AddComponent<StatusHud>();
			((Component)this).gameObject.AddComponent<ForcedFriendshipDriver>();
			((Component)this).gameObject.AddComponent<BeamRenderer>();
			((Component)this).gameObject.AddComponent<SettingsSyncer>();
		}

		internal static void ResetToLocalConfig()
		{
			ActiveEnabled = Enabled.Value;
			ActiveMode = Mode.Value;
			ActiveSafeDistance = SafeDistance.Value;
			ActiveBandWidth = BandWidth.Value;
			ActiveDamagePerBand = DamagePerBand.Value;
			ActiveTickInterval = TickInterval.Value;
			ActiveIncludeHeight = IncludeHeight.Value;
			ActiveGracePeriod = GracePeriod.Value;
		}

		internal static bool IsInGameplay()
		{
			if (!PhotonNetwork.InRoom)
			{
				return false;
			}
			RunManager instance = RunManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			Level levelCurrent = instance.levelCurrent;
			if ((Object)(object)levelCurrent == (Object)null)
			{
				return false;
			}
			if ((Object)(object)levelCurrent == (Object)(object)instance.levelLobby || (Object)(object)levelCurrent == (Object)(object)instance.levelLobbyMenu)
			{
				return false;
			}
			if (SemiFunc.IsLevelShop(levelCurrent))
			{
				return false;
			}
			return true;
		}
	}
	internal class SettingsSyncer : MonoBehaviour
	{
		private const string K_ENABLED = "FF_EN";

		private const string K_MODE = "FF_MODE";

		private const string K_SAFE = "FF_SAFE";

		private const string K_BAND = "FF_BAND";

		private const string K_DMG = "FF_DMG";

		private const string K_TICK = "FF_TICK";

		private const string K_HEIGHT = "FF_HGT";

		private const string K_GRACE = "FF_GRC";

		internal static SettingsSyncer? Instance;

		private bool _wasInRoom;

		private float _pullPollDelay;

		private bool? _lastEnabled;

		private int _lastMode = int.MinValue;

		private int _lastSafe = int.MinValue;

		private int _lastBand = int.MinValue;

		private int _lastTick = int.MinValue;

		private int _lastDmg = int.MinValue;

		private bool? _lastHeight;

		private int _lastGrace = int.MinValue;

		private void Awake()
		{
			Instance = this;
		}

		private void Start()
		{
			Plugin.Log.LogInfo((object)"[Sync] SettingsSyncer ready (polling mode)");
		}

		private void Update()
		{
			bool inRoom = PhotonNetwork.InRoom;
			if (inRoom && PhotonNetwork.IsMasterClient)
			{
				PushHostSettings();
			}
			else if (inRoom)
			{
				if (!_wasInRoom)
				{
					PullHostSettings();
					_pullPollDelay = 1f;
				}
				else
				{
					_pullPollDelay -= Time.unscaledDeltaTime;
					if (_pullPollDelay <= 0f)
					{
						_pullPollDelay = 1f;
						PullHostSettings();
					}
				}
			}
			else
			{
				if (_wasInRoom)
				{
					Plugin.Log.LogInfo((object)"[Sync] Left room");
				}
				Plugin.ResetToLocalConfig();
			}
			_wasInRoom = inRoom;
		}

		private void PullHostSettings()
		{
			Room currentRoom = PhotonNetwork.CurrentRoom;
			Hashtable val = ((currentRoom != null) ? ((RoomInfo)currentRoom).CustomProperties : null);
			if (val == null)
			{
				return;
			}
			bool flag = false;
			if (((Dictionary<object, object>)(object)val).ContainsKey((object)"FF_EN"))
			{
				bool flag2 = (bool)val[(object)"FF_EN"];
				if (Plugin.ActiveEnabled != flag2)
				{
					Plugin.ActiveEnabled = flag2;
					flag = true;
				}
			}
			if (((Dictionary<object, object>)(object)val).ContainsKey((object)"FF_MODE"))
			{
				AnchorMode anchorMode = (AnchorMode)(int)val[(object)"FF_MODE"];
				if (Plugin.ActiveMode != anchorMode)
				{
					Plugin.ActiveMode = anchorMode;
					flag = true;
				}
			}
			if (((Dictionary<object, object>)(object)val).ContainsKey((object)"FF_SAFE"))
			{
				int num = (int)val[(object)"FF_SAFE"];
				if (Plugin.ActiveSafeDistance != num)
				{
					Plugin.ActiveSafeDistance = num;
					flag = true;
				}
			}
			if (((Dictionary<object, object>)(object)val).ContainsKey((object)"FF_BAND"))
			{
				int num2 = (int)val[(object)"FF_BAND"];
				if (Plugin.ActiveBandWidth != num2)
				{
					Plugin.ActiveBandWidth = num2;
					flag = true;
				}
			}
			if (((Dictionary<object, object>)(object)val).ContainsKey((object)"FF_DMG"))
			{
				int num3 = (int)val[(object)"FF_DMG"];
				if (Plugin.ActiveDamagePerBand != num3)
				{
					Plugin.ActiveDamagePerBand = num3;
					flag = true;
				}
			}
			if (((Dictionary<object, object>)(object)val).ContainsKey((object)"FF_TICK"))
			{
				int num4 = (int)val[(object)"FF_TICK"];
				if (Plugin.ActiveTickInterval != num4)
				{
					Plugin.ActiveTickInterval = num4;
					flag = true;
				}
			}
			if (((Dictionary<object, object>)(object)val).ContainsKey((object)"FF_HGT"))
			{
				bool flag3 = (bool)val[(object)"FF_HGT"];
				if (Plugin.ActiveIncludeHeight != flag3)
				{
					Plugin.ActiveIncludeHeight = flag3;
					flag = true;
				}
			}
			if (((Dictionary<object, object>)(object)val).ContainsKey((object)"FF_GRC"))
			{
				int num5 = (int)val[(object)"FF_GRC"];
				if (Plugin.ActiveGracePeriod != num5)
				{
					Plugin.ActiveGracePeriod = num5;
					flag = true;
				}
			}
			if (flag)
			{
				Plugin.Log.LogInfo((object)$"[Sync] Pulled host rule — enabled={Plugin.ActiveEnabled} mode={Plugin.ActiveMode} safe={Plugin.ActiveSafeDistance} band={Plugin.ActiveBandWidth} dmg={Plugin.ActiveDamagePerBand} tick={Plugin.ActiveTickInterval}");
			}
		}

		private void PushHostSettings()
		{
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Expected O, but got Unknown
			if (PhotonNetwork.CurrentRoom != null)
			{
				bool value = Plugin.Enabled.Value;
				int value2 = (int)Plugin.Mode.Value;
				int value3 = Plugin.SafeDistance.Value;
				int value4 = Plugin.BandWidth.Value;
				int value5 = Plugin.TickInterval.Value;
				int value6 = Plugin.DamagePerBand.Value;
				bool value7 = Plugin.IncludeHeight.Value;
				int value8 = Plugin.GracePeriod.Value;
				if (value == _lastEnabled && value2 == _lastMode && value3 == _lastSafe && value4 == _lastBand && value6 == _lastDmg && value5 == _lastTick && value7 == _lastHeight && value8 == _lastGrace)
				{
					Plugin.ResetToLocalConfig();
					return;
				}
				_lastEnabled = value;
				_lastMode = value2;
				_lastSafe = value3;
				_lastBand = value4;
				_lastDmg = value6;
				_lastTick = value5;
				_lastHeight = value7;
				_lastGrace = value8;
				Hashtable val = new Hashtable
				{
					[(object)"FF_EN"] = value,
					[(object)"FF_MODE"] = value2,
					[(object)"FF_SAFE"] = value3,
					[(object)"FF_BAND"] = value4,
					[(object)"FF_DMG"] = value6,
					[(object)"FF_TICK"] = value5,
					[(object)"FF_HGT"] = value7,
					[(object)"FF_GRC"] = value8
				};
				PhotonNetwork.CurrentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
				Plugin.ResetToLocalConfig();
				Plugin.Log.LogInfo((object)$"[Sync] Host pushed rule — enabled={value} mode={(AnchorMode)value2} safe={value3} band={value4} dmg={value6} tick={value5} height={value7} grace={value8}");
			}
		}
	}
	internal class StatusHud : MonoBehaviour
	{
		internal static StatusHud? Instance;

		private readonly Dictionary<PlayerAvatar, float> _grace = new Dictionary<PlayerAvatar, float>();

		private readonly List<PlayerAvatar> _stale = new List<PlayerAvatar>();

		private readonly List<PlayerState> _states = new List<PlayerState>();

		private readonly List<PlayerAvatar> _avatars = new List<PlayerAvatar>();

		private readonly List<PhysGrabCart> _carts = new List<PhysGrabCart>();

		private readonly List<Vec3> _cartPositions = new List<Vec3>();

		private float _cartRescan;

		private AnchorResult[] _anchors = Array.Empty<AnchorResult>();

		private BeamZone[] _zones = Array.Empty<BeamZone>();

		private static Texture2D? _tex;

		private GUIStyle? _timerStyle;

		internal int PlayerCount { get; private set; }

		internal BeamZone LocalZone { get; private set; }

		internal bool LocalActive { get; private set; }

		internal PlayerAvatar AvatarAt(int i)
		{
			return _avatars[i];
		}

		internal AnchorResult AnchorAt(int i)
		{
			return _anchors[i];
		}

		internal BeamZone ZoneAt(int i)
		{
			return _zones[i];
		}

		internal Vector3 PlayerPosAt(int i)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			PlayerState playerState = _states[i];
			return new Vector3(playerState.X, playerState.Y, playerState.Z);
		}

		private void Awake()
		{
			Instance = this;
		}

		internal bool IsInGrace(PlayerAvatar pa)
		{
			if (Plugin.ActiveGracePeriod > 0 && _grace.TryGetValue(pa, out var value))
			{
				return value > 0f;
			}
			return false;
		}

		internal float Remaining(PlayerAvatar pa)
		{
			if (!_grace.TryGetValue(pa, out var value))
			{
				return 0f;
			}
			return value;
		}

		private void Update()
		{
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.ActiveEnabled || !Plugin.IsInGameplay())
			{
				_grace.Clear();
				PlayerCount = 0;
				LocalActive = false;
				return;
			}
			List<PlayerAvatar> list = GameDirector.instance?.PlayerList;
			if (list == null)
			{
				PlayerCount = 0;
				LocalActive = false;
				return;
			}
			float num = Plugin.ActiveGracePeriod;
			float deltaTime = Time.deltaTime;
			_states.Clear();
			_avatars.Clear();
			foreach (PlayerAvatar item in list)
			{
				if (!((Object)(object)item == (Object)null))
				{
					bool flag = PlayerLiveness.IsInTruck(item);
					if (!_grace.TryGetValue(item, out var value))
					{
						value = num;
					}
					value = (flag ? num : Mathf.Max(0f, value - deltaTime));
					if (value > num)
					{
						value = num;
					}
					_grace[item] = value;
					Vector3 position = ((Component)item).transform.position;
					_states.Add(new PlayerState(position.x, position.y, position.z, PlayerLiveness.IsAlive(item), flag));
					_avatars.Add(item);
				}
			}
			_stale.Clear();
			foreach (PlayerAvatar key in _grace.Keys)
			{
				if ((Object)(object)key == (Object)null || !_avatars.Contains(key))
				{
					_stale.Add(key);
				}
			}
			foreach (PlayerAvatar item2 in _stale)
			{
				_grace.Remove(item2);
			}
			AnchorMode activeMode = Plugin.ActiveMode;
			_cartRescan -= deltaTime;
			if (activeMode != AnchorMode.Cart)
			{
				_carts.Clear();
			}
			else if (_carts.Count == 0 || _cartRescan <= 0f)
			{
				CartLocator.FindMainCarts(_carts);
				_cartRescan = 1f;
			}
			_cartPositions.Clear();
			if (activeMode == AnchorMode.Cart)
			{
				foreach (PhysGrabCart cart in _carts)
				{
					if (!((Object)(object)cart == (Object)null))
					{
						Vector3 position2 = ((Component)cart).transform.position;
						_cartPositions.Add(new Vec3(position2.x, position2.y, position2.z));
					}
				}
			}
			_anchors = DamageCalculator.ResolveAnchors(_states, activeMode, _cartPositions, Plugin.ActiveIncludeHeight);
			int count = _states.Count;
			if (_zones.Length < count)
			{
				_zones = new BeamZone[count];
			}
			float safeDistance = Plugin.ActiveSafeDistance;
			float warnFraction = Plugin.WarnFraction;
			for (int i = 0; i < count; i++)
			{
				BeamZone beamZone = DamageCalculator.ZoneForAnchor(in _anchors[i], safeDistance, warnFraction);
				if (IsInGrace(_avatars[i]))
				{
					beamZone = BeamZone.Safe;
				}
				_zones[i] = beamZone;
			}
			PlayerCount = count;
			LocalActive = false;
			PlayerAvatar instance = PlayerAvatar.instance;
			if ((Object)(object)instance != (Object)null)
			{
				int num2 = _avatars.IndexOf(instance);
				if (num2 >= 0)
				{
					LocalZone = _zones[num2];
					LocalActive = true;
				}
			}
		}

		private void OnGUI()
		{
			//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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			if (!LocalActive)
			{
				return;
			}
			PlayerAvatar instance = PlayerAvatar.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if (Plugin.ActiveGracePeriod > 0 && !PlayerLiveness.IsInTruck(instance))
			{
				float num = Remaining(instance);
				if (num > 0f)
				{
					DrawTimer(Mathf.CeilToInt(num));
				}
			}
			if (Plugin.StatusIndicator.Value)
			{
				Color c = BeamColors.For(LocalZone, Plugin.BeamsColorblind.Value);
				c.a = 0.5f;
				DrawBorder(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), c, 4f);
			}
		}

		private void DrawTimer(int seconds)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			if (_timerStyle == null)
			{
				_timerStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 22,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4
				};
			}
			Rect val = default(Rect);
			((Rect)(ref val))..ctor((float)Screen.width * 0.5f - 110f, (float)Screen.height * 0.16f, 220f, 32f);
			Color color = GUI.color;
			GUI.color = new Color(0f, 0f, 0f, 0.6f);
			GUI.Label(new Rect(((Rect)(ref val)).x + 1f, ((Rect)(ref val)).y + 1f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), $"Safe for {seconds}s", _timerStyle);
			GUI.color = Color.white;
			GUI.Label(val, $"Safe for {seconds}s", _timerStyle);
			GUI.color = color;
		}

		private static void DrawBorder(Rect r, Color c, float t)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = WhiteTex();
			Color color = GUI.color;
			GUI.color = c;
			GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width, t), (Texture)(object)val);
			GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).yMax - t, ((Rect)(ref r)).width, t), (Texture)(object)val);
			GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, t, ((Rect)(ref r)).height), (Texture)(object)val);
			GUI.DrawTexture(new Rect(((Rect)(ref r)).xMax - t, ((Rect)(ref r)).y, t, ((Rect)(ref r)).height), (Texture)(object)val);
			GUI.color = color;
		}

		private static Texture2D WhiteTex()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_tex == (Object)null)
			{
				_tex = new Texture2D(1, 1);
				_tex.SetPixel(0, 0, Color.white);
				_tex.Apply();
			}
			return _tex;
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "darkharasho.ForcedFriendship";

		public const string PLUGIN_NAME = "ForcedFriendship";

		public const string PLUGIN_VERSION = "0.3.1";
	}
}