Decompiled source of CombatModes v2.0.0

HowToFish.CombatModes.Core.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using HowToFish.CombatModes.Core.Arena;
using Microsoft.CodeAnalysis;

[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("HowToFish.CombatModes.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d1a74e7102bbfa4f5efbca13639093f1f31d4ac8")]
[assembly: AssemblyProduct("HowToFish.CombatModes.Core")]
[assembly: AssemblyTitle("HowToFish.CombatModes.Core")]
[assembly: AssemblyVersion("1.0.0.0")]
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;
		}
	}
	internal static class IsExternalInit
	{
	}
}
namespace HowToFish.CombatModes.Core
{
	public sealed record SpawnPoint(Vector3 Position, float Yaw);
	public static class ArenaGeometry
	{
		public const float FallDepth = 10f;

		public static (SpawnPoint A, SpawnPoint B) AutoSpawns(Vector3 center, float radius)
		{
			Vector3 vector = new Vector3(radius / 2f, 0f, 0f);
			return Facing(center - vector, center + vector);
		}

		public static (SpawnPoint A, SpawnPoint B) Facing(Vector3 a, Vector3 b)
		{
			return (A: new SpawnPoint(a, YawTowards(a, b)), B: new SpawnPoint(b, YawTowards(b, a)));
		}

		public static SpawnPoint For(Side side, bool hostAtSpawnA, (SpawnPoint A, SpawnPoint B) spawns)
		{
			if (side == Side.Host != hostAtSpawnA)
			{
				return spawns.B;
			}
			return spawns.A;
		}

		public static bool IsOutside(Vector3 position, Vector3 center, float radius)
		{
			float num = position.X - center.X;
			float num2 = position.Z - center.Z;
			if (!(num * num + num2 * num2 > radius * radius))
			{
				return position.Y < center.Y - 10f;
			}
			return true;
		}

		public static float YawTowards(Vector3 from, Vector3 to)
		{
			return ((float)(Math.Atan2(to.X - from.X, to.Z - from.Z) * 180.0 / Math.PI) + 360f) % 360f;
		}
	}
	public enum CombatScreen
	{
		None,
		Lobby,
		Countdown,
		Fight,
		End
	}
	public enum BannerTone
	{
		Good,
		Bad,
		Neutral
	}
	public static class CombatUiRules
	{
		public static CombatScreen ScreenFor(DuelPhase? phase)
		{
			switch (phase)
			{
			case null:
			case DuelPhase.Waiting:
				return CombatScreen.Lobby;
			case DuelPhase.Preparing:
			case DuelPhase.Countdown:
				return CombatScreen.Countdown;
			case DuelPhase.MatchEnd:
				return CombatScreen.End;
			default:
				return CombatScreen.Fight;
			}
		}

		public static CombatScreen ScreenFor(ArenaPhase? phase)
		{
			switch (phase)
			{
			case null:
			case ArenaPhase.Lobby:
				return CombatScreen.Lobby;
			case ArenaPhase.Preparing:
			case ArenaPhase.Countdown:
				return CombatScreen.Countdown;
			case ArenaPhase.Ended:
				return CombatScreen.End;
			default:
				return CombatScreen.Fight;
			}
		}

		public static bool BlocksJournal(bool inCombatLobby)
		{
			return inCombatLobby;
		}

		public static bool FreesCursor(CombatScreen screen)
		{
			if (screen == CombatScreen.Lobby || screen == CombatScreen.End)
			{
				return true;
			}
			return false;
		}

		public static bool BlocksInputs(CombatScreen screen)
		{
			return FreesCursor(screen);
		}

		public static bool BlocksGameRespawn(bool inCombatLobby)
		{
			return inCombatLobby;
		}

		public static bool HidesServerButtons(bool inCombatLobby)
		{
			return inCombatLobby;
		}
	}
	public static class DuelCodec
	{
		public const string Prefix = "HTFCM";

		public const int ProtocolVersion = 1;

		private const int StateFieldCount = 14;

		private static readonly int[] NumericFields = new int[10] { 3, 4, 5, 6, 7, 9, 10, 11, 12, 13 };

		public static string Encode(DuelMessage m)
		{
			return m.Kind switch
			{
				MessageKind.Hello => string.Format("{0}|{1}|HELLO", "HTFCM", 1), 
				MessageKind.Rematch => string.Format("{0}|{1}|REQ|REMATCH", "HTFCM", 1), 
				_ => EncodeState(m.State ?? throw new ArgumentException("STATE without state", "m")), 
			};
		}

		private static string EncodeState(WireState s)
		{
			return string.Join("|", "HTFCM", 1, "STATE", (int)s.Phase, s.Round, s.HostScore, s.GuestScore, s.RemainingMs, s.KitName, SideCode(s.LastRoundWinner), Bit(s.LastRoundDraw), SideCode(s.MatchWinner), Bit(s.Forfeit), Bit(s.HostAtSpawnA));
		}

		public static bool TryDecode(string text, out DuelMessage? message, out string error)
		{
			message = null;
			string[] array = text.Split('|');
			if (array.Length < 3 || array[0] != "HTFCM")
			{
				return Fail("missing prefix", out error);
			}
			if (!int.TryParse(array[1], NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result != 1)
			{
				return Fail("unknown version '" + array[1] + "'", out error);
			}
			if (array.Length == 3 && array[2] == "HELLO")
			{
				message = DuelMessage.Hello();
			}
			else
			{
				if (array.Length != 4 || !(array[2] == "REQ") || !(array[3] == "REMATCH"))
				{
					if (array[2] == "STATE")
					{
						return TryDecodeState(array, out message, out error);
					}
					return Fail("unknown message type '" + array[2] + "'", out error);
				}
				message = DuelMessage.Rematch();
			}
			error = "";
			return true;
		}

		private static bool TryDecodeState(string[] p, out DuelMessage? message, out string error)
		{
			message = null;
			if (p.Length != 14)
			{
				return Fail($"field count {p.Length} instead of {14}", out error);
			}
			int[] array = new int[NumericFields.Length];
			for (int i = 0; i < NumericFields.Length; i++)
			{
				string text = p[NumericFields[i]];
				if (!int.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out array[i]))
				{
					return Fail("invalid number '" + text + "'", out error);
				}
			}
			if (!Enum.IsDefined(typeof(DuelPhase), array[0]))
			{
				return Fail($"unknown phase {array[0]}", out error);
			}
			if (array[1] < 0 || array[2] < 0 || array[3] < 0 || array[4] < 0)
			{
				return Fail("negative value", out error);
			}
			if (!IsSide(array[5]) || !IsSide(array[7]))
			{
				return Fail("invalid side", out error);
			}
			if (!IsBit(array[6]) || !IsBit(array[8]) || !IsBit(array[9]))
			{
				return Fail("invalid boolean", out error);
			}
			WireState state = new WireState((DuelPhase)array[0], array[1], array[2], array[3], array[4], p[8], ToSide(array[5]), array[6] == 1, ToSide(array[7]), array[8] == 1, array[9] == 1);
			message = DuelMessage.Of(state);
			error = "";
			return true;
		}

		private static bool Fail(string reason, out string error)
		{
			error = reason;
			return false;
		}

		private static int SideCode(Side? side)
		{
			if (side.HasValue)
			{
				return (int)side.Value;
			}
			return -1;
		}

		private static Side? ToSide(int code)
		{
			if (code >= 0)
			{
				return (Side)code;
			}
			return null;
		}

		private static bool IsSide(int code)
		{
			if (code >= -1)
			{
				return code <= 1;
			}
			return false;
		}

		private static bool IsBit(int value)
		{
			if (value != 0)
			{
				return value == 1;
			}
			return true;
		}

		private static int Bit(bool value)
		{
			return value ? 1 : 0;
		}
	}
	public enum DuelPhase
	{
		Waiting,
		Preparing,
		Countdown,
		Fighting,
		RoundEnd,
		MatchEnd
	}
	public enum DuelEffect
	{
		None,
		PrepareRound,
		StartFight,
		AnnounceRoundResult,
		AnnounceMatchResult,
		ResetToWaiting
	}
	public sealed record DuelSnapshot(DuelPhase Phase, int Round, int HostScore, int GuestScore, double PhaseEndsAt, Kit? Kit, Side? LastRoundWinner, bool LastRoundDraw, Side? MatchWinner, bool Forfeit, bool HostAtSpawnA, double RoundDecidedAt, int Island)
	{
		public static DuelSnapshot Initial { get; } = new DuelSnapshot(DuelPhase.Waiting, 0, 0, 0, 0.0, null, null, LastRoundDraw: false, null, Forfeit: false, HostAtSpawnA: true, 0.0, 0);

		public int ScoreOf(Side side)
		{
			if (side != Side.Host)
			{
				return GuestScore;
			}
			return HostScore;
		}

		[CompilerGenerated]
		private DuelSnapshot(DuelSnapshot original)
		{
			Phase = original.Phase;
			Round = original.Round;
			HostScore = original.HostScore;
			GuestScore = original.GuestScore;
			PhaseEndsAt = original.PhaseEndsAt;
			Kit = original.Kit;
			LastRoundWinner = original.LastRoundWinner;
			LastRoundDraw = original.LastRoundDraw;
			MatchWinner = original.MatchWinner;
			Forfeit = original.Forfeit;
			HostAtSpawnA = original.HostAtSpawnA;
			RoundDecidedAt = original.RoundDecidedAt;
			Island = original.Island;
		}
	}
	public abstract record DuelEvent;
	public sealed record PlayersReady : DuelEvent;
	public sealed record PlayerLeft(Side Side) : DuelEvent();
	public sealed record PlayerDied(Side Side) : DuelEvent();
	public sealed record Tick(int HostHealth, int GuestHealth) : DuelEvent();
	public sealed record RematchRequested : DuelEvent;
	public sealed record DuelStep(DuelSnapshot Snapshot, DuelEffect Effect);
	public sealed class DuelRules
	{
		private readonly DuelSettings _settings;

		private readonly IRandomSource _random;

		public DuelRules(DuelSettings settings, IRandomSource random)
		{
			_settings = settings;
			_random = random;
		}

		public DuelStep Apply(DuelSnapshot snapshot, DuelEvent duelEvent, double now)
		{
			if (!(duelEvent is PlayerLeft playerLeft))
			{
				if (!(duelEvent is PlayersReady))
				{
					if (!(duelEvent is RematchRequested))
					{
						if (!(duelEvent is PlayerDied playerDied))
						{
							if (duelEvent is Tick tick)
							{
								return OnTick(snapshot, tick, now);
							}
							return Same(snapshot);
						}
						return OnPlayerDied(snapshot, playerDied.Side, now);
					}
					return (snapshot.Phase == DuelPhase.MatchEnd) ? StartMatch(snapshot.Island, now) : Same(snapshot);
				}
				return (snapshot.Phase == DuelPhase.Waiting) ? StartMatch(null, now) : Same(snapshot);
			}
			return OnPlayerLeft(snapshot, playerLeft.Side);
		}

		public static bool AllowsDamage(DuelSnapshot s, double now)
		{
			if (s.Phase != DuelPhase.Fighting)
			{
				if (s.Phase == DuelPhase.RoundEnd && !s.LastRoundDraw)
				{
					return now - s.RoundDecidedAt <= 1.0;
				}
				return false;
			}
			return true;
		}

		private static DuelStep Same(DuelSnapshot s)
		{
			return new DuelStep(s, DuelEffect.None);
		}

		private DuelStep StartMatch(int? previousIsland, double now)
		{
			Kit kit = KitPicker.Pick(_settings.Kits, null, _random);
			return new DuelStep(DuelSnapshot.Initial with
			{
				Phase = DuelPhase.Preparing,
				Round = 1,
				Kit = kit,
				PhaseEndsAt = now + 1.0,
				Island = IslandPicker.Pick(_settings.Islands, previousIsland, _random)
			}, DuelEffect.PrepareRound);
		}

		private static DuelStep OnPlayerLeft(DuelSnapshot s, Side side)
		{
			if (s.Phase == DuelPhase.Waiting)
			{
				return Same(s);
			}
			return new DuelStep(DuelSnapshot.Initial with
			{
				Forfeit = true,
				MatchWinner = side.Other()
			}, DuelEffect.ResetToWaiting);
		}

		private static DuelStep OnPlayerDied(DuelSnapshot s, Side side, double now)
		{
			if (s.Phase == DuelPhase.Fighting)
			{
				return EndRound(s, side.Other(), now);
			}
			if (s.Phase != DuelPhase.RoundEnd || s.LastRoundDraw || s.LastRoundWinner != side || !(now - s.RoundDecidedAt <= 1.0))
			{
				return Same(s);
			}
			return new DuelStep(s with
			{
				HostScore = ((side == Side.Host) ? (s.HostScore - 1) : s.HostScore),
				GuestScore = ((side == Side.Guest) ? (s.GuestScore - 1) : s.GuestScore),
				LastRoundWinner = null,
				LastRoundDraw = true
			}, DuelEffect.AnnounceRoundResult);
		}

		private static DuelStep EndRound(DuelSnapshot s, Side? winner, double now)
		{
			return new DuelStep(s with
			{
				Phase = DuelPhase.RoundEnd,
				HostScore = ((winner == Side.Host) ? (s.HostScore + 1) : s.HostScore),
				GuestScore = ((winner == Side.Guest) ? (s.GuestScore + 1) : s.GuestScore),
				LastRoundWinner = winner,
				LastRoundDraw = !winner.HasValue,
				RoundDecidedAt = now,
				PhaseEndsAt = now + 3.0
			}, DuelEffect.AnnounceRoundResult);
		}

		private DuelStep OnTick(DuelSnapshot s, Tick tick, double now)
		{
			if (now < s.PhaseEndsAt)
			{
				return Same(s);
			}
			return s.Phase switch
			{
				DuelPhase.Preparing => Same(s with
				{
					Phase = DuelPhase.Countdown,
					PhaseEndsAt = now + (double)_settings.CountdownSeconds
				}), 
				DuelPhase.Countdown => new DuelStep(s with
				{
					Phase = DuelPhase.Fighting,
					PhaseEndsAt = now + (double)_settings.RoundTimeSeconds
				}, DuelEffect.StartFight), 
				DuelPhase.Fighting => EndRound(s, TimeoutWinner(tick), now), 
				DuelPhase.RoundEnd => AfterRound(s, now), 
				_ => Same(s), 
			};
		}

		private static Side? TimeoutWinner(Tick tick)
		{
			if (tick.HostHealth != tick.GuestHealth)
			{
				return (tick.HostHealth <= tick.GuestHealth) ? Side.Guest : Side.Host;
			}
			return null;
		}

		private DuelStep AfterRound(DuelSnapshot s, double now)
		{
			Side? lastRoundWinner = s.LastRoundWinner;
			if (lastRoundWinner.HasValue)
			{
				Side valueOrDefault = lastRoundWinner.GetValueOrDefault();
				if (s.ScoreOf(valueOrDefault) >= _settings.RoundsToWin)
				{
					return new DuelStep(s with
					{
						Phase = DuelPhase.MatchEnd,
						MatchWinner = valueOrDefault
					}, DuelEffect.AnnounceMatchResult);
				}
			}
			return new DuelStep(s with
			{
				Phase = DuelPhase.Preparing,
				Round = (s.LastRoundDraw ? s.Round : (s.Round + 1)),
				Kit = KitPicker.Pick(_settings.Kits, s.Kit, _random),
				Island = IslandPicker.Pick(_settings.Islands, s.Island, _random),
				HostAtSpawnA = !s.HostAtSpawnA,
				LastRoundWinner = null,
				LastRoundDraw = false,
				PhaseEndsAt = now + 1.0
			}, DuelEffect.PrepareRound);
		}
	}
	public sealed record RawDuelSettings(int RoundsToWin, int RoundTimeSeconds, int CountdownSeconds, float ArenaRadius, int IslandIndex, string Kits, string SpawnA, string SpawnB, bool DebugSolo, bool IslandRotation, bool RandomUpgrades);
	public sealed record DuelSettings(int RoundsToWin, int RoundTimeSeconds, int CountdownSeconds, float ArenaRadius, int IslandIndex, IReadOnlyList<Kit> Kits, Vector3? SpawnA, Vector3? SpawnB, bool DebugSolo, bool IslandRotation, IReadOnlyList<int> Islands, bool RandomUpgrades)
	{
		public static RawDuelSettings Defaults { get; } = new RawDuelSettings(5, 90, 3, 60f, 0, "pistol+knife;shotgun+knife;smg+knife;assaultrifle+knife;sniperrifle+pistol;bow+knife;knife", "", "", DebugSolo: false, IslandRotation: true, RandomUpgrades: true);

		public const double PrepareSeconds = 1.0;

		public const double RoundEndSeconds = 3.0;

		public const double DoubleKoWindowSeconds = 1.0;

		public const double StateBroadcastSeconds = 2.0;

		public const double HelloTimeoutSeconds = 5.0;

		public const string DefaultKits = "pistol+knife;shotgun+knife;smg+knife;assaultrifle+knife;sniperrifle+pistol;bow+knife;knife";

		public const int DefaultIslandIndex = 0;

		public DuelSettings WithKits(IReadOnlyList<Kit> kits)
		{
			return this with
			{
				Kits = kits
			};
		}

		public static DuelSettings FromRaw(RawDuelSettings raw, out IReadOnlyList<string> warnings)
		{
			List<string> list = new List<string>();
			RawDuelSettings defaults = Defaults;
			int num = SettingRange.Int("IslandIndex", raw.IslandIndex, 0, 255, defaults.IslandIndex, list);
			DuelSettings result = new DuelSettings(SettingRange.Int("RoundsToWin", raw.RoundsToWin, 1, 15, defaults.RoundsToWin, list), SettingRange.Int("RoundTimeSeconds", raw.RoundTimeSeconds, 15, 600, defaults.RoundTimeSeconds, list), SettingRange.Int("CountdownSeconds", raw.CountdownSeconds, 0, 10, defaults.CountdownSeconds, list), SettingRange.Float("ArenaRadius", raw.ArenaRadius, 10f, 500f, defaults.ArenaRadius, list), num, ParseKits(raw.Kits, list), ParseSpawn("SpawnA", raw.SpawnA, list), ParseSpawn("SpawnB", raw.SpawnB, list), raw.DebugSolo, raw.IslandRotation, new int[1] { num }, raw.RandomUpgrades);
			warnings = list;
			return result;
		}

		private static IReadOnlyList<Kit> ParseKits(string text, List<string> warnings)
		{
			string error;
			IReadOnlyList<Kit> readOnlyList = KitParser.Parse(text, out error);
			if (readOnlyList != null)
			{
				return readOnlyList;
			}
			warnings.Add("Invalid Kits (" + error + "), using default kits");
			string error2;
			return KitParser.Parse("pistol+knife;shotgun+knife;smg+knife;assaultrifle+knife;sniperrifle+pistol;bow+knife;knife", out error2);
		}

		private static Vector3? ParseSpawn(string name, string text, List<string> warnings)
		{
			if (string.IsNullOrWhiteSpace(text))
			{
				return null;
			}
			string[] array = text.Split(',');
			if (array.Length == 3 && TryFloat(array[0], out var value) && TryFloat(array[1], out var value2) && TryFloat(array[2], out var value3))
			{
				return new Vector3(value, value2, value3);
			}
			warnings.Add(name + "='" + text + "' invalid (expected format x,y,z), using automatic calculation");
			return null;
		}

		private static bool TryFloat(string text, out float value)
		{
			return float.TryParse(text.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value);
		}

		[CompilerGenerated]
		private DuelSettings(DuelSettings original)
		{
			RoundsToWin = original.RoundsToWin;
			RoundTimeSeconds = original.RoundTimeSeconds;
			CountdownSeconds = original.CountdownSeconds;
			ArenaRadius = original.ArenaRadius;
			IslandIndex = original.IslandIndex;
			Kits = original.Kits;
			SpawnA = original.SpawnA;
			SpawnB = original.SpawnB;
			DebugSolo = original.DebugSolo;
			IslandRotation = original.IslandRotation;
			Islands = original.Islands;
			RandomUpgrades = original.RandomUpgrades;
		}
	}
	public enum GameMode
	{
		Duel,
		Deathmatch,
		GunGame
	}
	public static class GameModes
	{
		public const int ArenaMaxMembers = 8;

		private const int DuelMaxMembers = 2;

		public static IReadOnlyList<GameMode> All { get; } = new GameMode[3]
		{
			GameMode.Duel,
			GameMode.Deathmatch,
			GameMode.GunGame
		};

		public static string Tag(GameMode mode)
		{
			return mode switch
			{
				GameMode.Duel => "duel", 
				GameMode.Deathmatch => "ffa", 
				_ => "gungame", 
			};
		}

		public static bool TryParseTag(string? tag, out GameMode mode)
		{
			foreach (GameMode item in All)
			{
				if (!(Tag(item) != tag))
				{
					mode = item;
					return true;
				}
			}
			mode = GameMode.Duel;
			return false;
		}

		public static int MaxMembers(GameMode mode)
		{
			if (mode != GameMode.Duel)
			{
				return 8;
			}
			return 2;
		}
	}
	public static class HudFormatter
	{
		public static string Clock(double seconds)
		{
			int num = (int)Math.Ceiling(Math.Max(0.0, seconds));
			return $"{num / 60}:{num % 60:00}";
		}

		public static int CountdownNumber(double seconds)
		{
			return (int)Math.Ceiling(Math.Max(0.0, seconds));
		}

		public static string ScoreLine(WireState s, Side local, string localName, string opponentName)
		{
			return $"{localName} {Score(s, local)} - {Score(s, local.Other())} {opponentName}";
		}

		public static string RoundBanner(UiText t, WireState s, Side local)
		{
			if (!s.LastRoundDraw && s.LastRoundWinner.HasValue)
			{
				if (s.LastRoundWinner != local)
				{
					return t.RoundLost;
				}
				return t.RoundWon;
			}
			return t.RoundDraw;
		}

		public static string MatchBanner(UiText t, WireState s, Side local)
		{
			return $"{((s.MatchWinner == local) ? t.Victory : t.Defeat)} {Score(s, local)} - {Score(s, local.Other())}";
		}

		public static string WaitingText(UiText t, WireState? s, Side local)
		{
			if ((object)s == null || !s.Forfeit || s.MatchWinner != local)
			{
				return t.WaitingForOpponent;
			}
			return t.ForfeitWin + " " + t.WaitingForOpponent;
		}

		public static BannerTone RoundTone(WireState s, Side local)
		{
			if (!s.LastRoundDraw && s.LastRoundWinner.HasValue)
			{
				if (s.LastRoundWinner != local)
				{
					return BannerTone.Bad;
				}
				return BannerTone.Good;
			}
			return BannerTone.Neutral;
		}

		public static BannerTone MatchTone(WireState s, Side local)
		{
			if (s.MatchWinner != local)
			{
				return BannerTone.Bad;
			}
			return BannerTone.Good;
		}

		public static IReadOnlyList<string> ScoreboardRows(UiText t, WireState s, Side local, string localName, string opponentName)
		{
			return new string[3]
			{
				localName + " — " + t.Rounds(Score(s, local)),
				opponentName + " — " + t.Rounds(Score(s, local.Other())),
				t.RoundKit(s.Round, s.KitName)
			};
		}

		private static int Score(WireState s, Side side)
		{
			if (side != Side.Host)
			{
				return s.GuestScore;
			}
			return s.HostScore;
		}
	}
	public static class ItemNames
	{
		public static string KeyOf(string objectName)
		{
			return objectName.Replace("(Clone)", "").Replace(" ", "").Trim()
				.ToLowerInvariant();
		}
	}
	public sealed record Kit(string Name, IReadOnlyList<string> Items);
	public static class KitFilter
	{
		public static IReadOnlyList<Kit> KeepAvailable(IReadOnlyList<Kit> kits, Func<string, bool> itemExists, out IReadOnlyList<string> missingItems)
		{
			List<string> missing = (from i in kits.SelectMany((Kit k) => k.Items)
				where !itemExists(i)
				select i).Distinct().ToList();
			missingItems = missing;
			return kits.Where((Kit k) => k.Items.All((string i) => !missing.Contains(i))).ToList();
		}
	}
	public static class KitParser
	{
		public static IReadOnlyList<Kit>? Parse(string text, out string error)
		{
			if (text.Contains('|'))
			{
				error = "the '|' character is not allowed in kits";
				return null;
			}
			List<Kit> list = new List<Kit>();
			string[] array = text.Split(';');
			foreach (string text2 in array)
			{
				if (!string.IsNullOrWhiteSpace(text2))
				{
					List<string> list2 = text2.Split('+').Select(Normalize).ToList();
					if (list2.Any((string text3) => text3.Length == 0))
					{
						error = "empty item in kit '" + text2.Trim() + "'";
						return null;
					}
					list.Add(new Kit(string.Join("+", list2), list2));
				}
			}
			if (list.Count == 0)
			{
				error = "no kit defined";
				return null;
			}
			error = "";
			return list;
		}

		private static string Normalize(string item)
		{
			return item.Replace(" ", "").Trim().ToLowerInvariant();
		}
	}
	public interface IRandomSource
	{
		int Next(int maxExclusive);
	}
	public sealed class SystemRandomSource : IRandomSource
	{
		private readonly Random _random;

		public SystemRandomSource(Random random)
		{
			_random = random;
		}

		public int Next(int maxExclusive)
		{
			return _random.Next(maxExclusive);
		}
	}
	public static class KitPicker
	{
		public static Kit Pick(IReadOnlyList<Kit> kits, Kit? previous, IRandomSource random)
		{
			return RandomPicker.PickAvoiding(kits, (Kit k) => previous != null && k.Name == previous.Name, random, "kits");
		}
	}
	public static class IslandPicker
	{
		public static int Pick(IReadOnlyList<int> islands, int? previous, IRandomSource random)
		{
			return RandomPicker.PickAvoiding(islands, (int i) => i == previous, random, "islands");
		}
	}
	internal static class RandomPicker
	{
		public static T PickAvoiding<T>(IReadOnlyList<T> items, Func<T, bool> isPrevious, IRandomSource random, string paramName)
		{
			if (items.Count == 0)
			{
				throw new ArgumentException("At least one element is required.", paramName);
			}
			List<T> list = items.Where((T i) => !isPrevious(i)).ToList();
			if (list.Count != 0)
			{
				return list[random.Next(list.Count)];
			}
			return items[random.Next(items.Count)];
		}
	}
	public static class MessagePolicy
	{
		public static bool IsAccepted(MessageKind kind, bool senderIsLobbyOwner, bool localIsHost)
		{
			switch (kind)
			{
			case MessageKind.State:
				return senderIsLobbyOwner && !localIsHost;
			case MessageKind.Hello:
			case MessageKind.Rematch:
				return !senderIsLobbyOwner && localIsHost;
			default:
				return false;
			}
		}
	}
	public sealed record OpenLobby(ulong Id, int Members);
	public static class QuickMatchPolicy
	{
		public static ulong? MergeTarget(ulong ownLobby, IEnumerable<ulong> openLobbies)
		{
			List<ulong> list = openLobbies.Where((ulong id) => id < ownLobby).ToList();
			if (list.Count != 0)
			{
				return list.Min();
			}
			return null;
		}

		public static ulong? MergeTarget(ulong ownLobby, IEnumerable<OpenLobby> openLobbies, bool arena)
		{
			List<OpenLobby> source = openLobbies.Where((OpenLobby l) => l.Id != ownLobby).ToList();
			if (arena)
			{
				OpenLobby openLobby = (from l in source
					where l.Members > 1
					orderby l.Members descending, l.Id
					select l).FirstOrDefault();
				if (openLobby != null)
				{
					return openLobby.Id;
				}
			}
			return MergeTarget(ownLobby, source.Select((OpenLobby l) => l.Id));
		}
	}
	internal static class SettingRange
	{
		public static int Int(string name, int value, int min, int max, int fallback, List<string> warnings)
		{
			if (value >= min && value <= max)
			{
				return value;
			}
			warnings.Add($"{name}={value} out of range [{min}-{max}], using default {fallback}");
			return fallback;
		}

		public static float Float(string name, float value, float min, float max, float fallback, List<string> warnings)
		{
			if (value >= min && value <= max)
			{
				return value;
			}
			warnings.Add($"{name}={value} out of range [{min}-{max}], using default {fallback}");
			return fallback;
		}
	}
	public enum Side
	{
		Host,
		Guest
	}
	public static class SideExtensions
	{
		public static Side Other(this Side side)
		{
			if (side != Side.Host)
			{
				return Side.Host;
			}
			return Side.Guest;
		}
	}
	public enum Language
	{
		English,
		French
	}
	public abstract class UiText
	{
		private sealed class EnglishText : UiText
		{
			public override string CombatModes => "Combat Modes";

			public override string InviteFriends => "Invite friends";

			public override string QuickMatch => "Quick match";

			public override string Back => "Back";

			public override string Cancel => "Cancel";

			public override string Incompatible => "This mod is not compatible with this version of the game.";

			public override string DuelTitle => "1V1 DUEL";

			public override string HostSuffix => " (host)";

			public override string WaitingForHost => "Waiting for the host…";

			public override string WaitingForOpponent => "Waiting for an opponent…";

			public override string ForfeitWin => "Your opponent left — you win by forfeit.";

			public override string ConnectingToHost => "Connecting to the host…";

			public override string Start => "Start";

			public override string Quit => "Quit";

			public override string Rematch => "Rematch";

			public override string PlayAgain => "Play again";

			public override string Scoreboard => "SCOREBOARD";

			public override string Opponent => "Opponent";

			public override string Bot => "Bot";

			public override string PreparingIsland => "Preparing the island…";

			public override string Go => "GO!";

			public override string RoundWon => "YOU WIN THE ROUND";

			public override string RoundLost => "YOUR OPPONENT WINS THE ROUND";

			public override string RoundDraw => "DRAW";

			public override string Victory => "VICTORY";

			public override string Defeat => "DEFEAT";

			public override string GameOver => "GAME OVER";

			public override string Kill => "KILL";

			public override string Eliminated => "ELIMINATED";

			public override string FinalLevel => "FINAL LEVEL: knife";

			public override string Bare => "bare";

			public override string Laser => "laser";

			public override string ExtendedMag => "extended mag";

			public override string OpponentWithoutMod => "Your opponent does not have the Combat Modes mod";

			public override string LobbyUnavailable => "This lobby is no longer available, start a new quick match";

			public override string LobbyOtherGameVersion => "This lobby uses a different version of the game";

			public override string SteamRefusedLobby => "Steam refused to create the lobby";

			public override string NoValidKits => "No valid weapon kit in htf.combatmodes.cfg (see the log)";

			public override string InvalidLadder => "Invalid gun game ladder in htf.combatmodes.cfg (at least 2 levels, the last one must be knife)";

			public override string ModeLabel(GameMode mode)
			{
				return mode switch
				{
					GameMode.Duel => "1v1 Duel", 
					GameMode.Deathmatch => "Free-for-all", 
					_ => "Gun Game", 
				};
			}

			public override string SearchingLobby(string modeLabel)
			{
				return "Searching for a " + modeLabel + " lobby…";
			}

			public override string LobbyTitle(string modeLabel, int players, int maxPlayers)
			{
				return $"LOBBY — {modeLabel} ({players}/{maxPlayers})";
			}

			public override string PlayerWins(string name)
			{
				return name.ToUpperInvariant() + " WINS";
			}

			public override string LevelUp(int level, string weapon)
			{
				return $"LEVEL {level}: {weapon}";
			}

			public override string Rounds(int count)
			{
				if (count != 1)
				{
					return $"{count} rounds";
				}
				return "1 round";
			}

			public override string RoundKit(int round, string kit)
			{
				return $"Round {round}  ·  Kit: {kit}";
			}

			public override string RoundClockKit(int round, string clock, string kit)
			{
				return $"Round {round}  ·  {clock}  ·  Kit: {kit}";
			}

			public override string ClockKit(string clock, string kit)
			{
				return clock + "  ·  Kit: " + kit;
			}

			public override string LevelScore(int level)
			{
				return $"level {level}";
			}

			public override string LocalAndLeader(string localScore, string leaderName, string leaderScore)
			{
				return "You: " + localScore + "  ·  Leader: " + leaderName + " " + leaderScore;
			}

			public override string LevelLine(int level, int levels, string weapon, string? next)
			{
				if (next != null)
				{
					return $"Level {level}/{levels}: {weapon}, next {next}";
				}
				return $"Level {level}/{levels}: knife, one kill to win";
			}

			public override string DeathmatchRow(int rank, string name, int kills, int deaths)
			{
				return $"{rank}. {name} — {kills} kills / {deaths} deaths";
			}

			public override string GunGameRow(int rank, string name, int level, int kills)
			{
				return $"{rank}. {name} — level {level} — {kills} kills";
			}

			public override string Sharpness(int level)
			{
				return $"sharpness {level}";
			}

			public override string AmmoLevel(int level)
			{
				return $"ammo lvl {level}";
			}

			public override string QuiverLevel(int level)
			{
				return $"quiver lvl {level}";
			}

			public override string MatchImpossible(string error)
			{
				return error + " — the match cannot start, go back to the menu";
			}

			public override string PlayerWithoutMod(string name)
			{
				return name + " does not have the Combat Modes mod";
			}

			public override string Position(string coordinates)
			{
				return "Position: " + coordinates;
			}
		}

		private sealed class FrenchText : UiText
		{
			public override string CombatModes => "Mods de combat";

			public override string InviteFriends => "Inviter des amis";

			public override string QuickMatch => "Partie rapide";

			public override string Back => "Retour";

			public override string Cancel => "Annuler";

			public override string Incompatible => "Mod incompatible avec cette version du jeu.";

			public override string DuelTitle => "DUEL 1V1";

			public override string HostSuffix => " (hôte)";

			public override string WaitingForHost => "En attente de l'hôte…";

			public override string WaitingForOpponent => "En attente d'un adversaire…";

			public override string ForfeitWin => "L'adversaire a quitté — victoire par forfait.";

			public override string ConnectingToHost => "Connexion à l'hôte…";

			public override string Start => "Lancer";

			public override string Quit => "Quitter";

			public override string Rematch => "Revanche";

			public override string PlayAgain => "Rejouer";

			public override string Scoreboard => "CLASSEMENT";

			public override string Opponent => "Adversaire";

			public override string Bot => "Bot";

			public override string PreparingIsland => "Préparation de l'île…";

			public override string Go => "GO !";

			public override string RoundWon => "TU GAGNES LA MANCHE";

			public override string RoundLost => "L'ADVERSAIRE GAGNE LA MANCHE";

			public override string RoundDraw => "MANCHE NULLE";

			public override string Victory => "VICTOIRE";

			public override string Defeat => "DÉFAITE";

			public override string GameOver => "PARTIE TERMINÉE";

			public override string Kill => "KILL";

			public override string Eliminated => "ÉLIMINÉ";

			public override string FinalLevel => "NIVEAU FINAL : couteau";

			public override string Bare => "nu";

			public override string Laser => "laser";

			public override string ExtendedMag => "chargeur étendu";

			public override string OpponentWithoutMod => "Ton adversaire n'a pas le mod Combat Modes";

			public override string LobbyUnavailable => "Ce salon n'est plus disponible, relance une partie rapide";

			public override string LobbyOtherGameVersion => "Ce salon utilise une autre version du jeu";

			public override string SteamRefusedLobby => "Steam a refusé la création du salon";

			public override string NoValidKits => "Aucun kit d'armes valide dans htf.combatmodes.cfg (voir le log)";

			public override string InvalidLadder => "Échelle d'armes invalide dans htf.combatmodes.cfg (2 niveaux minimum, le dernier doit être knife)";

			public override string ModeLabel(GameMode mode)
			{
				return mode switch
				{
					GameMode.Duel => "Duel 1v1", 
					GameMode.Deathmatch => "Mêlée générale", 
					_ => "Course à l'armement", 
				};
			}

			public override string SearchingLobby(string modeLabel)
			{
				return "Recherche d'un salon " + modeLabel + "…";
			}

			public override string LobbyTitle(string modeLabel, int players, int maxPlayers)
			{
				return $"SALON — {modeLabel} ({players}/{maxPlayers})";
			}

			public override string PlayerWins(string name)
			{
				return name.ToUpperInvariant() + " GAGNE";
			}

			public override string LevelUp(int level, string weapon)
			{
				return $"NIVEAU {level} : {weapon}";
			}

			public override string Rounds(int count)
			{
				if (count > 1)
				{
					return $"{count} manches";
				}
				return $"{count} manche";
			}

			public override string RoundKit(int round, string kit)
			{
				return $"Manche {round}  ·  Kit : {kit}";
			}

			public override string RoundClockKit(int round, string clock, string kit)
			{
				return $"Manche {round}  ·  {clock}  ·  Kit : {kit}";
			}

			public override string ClockKit(string clock, string kit)
			{
				return clock + "  ·  Kit : " + kit;
			}

			public override string LevelScore(int level)
			{
				return $"niveau {level}";
			}

			public override string LocalAndLeader(string localScore, string leaderName, string leaderScore)
			{
				return "Toi : " + localScore + "  ·  Leader : " + leaderName + " " + leaderScore;
			}

			public override string LevelLine(int level, int levels, string weapon, string? next)
			{
				if (next != null)
				{
					return $"Niveau {level}/{levels} : {weapon}, ensuite {next}";
				}
				return $"Niveau {level}/{levels} : couteau, un kill pour gagner";
			}

			public override string DeathmatchRow(int rank, string name, int kills, int deaths)
			{
				return $"{rank}. {name} — {kills} kills / {deaths} morts";
			}

			public override string GunGameRow(int rank, string name, int level, int kills)
			{
				return $"{rank}. {name} — niveau {level} — {kills} kills";
			}

			public override string Sharpness(int level)
			{
				return $"affûtage {level}";
			}

			public override string AmmoLevel(int level)
			{
				return $"munitions niv. {level}";
			}

			public override string QuiverLevel(int level)
			{
				return $"carquois niv. {level}";
			}

			public override string MatchImpossible(string error)
			{
				return error + " — partie impossible, retourne au menu";
			}

			public override string PlayerWithoutMod(string name)
			{
				return name + " n'a pas le mod Combat Modes";
			}

			public override string Position(string coordinates)
			{
				return "Position : " + coordinates;
			}
		}

		public static UiText English { get; } = new EnglishText();

		public static UiText French { get; } = new FrenchText();

		public abstract string CombatModes { get; }

		public abstract string InviteFriends { get; }

		public abstract string QuickMatch { get; }

		public abstract string Back { get; }

		public abstract string Cancel { get; }

		public abstract string Incompatible { get; }

		public abstract string DuelTitle { get; }

		public abstract string HostSuffix { get; }

		public abstract string WaitingForHost { get; }

		public abstract string WaitingForOpponent { get; }

		public abstract string ForfeitWin { get; }

		public abstract string ConnectingToHost { get; }

		public abstract string Start { get; }

		public abstract string Quit { get; }

		public abstract string Rematch { get; }

		public abstract string PlayAgain { get; }

		public abstract string Scoreboard { get; }

		public abstract string Opponent { get; }

		public abstract string Bot { get; }

		public abstract string PreparingIsland { get; }

		public abstract string Go { get; }

		public abstract string RoundWon { get; }

		public abstract string RoundLost { get; }

		public abstract string RoundDraw { get; }

		public abstract string Victory { get; }

		public abstract string Defeat { get; }

		public abstract string GameOver { get; }

		public abstract string Kill { get; }

		public abstract string Eliminated { get; }

		public abstract string FinalLevel { get; }

		public abstract string Bare { get; }

		public abstract string Laser { get; }

		public abstract string ExtendedMag { get; }

		public abstract string OpponentWithoutMod { get; }

		public abstract string LobbyUnavailable { get; }

		public abstract string LobbyOtherGameVersion { get; }

		public abstract string SteamRefusedLobby { get; }

		public abstract string NoValidKits { get; }

		public abstract string InvalidLadder { get; }

		public static UiText For(Language language)
		{
			if (language != Language.French)
			{
				return English;
			}
			return French;
		}

		public static Language LanguageFromLocale(string? localeCode)
		{
			if (localeCode == null || !localeCode.StartsWith("fr", StringComparison.OrdinalIgnoreCase))
			{
				return Language.English;
			}
			return Language.French;
		}

		public abstract string ModeLabel(GameMode mode);

		public abstract string SearchingLobby(string modeLabel);

		public abstract string LobbyTitle(string modeLabel, int players, int maxPlayers);

		public abstract string PlayerWins(string name);

		public abstract string LevelUp(int level, string weapon);

		public abstract string Rounds(int count);

		public abstract string RoundKit(int round, string kit);

		public abstract string RoundClockKit(int round, string clock, string kit);

		public abstract string ClockKit(string clock, string kit);

		public abstract string LevelScore(int level);

		public abstract string LocalAndLeader(string localScore, string leaderName, string leaderScore);

		public abstract string LevelLine(int level, int levels, string weapon, string? next);

		public abstract string DeathmatchRow(int rank, string name, int kills, int deaths);

		public abstract string GunGameRow(int rank, string name, int level, int kills);

		public abstract string Sharpness(int level);

		public abstract string AmmoLevel(int level);

		public abstract string QuiverLevel(int level);

		public abstract string MatchImpossible(string error);

		public abstract string PlayerWithoutMod(string name);

		public abstract string Position(string coordinates);
	}
	public sealed record WeaponOptions(int Sights, int Barrels, int BulletLevels, int QuiverLevels, bool HasExtendedMag, bool HasLaser, int SharpnessLevels)
	{
		public static WeaponOptions None { get; } = new WeaponOptions(0, 0, 0, 0, HasExtendedMag: false, HasLaser: false, 0);

		[CompilerGenerated]
		private WeaponOptions(WeaponOptions original)
		{
			Sights = original.Sights;
			Barrels = original.Barrels;
			BulletLevels = original.BulletLevels;
			QuiverLevels = original.QuiverLevels;
			HasExtendedMag = original.HasExtendedMag;
			HasLaser = original.HasLaser;
			SharpnessLevels = original.SharpnessLevels;
		}
	}
	public sealed record Loadout(int Sight, int Barrel, int Bullet, int Quiver, bool ExtendedMag, bool Laser, int Sharpness)
	{
		public static Loadout Bare { get; } = new Loadout(0, 0, 0, 0, ExtendedMag: false, Laser: false, 0);

		[CompilerGenerated]
		private Loadout(Loadout original)
		{
			Sight = original.Sight;
			Barrel = original.Barrel;
			Bullet = original.Bullet;
			Quiver = original.Quiver;
			ExtendedMag = original.ExtendedMag;
			Laser = original.Laser;
			Sharpness = original.Sharpness;
		}
	}
	public static class UpgradeRoller
	{
		public static Loadout Roll(WeaponOptions options, IRandomSource random)
		{
			return new Loadout(Level(options.Sights, random), Level(options.Barrels, random), Level(options.BulletLevels, random), Level(options.QuiverLevels, random), Toggle(options.HasExtendedMag, random), Toggle(options.HasLaser, random), Level(options.SharpnessLevels, random));
		}

		private static int Level(int count, IRandomSource random)
		{
			if (count <= 1)
			{
				return 0;
			}
			return random.Next(count);
		}

		private static bool Toggle(bool available, IRandomSource random)
		{
			if (available)
			{
				return random.Next(2) == 1;
			}
			return false;
		}
	}
	public sealed record WireState(DuelPhase Phase, int Round, int HostScore, int GuestScore, int RemainingMs, string KitName, Side? LastRoundWinner, bool LastRoundDraw, Side? MatchWinner, bool Forfeit, bool HostAtSpawnA)
	{
		public static WireState From(DuelSnapshot s, double now)
		{
			return new WireState(s.Phase, s.Round, s.HostScore, s.GuestScore, (int)Math.Round(Math.Max(0.0, s.PhaseEndsAt - now) * 1000.0), s.Kit?.Name ?? "", s.LastRoundWinner, s.LastRoundDraw, s.MatchWinner, s.Forfeit, s.HostAtSpawnA);
		}
	}
	public enum MessageKind
	{
		Hello,
		State,
		Rematch
	}
	public sealed record DuelMessage(MessageKind Kind, WireState? State)
	{
		public static DuelMessage Hello()
		{
			return new DuelMessage(MessageKind.Hello, null);
		}

		public static DuelMessage Rematch()
		{
			return new DuelMessage(MessageKind.Rematch, null);
		}

		public static DuelMessage Of(WireState state)
		{
			return new DuelMessage(MessageKind.State, state);
		}
	}
}
namespace HowToFish.CombatModes.Core.Arena
{
	public static class ArenaCodec
	{
		public const int ProtocolVersion = 2;

		private const string Kind = "ARENA";

		private const int FieldCount = 10;

		private const int PlayerFieldCount = 5;

		private static readonly string Header = string.Format("{0}|{1}|{2}|", "HTFCM", 2, "ARENA");

		private static readonly CultureInfo Inv = CultureInfo.InvariantCulture;

		public static bool IsArena(string text)
		{
			return text.StartsWith(Header, StringComparison.Ordinal);
		}

		public static string Encode(ArenaWire w)
		{
			return string.Join("|", "HTFCM", 2.ToString(Inv), "ARENA", ((int)w.Mode).ToString(Inv), ((int)w.Phase).ToString(Inv), w.RemainingMs.ToString(Inv), w.KitName, w.Winner.GetValueOrDefault().ToString(Inv), string.Join(",", w.Players.Select(EncodePlayer)), string.Join(";", w.Ladder));
		}

		private static string EncodePlayer(ArenaWirePlayer p)
		{
			return string.Join(":", p.SteamId.ToString(Inv), p.Kills.ToString(Inv), p.Deaths.ToString(Inv), p.Level.ToString(Inv), p.Alive ? "1" : "0");
		}

		public static bool TryDecode(string text, out ArenaWire? wire, out string error)
		{
			wire = null;
			string[] array = text.Split('|');
			if (!IsArena(text) || array.Length != 10)
			{
				return Fail($"malformed arena message ({array.Length} fields)", out error);
			}
			if (!TryInt(array[3], out var value) || !Enum.IsDefined(typeof(GameMode), value) || value == 0)
			{
				return Fail("invalid mode '" + array[3] + "'", out error);
			}
			if (!TryInt(array[4], out var value2) || !Enum.IsDefined(typeof(ArenaPhase), value2))
			{
				return Fail("invalid phase '" + array[4] + "'", out error);
			}
			if (!TryInt(array[5], out var value3) || value3 < 0)
			{
				return Fail("invalid time '" + array[5] + "'", out error);
			}
			if (!ulong.TryParse(array[7], NumberStyles.None, Inv, out var result))
			{
				return Fail("invalid winner '" + array[7] + "'", out error);
			}
			if (!TryPlayers(array[8], out IReadOnlyList<ArenaWirePlayer> players, out error))
			{
				return false;
			}
			string[] ladder = ((array[9].Length == 0) ? Array.Empty<string>() : array[9].Split(';'));
			wire = new ArenaWire((GameMode)value, (ArenaPhase)value2, value3, array[6], (result == 0L) ? ((ulong?)null) : new ulong?(result), players, ladder);
			return true;
		}

		private static bool TryPlayers(string field, out IReadOnlyList<ArenaWirePlayer> players, out string error)
		{
			List<ArenaWirePlayer> list = (List<ArenaWirePlayer>)(players = new List<ArenaWirePlayer>());
			if (field.Length == 0)
			{
				error = "";
				return true;
			}
			string[] array = field.Split(',');
			if (array.Length > 8)
			{
				return Fail($"{array.Length} players, maximum {8}", out error);
			}
			string[] array2 = array;
			foreach (string text in array2)
			{
				if (!TryPlayer(text, out ArenaWirePlayer player))
				{
					return Fail("invalid player '" + text + "'", out error);
				}
				list.Add(player);
			}
			error = "";
			return true;
		}

		private static bool TryPlayer(string entry, out ArenaWirePlayer? player)
		{
			player = null;
			string[] array = entry.Split(':');
			if (array.Length != 5 || !ulong.TryParse(array[0], NumberStyles.None, Inv, out var result))
			{
				return false;
			}
			if (!TryInt(array[1], out var value) || !TryInt(array[2], out var value2) || !TryInt(array[3], out var value3))
			{
				return false;
			}
			if (value2 < 0 || value3 < 0 || (array[4] != "0" && array[4] != "1"))
			{
				return false;
			}
			player = new ArenaWirePlayer(result, value, value2, value3, array[4] == "1");
			return true;
		}

		private static bool TryInt(string text, out int value)
		{
			return int.TryParse(text, NumberStyles.AllowLeadingSign, Inv, out value);
		}

		private static bool Fail(string reason, out string error)
		{
			error = reason;
			return false;
		}
	}
	public static class ArenaHudFormatter
	{
		public static IReadOnlyList<ArenaWirePlayer> Ranking(ArenaWire w)
		{
			if (w.Mode != GameMode.GunGame)
			{
				return (from p in w.Players
					orderby p.Kills descending, p.Deaths, p.SteamId
					select p).ToList();
			}
			return (from p in w.Players
				orderby p.Level descending, p.Kills descending, p.SteamId
				select p).ToList();
		}

		public static string TopLine(UiText t, ArenaWire w, ulong local, Func<ulong, string> name)
		{
			ArenaWirePlayer arenaWirePlayer = w.Players.FirstOrDefault((ArenaWirePlayer p) => p.SteamId == local);
			ArenaWirePlayer arenaWirePlayer2 = Ranking(w).FirstOrDefault();
			if (arenaWirePlayer == null || arenaWirePlayer2 == null)
			{
				return t.ModeLabel(w.Mode);
			}
			return t.LocalAndLeader(Score(t, arenaWirePlayer, w.Mode), name(arenaWirePlayer2.SteamId), Score(t, arenaWirePlayer2, w.Mode));
		}

		public static string SubLine(UiText t, ArenaWire w, ulong local, double remainingSeconds)
		{
			if (w.Mode == GameMode.Deathmatch)
			{
				return t.ClockKit(HudFormatter.Clock(remainingSeconds), w.KitName);
			}
			ArenaWirePlayer arenaWirePlayer = w.Players.FirstOrDefault((ArenaWirePlayer p) => p.SteamId == local);
			if (!(arenaWirePlayer == null) && w.Ladder.Count != 0)
			{
				return LevelLine(t, w.Ladder, arenaWirePlayer.Level);
			}
			return "";
		}

		public static string LevelLine(UiText t, IReadOnlyList<string> ladder, int level)
		{
			int num = ladder.Count - 1;
			int num2 = Math.Max(0, Math.Min(level, num));
			return t.LevelLine(num2 + 1, ladder.Count, ladder[num2], (num2 == num) ? null : ladder[num2 + 1]);
		}

		public static string Row(UiText t, int rank, ArenaWirePlayer p, GameMode mode, Func<ulong, string> name)
		{
			if (mode != GameMode.GunGame)
			{
				return t.DeathmatchRow(rank, name(p.SteamId), p.Kills, p.Deaths);
			}
			return t.GunGameRow(rank, name(p.SteamId), p.Level + 1, p.Kills);
		}

		public static string EndBanner(UiText t, ArenaWire w, ulong local, Func<ulong, string> name)
		{
			ulong? winner = w.Winner;
			if (winner.HasValue)
			{
				ulong valueOrDefault = winner.GetValueOrDefault();
				if (valueOrDefault != local)
				{
					return t.PlayerWins(name(valueOrDefault));
				}
				return t.Victory;
			}
			return t.GameOver;
		}

		public static (string Text, BannerTone Tone)? BannerFor(UiText t, ArenaWire? before, ArenaWire after, ulong local)
		{
			if (before == null || before.Phase != ArenaPhase.Playing || after.Phase != ArenaPhase.Playing)
			{
				return null;
			}
			ArenaWirePlayer arenaWirePlayer = before.Players.FirstOrDefault((ArenaWirePlayer p) => p.SteamId == local);
			ArenaWirePlayer arenaWirePlayer2 = after.Players.FirstOrDefault((ArenaWirePlayer p) => p.SteamId == local);
			if (arenaWirePlayer == null || arenaWirePlayer2 == null)
			{
				return null;
			}
			if (arenaWirePlayer.Alive && !arenaWirePlayer2.Alive)
			{
				return (t.Eliminated, BannerTone.Bad);
			}
			if (after.Mode == GameMode.GunGame && arenaWirePlayer2.Level > arenaWirePlayer.Level && after.Ladder.Count > 0)
			{
				return (LevelUpText(t, after.Ladder, arenaWirePlayer2.Level), BannerTone.Good);
			}
			if (arenaWirePlayer2.Kills > arenaWirePlayer.Kills)
			{
				return (t.Kill, BannerTone.Good);
			}
			return null;
		}

		private static string LevelUpText(UiText t, IReadOnlyList<string> ladder, int level)
		{
			if (level < ladder.Count - 1)
			{
				return t.LevelUp(level + 1, ladder[level]);
			}
			return t.FinalLevel;
		}

		private static string Score(UiText t, ArenaWirePlayer p, GameMode mode)
		{
			if (mode != GameMode.GunGame)
			{
				return p.Kills.ToString();
			}
			return t.LevelScore(p.Level + 1);
		}
	}
	public enum ArenaPhase
	{
		Lobby,
		Preparing,
		Countdown,
		Playing,
		Ended
	}
	public sealed record ArenaPlayer(ulong SteamId, int Kills, int Deaths, int Level, bool Alive, double RespawnAt, double ProtectedUntil);
	public sealed record ArenaSnapshot(GameMode Mode, ArenaPhase Phase, IReadOnlyList<ArenaPlayer> Players, Kit? Kit, double KitChangesAt, double PhaseEndsAt, double EndsAt, int Island, ulong? Winner)
	{
		public bool IsLive
		{
			get
			{
				ArenaPhase phase = Phase;
				if ((uint)(phase - 1) <= 2u)
				{
					return true;
				}
				return false;
			}
		}

		public static ArenaSnapshot Lobby(GameMode mode)
		{
			return new ArenaSnapshot(mode, ArenaPhase.Lobby, Array.Empty<ArenaPlayer>(), null, 0.0, 0.0, 0.0, -1, null);
		}

		public ArenaPlayer? Find(ulong id)
		{
			return Players.FirstOrDefault((ArenaPlayer p) => p.SteamId == id);
		}

		public ArenaSnapshot WithPlayer(ArenaPlayer player)
		{
			return this with
			{
				Players = (from p in Players.Where((ArenaPlayer p) => p.SteamId != player.SteamId).Append(player)
					orderby p.SteamId
					select p).ToList()
			};
		}

		public ArenaSnapshot Without(ulong id)
		{
			return this with
			{
				Players = Players.Where((ArenaPlayer p) => p.SteamId != id).ToList()
			};
		}
	}
	public abstract record ArenaEvent;
	public sealed record ArenaStart : ArenaEvent;
	public sealed record ArenaJoin(ulong Id) : ArenaEvent();
	public sealed record ArenaLeave(ulong Id) : ArenaEvent();
	public sealed record ArenaKill(ulong Killer, ulong Victim, string Weapon) : ArenaEvent();
	public sealed record ArenaDeath(ulong Victim) : ArenaEvent();
	public sealed record ArenaHit(ulong Attacker) : ArenaEvent();
	public sealed record ArenaTick : ArenaEvent;
	public abstract record ArenaAction;
	public sealed record PrepareIsland(int Island) : ArenaAction();
	public sealed record Respawn(ulong Id, Kit Kit) : ArenaAction();
	public sealed record Equip(ulong Id, Kit Kit) : ArenaAction();
	public sealed record RotateKit(Kit Kit) : ArenaAction();
	public sealed record Strip(ulong Id) : ArenaAction();
	public sealed record StartFight : ArenaAction;
	public sealed record EndGame(ulong Winner) : ArenaAction();
	public sealed record ArenaStep(ArenaSnapshot Snapshot, IReadOnlyList<ArenaAction> Actions);
	public sealed class ArenaRules
	{
		public const int MinPlayers = 2;

		private readonly ArenaSettings _settings;

		private readonly IRandomSource _random;

		public ArenaRules(ArenaSettings settings, IRandomSource random)
		{
			_settings = settings;
			_random = random;
		}

		public ArenaStep Apply(ArenaSnapshot s, ArenaEvent e, double now)
		{
			if (!(e is ArenaStart))
			{
				if (!(e is ArenaJoin arenaJoin))
				{
					if (!(e is ArenaLeave arenaLeave))
					{
						if (!(e is ArenaKill k))
						{
							if (!(e is ArenaDeath arenaDeath))
							{
								if (e is ArenaHit arenaHit)
								{
									return Unprotect(s, arenaHit.Attacker);
								}
								if (e is ArenaTick)
								{
									return OnTick(s, now);
								}
							}
							else if (s.Phase == ArenaPhase.Playing)
							{
								return OnDeath(s, arenaDeath.Victim, now, penalize: true);
							}
						}
						else if (s.Phase == ArenaPhase.Playing)
						{
							return OnKill(s, k, now);
						}
						return Same(s);
					}
					return Leave(s, arenaLeave.Id);
				}
				return Join(s, arenaJoin.Id, now);
			}
			return CanStart(s) ? Start(s, now) : Same(s);
		}

		public static bool AllowsDamage(ArenaSnapshot s, ulong victim, double now)
		{
			if (s.Phase == ArenaPhase.Playing)
			{
				ArenaPlayer arenaPlayer = s.Find(victim);
				if ((object)arenaPlayer != null && arenaPlayer.Alive)
				{
					return now >= arenaPlayer.ProtectedUntil;
				}
			}
			return false;
		}

		private Kit KitFor(ArenaSnapshot s, ArenaPlayer p)
		{
			if (s.Mode != GameMode.GunGame)
			{
				return s.Kit;
			}
			return _settings.Ladder.KitAt(p.Level);
		}

		private static bool CanStart(ArenaSnapshot s)
		{
			ArenaPhase phase = s.Phase;
			if ((phase == ArenaPhase.Lobby || phase == ArenaPhase.Ended) ? true : false)
			{
				return s.Players.Count >= 2;
			}
			return false;
		}

		private ArenaStep Start(ArenaSnapshot s, double now)
		{
			int? previous = ((s.Island >= 0) ? new int?(s.Island) : ((int?)null));
			int island = IslandPicker.Pick(_settings.Islands, previous, _random);
			Kit kit = ((s.Mode == GameMode.Deathmatch) ? KitPicker.Pick(_settings.Kits, null, _random) : null);
			List<ArenaPlayer> players = s.Players.Select((ArenaPlayer p) => new ArenaPlayer(p.SteamId, 0, 0, 0, Alive: false, 0.0, 0.0)).ToList();
			return Step(s with
			{
				Phase = ArenaPhase.Preparing,
				Players = players,
				Kit = kit,
				Island = island,
				Winner = null,
				PhaseEndsAt = now + 1.0,
				EndsAt = 0.0,
				KitChangesAt = 0.0
			}, new PrepareIsland(island));
		}

		private ArenaStep OnTick(ArenaSnapshot s, double now)
		{
			return s.Phase switch
			{
				ArenaPhase.Preparing => (now >= s.PhaseEndsAt) ? BeginCountdown(s, now) : Same(s), 
				ArenaPhase.Countdown => Chain(Respawns(s, now), (ArenaSnapshot r) => (!(now >= r.PhaseEndsAt)) ? Same(r) : BeginPlay(r, now)), 
				ArenaPhase.Playing => Chain(Respawns(s, now), (ArenaSnapshot r) => Clock(r, now)), 
				_ => Same(s), 
			};
		}

		private ArenaStep BeginCountdown(ArenaSnapshot s, double now)
		{
			List<ArenaPlayer> list = s.Players.Select((ArenaPlayer p) => p with
			{
				Alive = true,
				RespawnAt = 0.0,
				ProtectedUntil = 0.0
			}).ToList();
			ArenaSnapshot next = s with
			{
				Phase = ArenaPhase.Countdown,
				Players = list,
				PhaseEndsAt = now + (double)_settings.CountdownSeconds
			};
			return new ArenaStep(next, ((IEnumerable<ArenaPlayer>)list).Select((Func<ArenaPlayer, ArenaAction>)((ArenaPlayer p) => new Respawn(p.SteamId, KitFor(next, p)))).ToList());
		}

		private ArenaStep BeginPlay(ArenaSnapshot s, double now)
		{
			return Step(s with
			{
				Phase = ArenaPhase.Playing,
				EndsAt = ((s.Mode == GameMode.Deathmatch) ? (now + (double)_settings.TimeLimitSeconds) : 0.0),
				KitChangesAt = now + (double)_settings.KitRotationSeconds
			}, new StartFight());
		}

		private ArenaStep Clock(ArenaSnapshot s, double now)
		{
			if (s.Mode != GameMode.Deathmatch || s.Players.Count == 0)
			{
				return Same(s);
			}
			if (now >= s.EndsAt)
			{
				return End(s, Leader(s), Array.Empty<ArenaAction>());
			}
			if (now < s.KitChangesAt)
			{
				return Same(s);
			}
			Kit kit = KitPicker.Pick(_settings.Kits, s.Kit, _random);
			return Step(s with
			{
				Kit = kit,
				KitChangesAt = s.KitChangesAt + (double)_settings.KitRotationSeconds
			}, new RotateKit(kit));
		}

		private static ulong Leader(ArenaSnapshot s)
		{
			return (from p in s.Players
				orderby p.Kills descending, p.Deaths, p.SteamId
				select p).First().SteamId;
		}

		private ArenaStep Respawns(ArenaSnapshot s, double now)
		{
			ArenaSnapshot arenaSnapshot = s;
			List<ArenaAction> list = new List<ArenaAction>();
			foreach (ArenaPlayer item in s.Players.Where((ArenaPlayer p) => !p.Alive && p.RespawnAt <= now))
			{
				ArenaPlayer arenaPlayer = item with
				{
					Alive = true,
					ProtectedUntil = now + (double)_settings.SpawnProtectionSeconds
				};
				arenaSnapshot = arenaSnapshot.WithPlayer(arenaPlayer);
				list.Add(new Respawn(item.SteamId, KitFor(arenaSnapshot, arenaPlayer)));
			}
			if (list.Count != 0)
			{
				return new ArenaStep(arenaSnapshot, list);
			}
			return Same(s);
		}

		private static ArenaStep Join(ArenaSnapshot s, ulong id, double now)
		{
			if (s.Find(id) != null || s.Players.Count >= 8)
			{
				return Same(s);
			}
			int level = ((s.Mode == GameMode.GunGame && s.IsLive && s.Players.Count > 0) ? s.Players.Min((ArenaPlayer p) => p.Level) : 0);
			return Same(s.WithPlayer(new ArenaPlayer(id, 0, 0, level, Alive: false, now, 0.0)));
		}

		private static ArenaStep Leave(ArenaSnapshot s, ulong id)
		{
			if (s.Find(id) == null)
			{
				return Same(s);
			}
			ArenaSnapshot arenaSnapshot = s.Without(id);
			if (arenaSnapshot.Players.Count == 0)
			{
				return Same(ArenaSnapshot.Lobby(s.Mode)with
				{
					Island = s.Island
				});
			}
			if (s.IsLive && arenaSnapshot.Players.Count == 1)
			{
				return End(arenaSnapshot, arenaSnapshot.Players[0].SteamId, new ArenaAction[1]
				{
					new Strip(id)
				});
			}
			return Step(arenaSnapshot, new Strip(id));
		}

		private ArenaStep OnDeath(ArenaSnapshot s, ulong id, double now, bool penalize)
		{
			ArenaPlayer arenaPlayer = s.Find(id);
			if ((object)arenaPlayer == null || !arenaPlayer.Alive)
			{
				return Same(s);
			}
			ArenaPlayer player = Dead(arenaPlayer, now)with
			{
				Kills = ((penalize && s.Mode == GameMode.Deathmatch) ? (arenaPlayer.Kills - 1) : arenaPlayer.Kills)
			};
			return Step(s.WithPlayer(player), new Strip(id));
		}

		private ArenaStep OnKill(ArenaSnapshot s, ArenaKill k, double now)
		{
			if (k.Killer == k.Victim)
			{
				return OnDeath(s, k.Victim, now, penalize: true);
			}
			ArenaPlayer arenaPlayer = s.Find(k.Killer);
			if (arenaPlayer == null)
			{
				return OnDeath(s, k.Victim, now, penalize: false);
			}
			ArenaPlayer arenaPlayer2 = s.Find(k.Victim);
			if ((object)arenaPlayer2 == null || !arenaPlayer2.Alive)
			{
				return Same(s);
			}
			List<ArenaAction> actions = new List<ArenaAction>
			{
				new Strip(arenaPlayer2.SteamId)
			};
			ArenaPlayer arenaPlayer3 = Dead(arenaPlayer2, now);
			if (s.Mode == GameMode.GunGame)
			{
				return GunGameKill(s, arenaPlayer, arenaPlayer3, k.Weapon == "knife", actions);
			}
			ArenaPlayer arenaPlayer4 = arenaPlayer with
			{
				Kills = arenaPlayer.Kills + 1
			};
			ArenaSnapshot arenaSnapshot = s.WithPlayer(arenaPlayer3).WithPlayer(arenaPlayer4);
			if (arenaPlayer4.Kills < _settings.KillTarget)
			{
				return new ArenaStep(arenaSnapshot, actions);
			}
			return End(arenaSnapshot, arenaPlayer4.SteamId, actions);
		}

		private ArenaStep GunGameKill(ArenaSnapshot s, ArenaPlayer killer, ArenaPlayer dead, bool knife, List<ArenaAction> actions)
		{
			int lastLevel = _settings.Ladder.LastLevel;
			ArenaPlayer player = (knife ? dead with
			{
				Level = Math.Max(0, dead.Level - 1)
			} : dead);
			ArenaPlayer arenaPlayer = killer with
			{
				Kills = killer.Kills + 1,
				Level = Math.Min(killer.Level + 1, lastLevel)
			};
			ArenaSnapshot arenaSnapshot = s.WithPlayer(player).WithPlayer(arenaPlayer);
			if (knife && killer.Level >= lastLevel)
			{
				return End(arenaSnapshot, killer.SteamId, actions);
			}
			if (arenaPlayer.Alive && arenaPlayer.Level != killer.Level)
			{
				actions.Add(new Equip(arenaPlayer.SteamId, _settings.Ladder.KitAt(arenaPlayer.Level)));
			}
			return new ArenaStep(arenaSnapshot, actions);
		}

		private ArenaPlayer Dead(ArenaPlayer p, double now)
		{
			return p with
			{
				Deaths = p.Deaths + 1,
				Alive = false,
				RespawnAt = now + (double)_settings.RespawnSeconds,
				ProtectedUntil = 0.0
			};
		}

		private static ArenaStep Unprotect(ArenaSnapshot s, ulong id)
		{
			ArenaPlayer arenaPlayer = s.Find(id);
			if ((object)arenaPlayer == null || !(arenaPlayer.ProtectedUntil > 0.0))
			{
				return Same(s);
			}
			return Same(s.WithPlayer(arenaPlayer with
			{
				ProtectedUntil = 0.0
			}));
		}

		private static ArenaStep End(ArenaSnapshot s, ulong winner, IEnumerable<ArenaAction> actions)
		{
			return new ArenaStep(s with
			{
				Phase = ArenaPhase.Ended,
				Winner = winner
			}, actions.Append(new EndGame(winner)).ToList());
		}

		private static ArenaStep Chain(ArenaStep first, Func<ArenaSnapshot, ArenaStep> next)
		{
			ArenaStep arenaStep = next(first.Snapshot);
			return new ArenaStep(arenaStep.Snapshot, first.Actions.Concat(arenaStep.Actions).ToList());
		}

		private static ArenaStep Step(ArenaSnapshot s, ArenaAction action)
		{
			return new ArenaStep(s, new ArenaAction[1] { action });
		}

		private static ArenaStep Same(ArenaSnapshot s)
		{
			return new ArenaStep(s, Array.Empty<ArenaAction>());
		}
	}
	public sealed record RawArenaSettings(int KillTarget, int TimeLimitSeconds, int KitRotationSeconds, int RespawnSeconds, int SpawnProtectionSeconds, string Ladder);
	public sealed record ArenaSettings(int KillTarget, int TimeLimitSeconds, int KitRotationSeconds, int RespawnSeconds, int SpawnProtectionSeconds, int CountdownSeconds, GunGameLadder Ladder, IReadOnlyList<Kit> Kits, IReadOnlyList<int> Islands, bool RandomUpgrades)
	{
		public static RawArenaSettings Defaults { get; } = new RawArenaSettings(20, 600, 120, 3, 2, "sniperrifle;assaultrifle;smg;shotgun;bow;pistol;knife");

		public const int MaxPlayers = 8;

		public const double PrepareSeconds = 1.0;

		public const double KillCreditSeconds = 10.0;

		public const string DefaultLadder = "sniperrifle;assaultrifle;smg;shotgun;bow;pistol;knife";

		public static ArenaSettings FromRaw(RawArenaSettings raw, DuelSettings shared, out IReadOnlyList<string> warnings)
		{
			List<string> list = new List<string>();
			RawArenaSettings defaults = Defaults;
			string error;
			GunGameLadder gunGameLadder = GunGameLadder.Parse(raw.Ladder, out error);
			if (gunGameLadder == null)
			{
				list.Add("Invalid Ladder (" + error + "), using default ladder");
				gunGameLadder = GunGameLadder.Parse("sniperrifle;assaultrifle;smg;shotgun;bow;pistol;knife", out string _);
			}
			ArenaSettings result = new ArenaSettings(SettingRange.Int("KillTarget", raw.KillTarget, 1, 100, defaults.KillTarget, list), SettingRange.Int("TimeLimitSeconds", raw.TimeLimitSeconds, 60, 3600, defaults.TimeLimitSeconds, list), SettingRange.Int("KitRotationSeconds", raw.KitRotationSeconds, 30, 600, defaults.KitRotationSeconds, list), SettingRange.Int("RespawnSeconds", raw.RespawnSeconds, 0, 15, defaults.RespawnSeconds, list), SettingRange.Int("SpawnProtectionSeconds", raw.SpawnProtectionSeconds, 0, 10, defaults.SpawnProtectionSeconds, list), shared.CountdownSeconds, gunGameLadder, shared.Kits, shared.Islands, shared.RandomUpgrades);
			warnings = list;
			return result;
		}

		[CompilerGenerated]
		private ArenaSettings(ArenaSettings original)
		{
			KillTarget = original.KillTarget;
			TimeLimitSeconds = original.TimeLimitSeconds;
			KitRotationSeconds = original.KitRotationSeconds;
			RespawnSeconds = original.RespawnSeconds;
			SpawnProtectionSeconds = original.SpawnProtectionSeconds;
			CountdownSeconds = original.CountdownSeconds;
			Ladder = original.Ladder;
			Kits = original.Kits;
			Islands = original.Islands;
			RandomUpgrades = original.RandomUpgrades;
		}
	}
	public sealed record ArenaWirePlayer(ulong SteamId, int Kills, int Deaths, int Level, bool Alive);
	public sealed record ArenaWire(GameMode Mode, ArenaPhase Phase, int RemainingMs, string KitName, ulong? Winner, IReadOnlyList<ArenaWirePlayer> Players, IReadOnlyList<string> Ladder)
	{
		public static ArenaWire From(ArenaSnapshot s, double now, IReadOnlyList<string> ladder)
		{
			double num = ((s.Phase != ArenaPhase.Playing) ? s.PhaseEndsAt : ((s.Mode == GameMode.Deathmatch) ? s.EndsAt : now));
			List<ArenaWirePlayer> players = s.Players.Select((ArenaPlayer p) => new ArenaWirePlayer(p.SteamId, p.Kills, p.Deaths, p.Level, p.Alive)).ToList();
			GameMode mode = s.Mode;
			ArenaPhase phase = s.Phase;
			int remainingMs = (int)Math.Round(Math.Max(0.0, num - now) * 1000.0);
			string kitName = s.Kit?.Name ?? "";
			ulong? winner = s.Winner;
			IReadOnlyList<string> ladder2;
			if (s.Mode != GameMode.GunGame)
			{
				IReadOnlyList<string> readOnlyList = Array.Empty<string>();
				ladder2 = readOnlyList;
			}
			else
			{
				ladder2 = ladder;
			}
			return new ArenaWire(mode, phase, remainingMs, kitName, winner, players, ladder2);
		}
	}
	public sealed record GunGameLadder(IReadOnlyList<string> Weapons)
	{
		public int LastLevel => Weapons.Count - 1;

		public const string FinalWeapon = "knife";

		private const int MinLevels = 2;

		public static GunGameLadder? Parse(string text, out string error)
		{
			return Validate((from w in (text ?? "").Split(';')
				select w.Replace(" ", "").Trim().ToLowerInvariant() into w
				where w.Length > 0
				select w).ToList(), out error);
		}

		public GunGameLadder? KeepAvailable(Func<string, bool> exists, out IReadOnlyList<string> missing)
		{
			missing = Weapons.Where((string w) => !exists(w)).Distinct().ToList();
			string error;
			return Validate(Weapons.Where(exists).ToList(), out error);
		}

		public Kit KitAt(int level)
		{
			int num = Math.Max(0, Math.Min(level, LastLevel));
			string text = Weapons[num];
			string[] array = ((num != LastLevel) ? new string[2] { text, "knife" } : new string[1] { text });
			return new Kit(string.Join("+", array), array);
		}

		private static GunGameLadder? Validate(List<string> weapons, out string error)
		{
			if (weapons.Count < 2)
			{
				error = $"at least {2} levels required";
				return null;
			}
			if (weapons[weapons.Count - 1] != "knife")
			{
				error = "the last level must be knife";
				return null;
			}
			error = "";
			return new GunGameLadder(weapons);
		}

		[CompilerGenerated]
		private GunGameLadder(GunGameLadder original)
		{
			Weapons = original.Weapons;
		}
	}
	public static class SpawnPicker
	{
		public static IReadOnlyList<Vector3> Ring(Vector3 center, float radius, int count)
		{
			return Enumerable.Range(0, count).Select(delegate(int i)
			{
				double num = Math.PI * 2.0 * (double)i / (double)count;
				return new Vector3(center.X + radius * (float)Math.Cos(num), center.Y, center.Z + radius * (float)Math.Sin(num));
			}).ToList();
		}

		public static int Farthest(IReadOnlyList<Vector3> candidates, IReadOnlyList<Vector3> occupied)
		{
			if (candidates.Count == 0)
			{
				throw new ArgumentException("At least one spawn is required.", "candidates");
			}
			if (occupied.Count == 0)
			{
				return 0;
			}
			int result = 0;
			float num = -1f;
			int i;
			for (i = 0; i < candidates.Count; i++)
			{
				float num2 = occupied.Min((Vector3 o) => Vector3.DistanceSquared(candidates[i], o));
				if (!(num2 <= num))
				{
					result = i;
					num = num2;
				}
			}
			return result;
		}
	}
}

HowToFish.CombatModes.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet;
using FishNet.Connection;
using FishNet.Object.Synchronizing;
using HarmonyLib;
using HowToFish.CombatModes.Arena;
using HowToFish.CombatModes.Core;
using HowToFish.CombatModes.Core.Arena;
using HowToFish.CombatModes.Host;
using HowToFish.CombatModes.Net;
using HowToFish.CombatModes.Ui;
using HowToFish.CombatModes.Ui.Native;
using Microsoft.CodeAnalysis;
using Steamworks;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.SceneManagement;
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("HowToFish.CombatModes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d1a74e7102bbfa4f5efbca13639093f1f31d4ac8")]
[assembly: AssemblyProduct("HowToFish.CombatModes")]
[assembly: AssemblyTitle("HowToFish.CombatModes")]
[assembly: AssemblyVersion("1.0.0.0")]
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;
		}
	}
}
namespace HowToFish.CombatModes
{
	[BepInPlugin("htf.combatmodes", "Combat Modes", "2.0.0")]
	public sealed class CombatModesPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "htf.combatmodes";

		public const string PluginName = "Combat Modes";

		public const string PluginVersion = "2.0.0";

		internal static ManualLogSource Log { get; private set; }

		internal static GameObject Root { get; private set; }

		private void Awake()
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			(DuelSettings Settings, ArenaSettings Arena, string DebugPositionKey) tuple = ConfigBinder.Load(((BaseUnityPlugin)this).Config, Log);
			DuelSettings item = tuple.Settings;
			ArenaSettings item2 = tuple.Arena;
			string item3 = tuple.DebugPositionKey;
			bool flag = CompatibilityCheck.Run(Log);
			GameUiKit.ForceFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "ForceImguiFallback", false, "Show the legacy IMGUI interface (diagnostics)").Value;
			CombatSession.Initialize(item, item2, item3, flag);
			Root = new GameObject("HowToFish.CombatModes");
			Object.DontDestroyOnLoad((Object)(object)Root);
			((Object)Root).hideFlags = (HideFlags)61;
			MainMenuButton mainMenuButton = Root.AddComponent<MainMenuButton>();
			Root.AddComponent<CombatMenuScreen>();
			if (flag)
			{
				new Harmony("htf.combatmodes").PatchAll(typeof(CombatModesPlugin).Assembly);
				Root.AddComponent<CombatLobby>();
				Root.AddComponent<CombatChannel>();
				Root.AddComponent<DuelHost>();
				Root.AddComponent<ArenaHost>();
				Root.AddComponent<DuelHud>();
				Root.AddComponent<ArenaHud>();
				Root.AddComponent<CombatControllers>();
				Root.AddComponent<LobbyScreen>();
				Root.AddComponent<EndScreen>();
				Root.AddComponent<CombatHud>();
				Root.AddComponent<ScoreboardScreen>();
				Root.AddComponent<NoticeScreen>();
				Root.AddComponent<DebugTools>();
			}
			else
			{
				mainMenuButton.InjectButton();
			}
			Log.LogInfo((object)string.Format("{0} {1} loaded (compatible={2}, kits={3})", "Combat Modes", "2.0.0", flag, item.Kits.Count));
		}
	}
	internal static class CombatSession
	{
		public const string SaveName = "__combatmodes";

		public static DuelSettings Settings { get; private set; }

		public static ArenaSettings ArenaSettings { get; private set; }

		public static string DebugPositionKey { get; private set; }

		public static bool IsCompatible { get; private set; }

		public static GameMode Mode { get; private set; }

		public static bool IsDuelLobby { get; private set; }

		public static bool IsHost { get; private set; }

		public static bool IsSearching { get; private set; }

		public static bool IsLobbyLocked { get; private set; }

		public static ELobbyType? PendingLobbyType { get; private set; }

		public static CSteamID LobbyId { get; private set; }

		public static DuelSnapshot Snapshot { get; set; }

		public static WireState? Display { get; private set; }

		public static float DisplayReceivedAt { get; private set; }

		public static ArenaSnapshot ArenaSnapshot { get; set; }

		public static ArenaWire? ArenaDisplay { get; private set; }

		public static float ArenaDisplayReceivedAt { get; private set; }

		public static string Notice { get; private set; }

		public static float NoticeUntil { get; private set; }

		public static bool IsHostingDuel
		{
			get
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Invalid comparison between Unknown and I4
				if (IsDuelLobby && IsHost)
				{
					return (int)Mode == 0;
				}
				return false;
			}
		}

		public static bool IsHostingArena
		{
			get
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Invalid comparison between Unknown and I4
				if (IsDuelLobby && IsHost)
				{
					return (int)Mode > 0;
				}
				return false;
			}
		}

		public static bool IsArenaLobby
		{
			get
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Invalid comparison between Unknown and I4
				if (IsDuelLobby)
				{
					return (int)Mode > 0;
				}
				return false;
			}
		}

		public static bool BlocksDamage
		{
			get
			{
				if (IsHostingDuel)
				{
					return !DuelRules.AllowsDamage(Snapshot, (double)Time.time);
				}
				return false;
			}
		}

		public static Side LocalSide
		{
			get
			{
				if (IsHost)
				{
					return (Side)0;
				}
				return (Side)1;
			}
		}

		public static void Initialize(DuelSettings settings, ArenaSettings arena, string debugPositionKey, bool compatible)
		{
			Settings = settings;
			ArenaSettings = arena;
			DebugPositionKey = debugPositionKey;
			IsCompatible = compatible;
		}

		public static void UseSettings(DuelSettings settings)
		{
			Settings = settings;
		}

		public static void UseArenaSettings(ArenaSettings settings)
		{
			ArenaSettings = settings;
		}

		public static void BeginHosting(ELobbyType lobbyType, GameMode mode)
		{
			//IL_0000: 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)
			PendingLobbyType = lobbyType;
			Mode = mode;
		}

		public static void BeginSearch(GameMode mode)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			IsSearching = true;
			Mode = mode;
		}

		public static void EnterDuelLobby(CSteamID lobby, bool isHost, GameMode mode)
		{
			//IL_000c: 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_0042: 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)
			IsDuelLobby = true;
			IsHost = isHost;
			LobbyId = lobby;
			Mode = mode;
			IsLobbyLocked = false;
			PendingLobbyType = null;
			IsSearching = false;
			Snapshot = DuelSnapshot.Initial;
			Display = null;
			ArenaSnapshot = ArenaSnapshot.Lobby((GameMode)(((int)mode == 0) ? 1 : ((int)mode)));
			ArenaDisplay = null;
		}

		public static void SetSearching(bool searching)
		{
			IsSearching = searching;
		}

		public static void LockLobby()
		{
			IsLobbyLocked = true;
		}

		public static void SetDisplay(WireState state, float receivedAt)
		{
			Display = state;
			DisplayReceivedAt = receivedAt;
		}

		public static void SetArenaDisplay(ArenaWire wire, float receivedAt)
		{
			ArenaDisplay = wire;
			ArenaDisplayReceivedAt = receivedAt;
		}

		public static void ShowNotice(string text, float now, float seconds = 5f)
		{
			Notice = text;
			NoticeUntil = now + seconds;
		}

		public static void Reset()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			IsLobbyLocked = false;
			IsDuelLobby = false;
			IsHost = false;
			IsSearching = false;
			PendingLobbyType = null;
			LobbyId = CSteamID.Nil;
			Snapshot = DuelSnapshot.Initial;
			Display = null;
			ArenaSnapshot = ArenaSnapshot.Lobby((GameMode)1);
			ArenaDisplay = null;
		}

		static CombatSession()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<string> readOnlyList = default(IReadOnlyList<string>);
			Settings = DuelSettings.FromRaw(DuelSettings.Defaults, ref readOnlyList);
			ArenaSettings = ArenaSettings.FromRaw(ArenaSettings.Defaults, Settings, ref readOnlyList);
			DebugPositionKey = "F9";
			Mode = (GameMode)0;
			LobbyId = CSteamID.Nil;
			Snapshot = DuelSnapshot.Initial;
			ArenaSnapshot = ArenaSnapshot.Lobby((GameMode)1);
			Notice = "";
		}
	}
	internal static class CompatibilityCheck
	{
		private static readonly (Type Type, string Name)[] Methods = new(Type, string)[35]
		{
			(typeof(Server), "TeleportPlayer"),
			(typeof(Server), "RespawnPlayer"),
			(typeof(PlayerVitals), "TakeDamage"),
			(typeof(PlayerVitals), "ServerResetVitals"),
			(typeof(PlayerVitals), "Heal"),
			(typeof(PlayerDying), "ServerDie"),
			(typeof(SteamManager), "CreateLobby"),
			(typeof(SteamManager), "OnLobbyCreated"),
			(typeof(SteamManager), "LeaveLobby"),
			(typeof(SteamManager), "JoinLobby"),
			(typeof(SaveManager), "LoadAllServers"),
			(typeof(SaveManager), "SaveServer"),
			(typeof(SaveManager), "CreateServer"),
			(typeof(SaveManager), "OnServerLoaded"),
			(typeof(SaveManager), "PlayerHasFinishedTutorial"),
			(typeof(SaveSystem), "DeleteServer"),
			(typeof(MainMenuManager), "Start"),
			(typeof(MainMenuManager), "ToggleMenu"),
			(typeof(ConnectionManager), "KickSteamUserIfConnected"),
			(typeof(OnlineIslandManager), "TpToSpecificIsland"),
			(typeof(GameInfo), "GetSpawnable"),
			(typeof(Item), "SetSyncedHolder"),
			(typeof(Item), "DestroyItem"),
			(typeof(ServerSettings), "ToggleFriendlyFire"),
			(typeof(PlayerCamera), "ToggleMouse"),
			(typeof(AchievementManager), "CheckAttachmentsAchievement"),
			(typeof(PlayerThinking), "ThinkingInput"),
			(typeof(Player), "get_BlockInputs"),
			(typeof(PauseManager), "PauseInput"),
			(typeof(Server), "RpcLogic___RespawnPlayer___2210451296"),
			(typeof(Server), "RpcLogic___ResurrectPlayer___2247010027"),
			(typeof(PlayerDying), "MouseClick"),
			(typeof(DeathUI), "ToggleDeathUI"),
			(typeof(PauseManager), "OnPause"),
			(typeof(Server), "RpcLogic___HitPlayer___2449261505")
		};

		private static readonly (Type Type, string Name)[] Fields = new(Type, string)[18]
		{
			(typeof(PlayerDying), "_player"),
			(typeof(PlayerInventory), "_items"),
			(typeof(PlayerVitals), "_player"),
			(typeof(CanvasManager), "_allMainMenuButtons"),
			(typeof(PauseManager), "_pauseHolder"),
			(typeof(PauseManager), "_serverButtons"),
			(typeof(PauseManager), "_instance"),
			(typeof(DeathUI), "_friendsHelpText"),
			(typeof(DeathUI), "_giveUpMask"),
			(typeof(DeathUI), "_giveUpEvent"),
			(typeof(DeathUI), "_giveUpHolderEvent"),
			(typeof(Attachments), "_sights"),
			(typeof(Attachments), "_barrelAttachments"),
			(typeof(Attachments), "_bulletUpgrades"),
			(typeof(Attachments), "_quiverUpgrades"),
			(typeof(Attachments), "_extendedMagInfo"),
			(typeof(Attachments), "_laserSight"),
			(typeof(Melee), "_sharpnessUpgrades")
		};

		public static bool Run(ManualLogSource log)
		{
			List<string> list = (from x in Methods.Where(((Type Type, string Name) m) => !AccessTools.GetDeclaredMethods(m.Type).Any((MethodInfo x) => x.Name == m.Name)).Concat(Fields.Where(((Type Type, string Name) f) => AccessTools.Field(f.Type, f.Name) == null))
				select x.Type.Name + "." + x.Name).ToList();
			foreach (string item in list)
			{
				log.LogError((object)("Incompatible: " + item + " not found in this game version"));
			}
			return list.Count == 0;
		}
	}
	internal static class ConfigBinder
	{
		public static (DuelSettings Settings, ArenaSettings Arena, string DebugPositionKey) Load(ConfigFile config, ManualLogSource log)
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			DuelSettings val = LoadDuel(config, log);
			RawArenaSettings defaults = ArenaSettings.Defaults;
			IReadOnlyList<string> readOnlyList = default(IReadOnlyList<string>);
			ArenaSettings item = ArenaSettings.FromRaw(new RawArenaSettings(config.Bind<int>("Arena", "KillTarget", defaults.KillTarget, "Free-for-all: kills to win (1-100)").Value, config.Bind<int>("Arena", "TimeLimitSeconds", defaults.TimeLimitSeconds, "Free-for-all: max duration in seconds (60-3600)").Value, config.Bind<int>("Arena", "KitRotationSeconds", defaults.KitRotationSeconds, "Free-for-all: kit changes every N seconds (30-600)").Value, config.Bind<int>("Arena", "RespawnSeconds", defaults.RespawnSeconds, "Respawn delay in seconds (0-15)").Value, config.Bind<int>("Arena", "SpawnProtectionSeconds", defaults.SpawnProtectionSeconds, "Spawn protection in seconds (0-10)").Value, config.Bind<string>("Arena", "Ladder", defaults.Ladder, "Gun game: weapons separated by ';', the last must be knife").Value), val, ref readOnlyList);
			foreach (string item2 in readOnlyList)
			{
				log.LogWarning((object)item2);
			}
			string value = config.Bind<string>("Debug", "DebugPositionKey", "F9", "Key that shows your position (Input System key name)").Value;
			return (Settings: val, Arena: item, DebugPositionKey: value);
		}

		private static DuelSettings LoadDuel(ConfigFile config, ManualLogSource log)
		{
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Expected O, but got Unknown
			RawDuelSettings defaults = DuelSettings.Defaults;
			IReadOnlyList<string> readOnlyList = default(IReadOnlyList<string>);
			DuelSettings result = DuelSettings.FromRaw(new RawDuelSettings(config.Bind<int>("Match", "RoundsToWin", defaults.RoundsToWin, "Rounds to win the duel (1-15)").Value, config.Bind<int>("Match", "RoundTimeSeconds", defaults.RoundTimeSeconds, "Max round duration in seconds (15-600)").Value, config.Bind<int>("Match", "CountdownSeconds", defaults.CountdownSeconds, "Countdown before each round in seconds (0-10)").Value, config.Bind<float>("Arena", "ArenaRadius", defaults.ArenaRadius, "Combat zone radius in meters (10-500)").Value, config.Bind<int>("Arena", "IslandIndex", defaults.IslandIndex, "Index of the island used as arena (0-255)").Value, config.Bind<string>("Match", "Kits", defaults.Kits, "Weapon kits: kits separated by ';', items by '+'").Value, config.Bind<string>("Arena", "SpawnA", defaults.SpawnA, "Spawn A 'x,y,z' (vide = automatique)").Value, config.Bind<string>("Arena", "SpawnB", defaults.SpawnB, "Spawn B 'x,y,z' (vide = automatique)").Value, config.Bind<bool>("Debug", "DebugSolo", defaults.DebugSolo, "Duel against a fake opponent (development)").Value, config.Bind<bool>("Arena", "IslandRotation", defaults.IslandRotation, "Switch island every round (otherwise IslandIndex only)").Value, config.Bind<bool>("Match", "RandomUpgrades", defaults.RandomUpgrades, "Random weapon attachments each round, identical for everyone").Value), ref readOnlyList);
			foreach (string item in readOnlyList)
			{
				log.LogWarning((object)item);
			}
			return result;
		}
	}
}
namespace HowToFish.CombatModes.Ui
{
	internal sealed class ArenaHud : MonoBehaviour
	{
		private const float ReferenceHeight = 1080f;

		private const float PanelWidth = 560f;

		private const float RowHeight = 34f;

		private GUIStyle? _top;

		private GUIStyle? _big;

		private GUIStyle? _banner;

		private GUIStyle? _row;

		private void OnGUI()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if (!GameUiKit.HudReady && CombatSession.IsArenaLobby)
			{
				EnsureStyles();
				float num = (float)Screen.height / 1080f;
				GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
				float num2 = (float)Screen.width / num;
				DrawNotice(num2);
				ArenaWire arenaDisplay = CombatSession.ArenaDisplay;
				if (arenaDisplay == (ArenaWire)null)
				{
					GUI.Label(new Rect(0f, 40f, num2, 60f), Lang.Text.ConnectingToHost, _top);
				}
				else
				{
					DrawPhase(arenaDisplay, num2);
				}
			}
		}

		private void DrawPhase(ArenaWire w, float width)
		{
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected I4, but got Unknown
			//IL_003b: 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)
			ArenaPhase phase = w.Phase;
			switch ((int)phase)
			{
			case 0:
				DrawLobby(w, width);
				break;
			case 1:
				GUI.Label(new Rect(0f, 40f, width, 60f), Lang.Text.PreparingIsland, _top);
				break;
			case 2:
				DrawTop(w, width);
				GUI.Label(new Rect(0f, 380f, width, 200f), HudFormatter.CountdownNumber(Remaining(w)).ToString(), _big);
				break;
			case 3:
			{
				DrawTop(w, width);
				Keyboard current = Keyboard.current;
				if (current != null && ((ButtonControl)current.tabKey).isPressed)
				{
					DrawScoreboard(w, width, 200f);
				}
				break;
			}
			case 4:
				DrawEnd(w, width);
				break;
			}
		}

		private static double Remaining(ArenaWire w)
		{
			return (double)w.RemainingMs / 1000.0 - (double)(Time.time - CombatSession.ArenaDisplayReceivedAt);
		}

		private void DrawTop(ArenaWire w, float width)
		{
			//IL_001c: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			GUI.Box(new Rect(width / 2f - 320f, 20f, 640f, 90f), GUIContent.none);
			GUI.Label(new Rect(width / 2f - 320f, 25f, 640f, 45f), ArenaHudFormatter.TopLine(Lang.Text, w, CombatUiState.LocalId, (Func<ulong, string>)CombatUiState.NameOf), _top);
			GUI.Label(new Rect(width / 2f - 320f, 65f, 640f, 40f), ArenaHudFormatter.SubLine(Lang.Text, w, CombatUiState.LocalId, Remaining(w)), _row);
		}

		private void DrawLobby(ArenaWire w, float width)
		{
			//IL_0031: 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_0060: 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_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			float num = width / 2f - 280f;
			GUI.Box(new Rect(num, 200f, 560f, 160f + (float)w.Players.Count * 34f), GUIContent.none);
			GUI.Label(new Rect(num, 210f, 560f, 40f), Lang.Text.LobbyTitle(Lang.Text.ModeLabel(w.Mode), w.Players.Count, 8), _top);
			for (int i = 0; i < w.Players.Count; i++)
			{
				GUI.Label(new Rect(num, 260f + (float)i * 34f, 560f, 34f), CombatUiState.NameOf(w.Players[i].SteamId), _row);
			}
			float num2 = 270f + (float)w.Players.Count * 34f;
			if (CombatSession.IsHost)
			{
				GUI.enabled = w.Players.Count >= 2;
				if (GUI.Button(new Rect(num + 40f, num2, 220f, 50f), Lang.Text.Start))
				{
					CombatActions.Replay();
				}
				GUI.enabled = true;
			}
			else
			{
				GUI.Label(new Rect(num + 20f, num2, 260f, 50f), Lang.Text.WaitingForHost, _row);
			}
			if (GUI.Button(new Rect(num + 560f - 260f, num2, 220f, 50f), Lang.Text.Quit))
			{
				CombatActions.Quit();
			}
		}

		private void DrawScoreboard(ArenaWire w, float width, float y)
		{
			//IL_002f: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<ArenaWirePlayer> readOnlyList = ArenaHudFormatter.Ranking(w);
			float num = width / 2f - 280f;
			GUI.Box(new Rect(num, y, 560f, 20f + (float)readOnlyList.Count * 34f), GUIContent.none);
			for (int i = 0; i < readOnlyList.Count; i++)
			{
				GUI.Label(new Rect(num, y + 10f + (float)i * 34f, 560f, 34f), ArenaHudFormatter.Row(Lang.Text, i + 1, readOnlyList[i], w.Mode, (Func<ulong, string>)CombatUiState.NameOf), _row);
			}
		}

		private void DrawEnd(ArenaWire w, float width)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			GUI.Label(new Rect(0f, 100f, width, 100f), ArenaHudFormatter.EndBanner(Lang.Text, w, CombatUiState.LocalId, (Func<ulong, string>)CombatUiState.NameOf), _banner);
			DrawScoreboard(w, width, 220f);
			float num = 260f + (float)w.Players.Count * 34f;
			if (CombatSession.IsHost)
			{
				GUI.enabled = w.Players.Count >= 2;
				if (GUI.Button(new Rect(width / 2f - 230f, num, 210f, 60f), Lang.Text.PlayAgain))
				{
					CombatActions.Replay();
				}
				GUI.enabled = true;
			}
			if (GUI.Button(new Rect(width / 2f + 20f, num, 210f, 60f), Lang.Text.Quit))
			{
				CombatActions.Quit();
			}
		}

		private void DrawNotice(float width)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (!(Time.time > CombatSession.NoticeUntil) && CombatSession.Notice.Length != 0)
			{
				GUI.Label(new Rect(0f, 130f, width, 40f), CombatSession.Notice, _top);
			}
		}

		private void EnsureStyles()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//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_0050: Expected O, but got Unknown
			//IL_0057: 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_0069: Expected O, but got Unknown
			//IL_0070: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			if (_top == null)
			{
				_top = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontSize = 28,
					fontStyle = (FontStyle)1
				};
				_big = new GUIStyle(_top)
				{
					fontSize = 140
				};
				_banner = new GUIStyle(_top)
				{
					fontSize = 60
				};
				_row = new GUIStyle(_top)
				{
					fontSize = 22,
					fontStyle = (FontStyle)0
				};
			}
		}
	}
	internal static class CombatActions
	{
		public static void Replay()
		{
			if (!CombatUiState.IsDuel)
			{
				ArenaHost.Instance?.RequestStart();
			}
			else if (CombatSession.IsHost)
			{
				DuelHost.Instance?.RequestRematch();
			}
			else
			{
				CombatChannel.Send(DuelMessage.Rematch());
			}
		}

		public static void Quit()
		{
			SteamManager.LeaveLobby((DisconnectReason)0);
			MainMenuManager.ToggleMenu(true);
		}
	}
	internal sealed class CombatControllers : MonoBehaviour
	{
		private const float RefreshInterval = 0.5f;

		private const float RefillWindow = 2f;

		private bool _cursorFreed;

		private bool _wasAlive;

		private float _refillUntil;

		private float _nextRefresh;

		public static string LoadoutText { get; private set; } = "";

		private void Update()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			CombatScreen screen = CombatUiState.Screen;
			KeepCursor(screen);
			TrackLoadout(screen);
		}

		private void KeepCursor(CombatScreen screen)
		{
			//IL_0000: 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)
			bool flag = CombatUiRules.FreesCursor(screen);
			if (flag != _cursorFreed)
			{
				_cursorFreed = flag;
				PlayerCamera.ToggleMouse(flag);
			}
			else if (flag && (int)Cursor.lockState != 0)
			{
				PlayerCamera.ToggleMouse(true);
			}
		}

		private void TrackLoadout(CombatScreen screen)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Invalid comparison between Unknown and I4
			bool localAlive = CombatUiState.LocalAlive;
			if (localAlive && !_wasAlive)
			{
				_refillUntil = Time.time + 2f;
			}
			_wasAlive = localAlive;
			bool flag = screen - 2 <= 1;
			if (!flag || (Object)(object)Player.LocalPlayer == (Object)null)
			{
				LoadoutText = "";
			}
			else
			{
				if (Time.time < _nextRefresh)
				{
					return;
				}
				_nextRefresh = Time.time + 0.5f;
				try
				{
					if ((int)screen == 2 || Time.time < _refillUntil)
					{
						WeaponUpgrades.RefillLocalAmmo(Player.LocalPlayer);
					}
					LoadoutText = WeaponUpgrades.Describe(Player.LocalPlayer);
				}
				catch (Exception arg)
				{
					CombatModesPlugin.Log.LogError((object)$"Could not read attachments : {arg}");
				}
			}
		}
	}
	internal static class CombatLaunch
	{
		public static bool Launch(GameMode mode, bool quickMatch)
		{
			//IL_001e: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)CombatLobby.Instance == (Object)null)
			{
				CombatModesPlugin.Log.LogError((object)"CombatLobby missing");
				return false;
			}
			if (!Validate(mode, out string error))
			{
				CombatSession.ShowNotice(error, Time.time);
				return false;
			}
			if (quickMatch)
			{
				CombatLobby.Instance.StartQuickMatch(mode);
			}
			else
			{
				CombatLobby.Instance.Host(mode, isPublic: false);
			}
			return true;
		}

		private static bool Validate(GameMode mode, out string error)
		{
			//IL_0000: 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)
			ArenaSettings validated;
			if ((int)mode != 0)
			{
				return Preflight.TryValidateArena(CombatSession.ArenaSettings, CombatSession.Settings, mode, out validated, out error);
			}
			DuelSettings validated2;
			return Preflight.TryValidate(CombatSession.Settings, out validated2, out error);
		}
	}
	internal static class CombatUiState
	{
		public static bool InCombat => CombatSession.IsDuelLobby;

		public static bool IsDuel => (int)CombatSession.Mode == 0;

		public static CombatScreen Screen
		{
			get
			{
				//IL_0046: 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_0050: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				if (InCombat)
				{
					if (!IsDuel)
					{
						ArenaWire? arenaDisplay = CombatSession.ArenaDisplay;
						return CombatUiRules.ScreenFor((arenaDisplay != null) ? new ArenaPhase?(arenaDisplay.Phase) : ((ArenaPhase?)null));
					}
					WireState? display = CombatSession.Display;
					return CombatUiRules.ScreenFor((display != null) ? new DuelPhase?(display.Phase) : ((DuelPhase?)null));
				}
				return (CombatScreen)0;
			}
		}

		public static ulong LocalId => SteamUser.GetSteamID().m_SteamID;

		public static ulong OwnerId => SteamMatchmaking.GetLobbyOwner(CombatSession.LobbyId).m_SteamID;

		public static bool LocalAlive
		{
			get
			{
				if (!IsDuel && CombatSession.ArenaDisplay != (ArenaWire)null)
				{
					return CombatSession.ArenaDisplay.Players.Any((ArenaWirePlayer p) => p.SteamId == LocalId && p.Alive);
				}
				return false;
			}
		}

		public static string OpponentName
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0005: 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_0007: 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)
				CSteamID val = CombatLobby.Opponent();
				if (!(val == CSteamID.Nil))
				{
					return SteamFriends.GetFriendPersonaName(val);
				}
				return Lang.Text.Opponent;
			}
		}

		public static string NameOf(ulong id)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (id != 1)
			{
				if (id != LocalId)
				{
					return SteamFriends.GetFriendPersonaName(new CSteamID(id));
				}
				return SteamFriends.GetPersonaName();
			}
			return Lang.Text.Bot;
		}

		public static double Remaining()
		{
			if (IsDuel)
			{
				WireState display = CombatSession.Display;
				if (!(display == (WireState)null))
				{
					return (double)display.RemainingMs / 1000.0 - (double)(Time.time - CombatSession.DisplayReceivedAt);
				}
				return 0.0;
			}
			ArenaWire arenaDisplay = CombatSession.ArenaDisplay;
			if (!(arenaDisplay == (ArenaWire)null))
			{
				return (double)arenaDisplay.RemainingMs / 1000.0 - (double)(Time.time - CombatSession.ArenaDisplayReceivedAt);
			}
			return 0.0;
		}
	}
	internal sealed class DebugTools : MonoBehaviour
	{
		private Key _positionKey = (Key)102;

		private void Awake()
		{
			//IL_0010: 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)
			if (Enum.TryParse<Key>(CombatSession.DebugPositionKey, ignoreCase: true, out Key result))
			{
				_positionKey = result;
			}
			else
			{
				CombatModesPlugin.Log.LogWarning((object)("DebugPositionKey '" + CombatSession.DebugPositionKey + "' unknown, using F9"));
			}
		}

		private void Update()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_0058: 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)
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				return;
			}
			if (((ButtonControl)current[_positionKey]).wasPressedThisFrame && (Object)(object)Player.LocalPlayer != (Object)null)
			{
				Vector3 position = Player.LocalPlayer.Transform.position;
				string text = FormattableString.Invariant($"{position.x:F1},{position.y:F1},{position.z:F1}");
				CombatModesPlugin.Log.LogInfo((object)("Position: " + text));
				CombatSession.ShowNotice(Lang.Text.Position(text), Time.time);
			}
			if (!CombatSession.Settings.DebugSolo)
			{
				return;
			}
			if (((ButtonControl)current.f10Key).wasPressedThisFrame)
			{
				if (CombatSession.IsHostingArena)
				{
					ArenaHost.Instance?.KillBot();
				}
				else
				{
					DuelHost.Instance?.SimulateGuestDeath();
				}
			}
			if (((ButtonControl)current.f11Key).wasPressedThisFrame && CombatSession.IsHostingArena)
			{
				ArenaHost.Instance?.AddBot();
			}
		}
	}
	internal sealed class DuelHud : MonoBehaviour
	{
		private const float ReferenceHeight = 1080f;

		private GUIStyle? _top;

		private GUIStyle? _big;

		private GUIStyle? _banner;

		private GUIStyle? _small;

		private void OnGUI()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (!GameUiKit.HudReady && CombatSession.IsDuelLobby && (int)CombatSession.Mode == 0)
			{
				EnsureStyles();
				float num = (float)Screen.height / 1080f;
				GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
				float num2 = (float)Screen.width / num;
				DrawNotice(num2);
				WireState display = CombatSession.Display;
				if (display == (WireState)null || (int)display.Phase == 0)
				{
					GUI.Label(new Rect(0f, 40f, num2, 60f), HudFormatter.WaitingText(Lang.Text, display, CombatSession.LocalSide), _top);
					return;
				}
				DrawTopBar(display, num2);
				DrawCenter(display, num2);
			}
		}

		private static double Remaining(WireState s)
		{
			return (double)s.RemainingMs / 1000.0 - (double)(Time.time - CombatSession.DisplayReceivedAt);
		}

		private void DrawTopBar(WireState s, float width)
		{
			//IL_0000: 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_0039: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			string friendPersonaName = SteamFriends.GetFriendPersonaName(CombatLobby.Opponent());
			string text = HudFormatter.ScoreLine(s, CombatSession.LocalSide, SteamFriends.GetPersonaName(), friendPersonaName);
			GUI.Box(new Rect(width / 2f - 260f, 20f, 520f, 90f), GUIContent.none);
			GUI.Label(new Rect(width / 2f - 260f, 25f, 520f, 45f), text, _top);
			string text2 = Lang.Text.RoundClockKit(s.Round, HudFormatter.Clock(Remaining(s)), s.KitName);
			GUI.Label(new Rect(width / 2f - 260f, 65f, 520f, 40f), text2, _top);
			if (CombatControllers.LoadoutText.Length > 0)
			{
				GUI.Label(new Rect(0f, 115f, width, 40f), CombatControllers.LoadoutText, _small);
			}
		}

		private void DrawCenter(WireState s, float width)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0036: Expected I4, but got Unknown
			//IL_0037: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(0f, 380f, width, 200f);
			DuelPhase phase = s.Phase;
			switch (phase - 2)
			{
			case 0:
				GUI.Label(val, HudFormatter.CountdownNumber(Remaining(s)).ToString(), _big);
				break;
			case 2:
				GUI.Label(val, HudFormatter.RoundBanner(Lang.Text, s, CombatSession.LocalSide), _banner);
				break;
			case 3:
				DrawMatchEnd(s, width);
				break;
			case 1:
				break;
			}
		}

		private void DrawMatchEnd(WireState s, float width)
		{
			//IL_0010: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			GUI.Label(new Rect(0f, 360f, width, 120f), HudFormatter.MatchBanner(Lang.Text, s, CombatSession.LocalSide), _banner);
			if (GUI.Button(new Rect(width / 2f - 220f, 500f, 200f, 60f), Lang.Text.Rematch))
			{
				CombatActions.Replay();
			}
			if (GUI.Button(new Rect(width / 2f + 20f, 500f, 200f, 60f), Lang.Text.Quit))
			{
				CombatActions.Quit();
			}
		}

		private void DrawNotice(float width)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (!(Time.time > CombatSession.NoticeUntil) && CombatSession.Notice.Length != 0)
			{
				GUI.Label(new Rect(0f, 130f, width, 40f), CombatSession.Notice, _top);
			}
		}

		private void EnsureStyles()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//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_0050: Expected O, but got Unknown
			//IL_0057: 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_0069: Expected O, but got Unknown
			//IL_0070: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			if (_top == null)
			{
				_top = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontSize = 28,
					fontStyle = (FontStyle)1
				};
				_big = new GUIStyle(_top)
				{
					fontSize = 140
				};
				_banner = new GUIStyle(_top)
				{
					fontSize = 60
				};
				_small = new GUIStyle(_top)
				{
					fontSize = 20,
					fontStyle = (FontStyle)0
				};
			}
		}
	}
	internal static class Lang
	{
		public static UiText Text
		{
			get
			{
				//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)
				try
				{
					Locale selectedLocale = LocalizationSettings.SelectedLocale;
					object obj;
					if (!((Object)(object)selectedLocale != (Object)null))
					{
						obj = null;
					}
					else
					{
						LocaleIdentifier identifier = selectedLocale.Identifier;
						obj = ((LocaleIdentifier)(ref identifier)).Code;
					}
					return UiText.For(UiText.LanguageFromLocale((string)obj));
				}
				catch (Exception)
				{
					return UiText.English;
				}
			}
		}
	}
	internal sealed class MainMenuButton : MonoBehaviour
	{
		private const string TemplateMethod = "QuitButton";

		private const float PanelWidth = 440f;

		private const float PanelHeight = 380f;

		private const int WindowId = 53473;

		private bool _panelOpen;

		private bool _useFallbackButton;

		private GameMode? _mode;

		private GameObject? _button;

		public static MainMenuButton? Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		public void InjectButton()
		{
			((MonoBehaviour)this).StartCoroutine(InjectNextFrame());
		}

		private IEnumerator InjectNextFrame()
		{
			yield return null;
			if (!((Object)(object)_button != (Object)null))
			{
				Button val = ((IEnumerable<Button>)Resources.FindObjectsOfTypeAll<Button>()).FirstOrDefault((Func<Button, bool>)delegate(Button b)
				{
					//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)
					Scene scene = ((Component)b).gameObject.scene;
					return ((Scene)(ref scene)).IsValid() && GameUiKit.CallsMethod(b, "QuitButton");
				});
				if ((Object)(object)val == (Object)null)
				{
					CombatModesPlugin.Log.LogWarning((object)"Quit button not found, using fallback button (IMGUI)");
					_useFallbackButton = true;
				}
				else
				{
					_button = CloneButton(val);
					CombatModesPlugin.Log.LogInfo((object)(Lang.Text.CombatModes + " added to menu (" + ((Object)((Component)val).transform.parent).name + ")"));
				}
			}
		}

		private GameObject CloneButton(Button template)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, ((Component)template).transform.parent);
			((Object)val).name = "DuelButton";
			val.transform.SetSiblingIndex(((Component)template).transform.GetSiblingIndex());
			foreach (MonoBehaviour item in from c in val.GetComponentsInChildren<MonoBehaviour>(true)
				where ((object)c).GetType().Name.Contains("Locali")
				select c)
			{
				Object.Destroy((Object)(object)item);
			}
			TMP_Text componentInChildren = val.GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = Lang.Text.CombatModes;
			}
			Button component = val.GetComponent<Button>();
			component.onClick = new ButtonClickedEvent();
			((UnityEvent)component.onClick).AddListener(new UnityAction(OnButton));
			((Selectable)component).interactable = CombatSession.IsCompatible;
			return val;
		}

		private void OnButton()
		{
			if (!((Object)(object)CombatMenuScreen.Instance != (Object)null) || !CombatMenuScreen.Instance.TryOpen())
			{
				Open();
			}
		}

		private void Open()
		{
			_panelOpen = true;
			_mode = null;
		}

		private void OnGUI()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			if (!MainMenuManager.IsInMenu)
			{
				_panelOpen = false;
				return;
			}
			if (_useFallbackButton && GUI.Button(new Rect(20f, (float)(Screen.height - 80), 220f, 50f), Lang.Text.CombatModes))
			{
				Open();
			}
			if (_panelOpen)
			{
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(((float)Screen.width - 440f) / 2f, ((float)Screen.height - 380f) / 2f, 440f, 380f);
				GUI.Window(53473, val, new WindowFunction(DrawPanel), Lang.Text.CombatModes.ToUpperInvariant());
			}
		}

		private void DrawPanel(int id)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Space(20f);
			if (!CombatSession.IsCompatible)
			{
				GUILayout.Label(Lang.Text.Incompatible, Array.Empty<GUILayoutOption>());
			}
			else if (CombatSession.IsSearching)
			{
				DrawSearching();
			}
			else
			{
				GameMode? mode = _mode;
				if (mode.HasValue)
				{
					GameMode valueOrDefault = mode.GetValueOrDefault();
					DrawLaunch(valueOrDefault);
				}
				else
				{
					DrawModes();
				}
			}
			if (Time.time <= CombatSession.NoticeUntil && CombatSession.Notice.Length > 0)
			{
				GUILayout.Label(CombatSession.Notice, Array.Empty<GUILayoutOption>());
			}
			GUILayout.FlexibleSpace();
			if (!CombatSession.IsSearching && GUILayout.Button(Lang.Text.Back, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }))
			{
				Back();
			}
		}

		private void Back()
		{
			if (_mode.HasValue)
			{
				_mode = null;
			}
			else
			{
				_panelOpen = false;
			}
		}

		private void DrawModes()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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)
			foreach (GameMode item in GameModes.All)
			{
				if (GUILayout.Button(Lang.Text.ModeLabel(item), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(55f) }))
				{
					_mode = item;
				}
				GUILayout.Space(6f);
			}
		}

		private void DrawLaunch(GameMode mode)
		{
			//IL_0005: 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_007f: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Label(Lang.Text.ModeLabel(mode), Array.Empty<GUILayoutOption>());
			GUILayout.Space(6f);
			if (GUILayout.Button(Lang.Text.InviteFriends, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(55f) }))
			{
				AfterLaunch(CombatLaunch.Launch(mode, quickMatch: false));
			}
			GUILayout.Space(10f);
			if (GUILayout.Button(Lang.Text.QuickMatch, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(55f) }))
			{
				AfterLaunch(CombatLaunch.Launch(mode, quickMatch: true));
			}
		}

		private void AfterLaunch(bool started)
		{
			if (started && !CombatSession.IsSearching)
			{
				_panelOpen = false;
				_mode = null;
			}
		}

		private static void DrawSearching()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Label(Lang.Text.SearchingLobby(Lang.Text.ModeLabel(CombatSession.Mode)), Array.Empty<GUILayoutOption>());
			GUILayout.Space(10f);
			if (GUILayout.Button(Lang.Text.Cancel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) }))
			{
				CombatLobby.Instance?.CancelSearch();
			}
		}
	}
}
namespace HowToFish.CombatModes.Ui.Native
{
	internal sealed class CombatHud : NativeScreen
	{
		private const float BannerSeconds = 2f;

		private const float BannerFade = 0.5f;

		private const float CountdownPulse = 0.35f;

		private TextMeshProUGUI? _top;

		private TextMeshProUGUI? _sub;

		private TextMeshProUGUI? _loadout;

		private TextMeshProUGUI? _countdown;

		private TextMeshProUGUI? _banner;

		private DuelPhase? _lastDuelPhase;

		private ArenaWire? _lastArena;

		private float _bannerUntil;

		protected override bool ShouldShow(CombatScreen screen)
		{
			//IL_0000: 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_0004: Invalid comparison between Unknown and I4
			if (screen - 2 <= 1)
			{
				return true;
			}
			return false;
		}

		protected override string Key()
		{
			if (!CombatUiState.IsDuel)
			{
				return "arena";
			}
			return "duel";
		}

		protected override void Build(RectTransform layer)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_0065: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			RectTransform rectTransform = ((Graphic)GameUiKit.Panel((Transform)(object)layer, new Vector2(820f, 150f))).rectTransform;
			rectTransform.anchorMin = new Vector2(0.5f, 1f);
			rectTransform.anchorMax = new Vector2(0.5f, 1f);
			rectTransform.pivot = new Vector2(0.5f, 1f);
			rectTransform.anchoredPosition = new Vector2(0f, -16f);
			_top = GameUiKit.Label((Transform)(object)rectTransform, "", 32f, GameUiKit.TextColor, (TextAlignmentOptions)514);
			GameUiKit.Fit((Component)(object)_top, 44f);
			_sub = GameUiKit.Label((Transform)(object)rectTransform, "", 24f, GameUiKit.TextColor, (TextAlignmentOptions)514);
			GameUiKit.Fit((Component)(object)_sub, 32f);
			_loadout = GameUiKit.Label((Transform)(object)rectTransform, "", 18f, GameUiKit.TextColor, (TextAlignmentOptions)514);
			GameUiKit.Fit((Component)(object)_loadout, 26f);
			_countdown = Centered(layer, 160f, 0f);
			_banner = Centered(layer, 64f, 160f);
		}

		private static TextMeshProUGUI Centered(RectTransform layer, float size, float y)
		{
			//IL_0007: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI obj = GameUiKit.Label((Transform)(object)layer, "", size, GameUiKit.TextColor, (TextAlignmentOptions)514);
			((TMP_Text)obj).rectTransform.sizeDelta = new Vector2(1400f, size * 1.5f);
			((TMP_Text)obj).rectTransform.anchoredPosition = new Vector2(0f, y);
			return obj;
		}

		protected override void Tick()
		{
			if (!((Object)(object)_top == (Object)null) && !((Object)(object)_sub == (Object)null) && !((Object)(object)_loadout == (Object)null) && !((Object)(object)_countdown == (Object)null) && !((Object)(object)_banner == (Object)null))
			{
				if (CombatUiState.IsDuel)
				{
					TickDuel();
				}
				else
				{
					TickArena();
				}
				((TMP_Text)_loadout).text = CombatControllers.LoadoutText;
				TickCountdown();
				TickBanner();
			}
		}

		private void TickDuel()
		{
			//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_001d: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Invalid comparison between Unknown and I4
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Invalid comparison between Unknown and I4
			//IL_0092: 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_009a: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Invalid comparison between Unknown and I4
			WireState display = CombatSession.Display;
			if (display == (WireState)null)
			{
				return;
			}
			Side localSide = CombatSession.LocalSide;
			((TMP_Text)_top).text = HudFormatter.ScoreLine(display, localSide, CombatUiState.NameOf(CombatUiState.LocalId), CombatUiState.OpponentName);
			((TMP_Text)_sub).text = Lang.Text.RoundClockKit(display.Round, HudFormatter.Clock(CombatUiState.Remaining()), display.KitName);
			if ((DuelPhase?)display.Phase != _lastDuelPhase)
			{
				if ((int)display.Phase == 4)
				{
					ShowBanner(HudFormatter.RoundBanner(Lang.Text, display, localSide), HudFormatter.RoundTone(display, localSide));
				}
				if ((int)display.Phase == 3 && (int)_lastDuelPhase.GetValueOrDefault() == 2)
				{
					ShowBanner(Lang.Text.Go, (BannerTone)0);
				}
				_lastDuelPhase = display.Phase;
			}
		}

		private void TickArena()
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Invalid comparison between Unknown and I4
			//IL_00c4: 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_007d: Invalid comparison between Unknown and I4
			ArenaWire arenaDisplay = CombatSession.ArenaDisplay;
			if (arenaDisplay == (ArenaWire)null)
			{
				return;
			}
			((TMP_Text)_top).text = ArenaHudFormatter.TopLine(Lang.Text, arenaDisplay, CombatUiState.LocalId, (Func<ulong, string>)CombatUiState.NameOf);
			((TMP_Text)_sub).text = ArenaHudFormatter.SubLine(Lang.Text, arenaDisplay, CombatUiState.LocalId, CombatUiState.Remaining());
			if (arenaDisplay == _lastArena)
			{
				return;
			}
			if ((int)arenaDisplay.Phase == 3)
			{
				ArenaWire? lastArena = _lastArena;
				if (lastArena != null && (int)lastArena.Phase == 2)
				{
					ShowBanner(Lang.Text.Go, (BannerTone)0);
					goto IL_00ce;
				}
			}
			(string, BannerTone)? tuple = ArenaHudFormatter.BannerFor(Lang.Text, _lastArena, arenaDisplay, CombatUiState.LocalId);
			if (tuple.HasValue)
			{
				(string, BannerTone) valueOrDefault = tuple.GetValueOrDefault();
				ShowBanner(valueOrDefault.Item1, valueOrDefault.Item2);
			}
			goto IL_00ce;
			IL_00ce:
			_lastArena = arenaDisplay;
		}

		private void TickCountdown()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Invalid comparison between Unknown and I4
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Invalid comparison between Unknown and I4
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: 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)
			if ((int)CombatUiState.Screen != 2)
			{
				((TMP_Text)_countdown).text = "";
				return;
			}
			bool num;
			if (!CombatUiState.IsDuel)
			{
				ArenaWire? arenaDisplay = CombatSession.ArenaDisplay;
				if (arenaDisplay != null)
				{
					num = (int)arenaDisplay.Phase == 1;
					goto IL_004a;
				}
			}
			else
			{
				WireState? display = CombatSession.Display;
				if (display != null)
				{
					num = (int)display.Phase == 1;
					goto IL_004a;
				}
			}
			goto IL_0096;
			IL_0096:
			double num2 = CombatUiState.Remaining();
			float num3 = (float)(num2 - Math.Floor(num2));
			((TMP_Text)_countdown).text = HudFormatter.CountdownNumber(num2).ToString();
			((TMP_Text)_countdown).transform.localScale = Vector3.one * (1f + 0.35f * num3);
			((TMP_Text)_countdown).alpha = 0.4f + 0.6f * num3;
			return;
			IL_004a:
			if (num)
			{
				((TMP_Text)_countdown).text = "<size=50%>" + Lang.Text.PreparingIsland + "</size>";
				((TMP_Text)_countdown).transform.localScale = Vector3.one;
				((TMP_Text)_countdown).alpha = 1f;
				return;
			}
			goto IL_0096;
		}

		private void ShowBanner(string text, BannerTone tone)
		{
			//IL_0012: 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)
			((TMP_Text)_banner).text = text;
			((Graphic)_banner).color = ToneColors.Of(tone);
			_bannerUntil = Time.time + 2f;
		}

		private void TickBanner()
		{
			float num = _bannerUntil - Time.time;
			((TMP_Text)_banner).alpha = ((num <= 0f) ? 0f : Mathf.Clamp01(num / 0.5f));
		}
	}
	internal sealed class CombatMenuScreen : MonoBehaviour
	{
		private enum Page
		{
			Modes,
			Launch,
			Searching
		}

		private const float TitleSize = 46f;

		private const float TextSize = 26f;

		private const float ButtonHeight = 70f;

		private const float LabelWidth = 640f;

		private GameObject? _root;

		private TextMeshProUGUI? _notice;

		private Page _page;

		private GameMode _mode;

		private string _built = "";

		public static CombatMenuScreen? Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		public bool TryOpen()
		{
			List<GameObject> menuPanels = GameUiKit.MenuPanels;
			if (menuPanels == null || menuPanels.Count == 0)
			{
				return false;
			}
			if ((Object)(object)_root == (Object)null)
			{
				Build(menuPanels);
			}
			foreach (GameObject item in menuPanels)
			{
				item.SetActive(false);
			}
			_root.SetActive(true);
			_page = Page.Modes;
			_built = "";
			return true;
		}

		private void Build(List<GameObject> panels)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (RectTransform)panels[(panels.Count > 1) ? 1 : 0].transform;
			_root = new GameObject("HTF_CombatMenu", new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)_root.transform;
			((Transform)val2).SetParent(((Transform)val).parent, false);
			val2.anchorMin = val.anchorMin;
			val2.anchorMax = val.anchorMax;
			val2.pivot = val.pivot;
			val2.anchoredPosition = val.anchoredPosition;
			val2.sizeDelta = val.sizeDelta;
			VerticalLayoutGroup obj = _root.AddComponent<VerticalLayoutGroup>();
			VerticalLayoutGroup component = ((Component)val).GetComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj).childAlignment = (TextAnchor)((!((Object)(object)component != (Object)null)) ? 4 : ((int)((LayoutGroup)component).childAlignment));
			((HorizontalOrVerticalLayoutGroup)obj).spacing = (((Object)(object)component != (Object)null) ? ((HorizontalOrVerticalLayoutGroup)component).spacing : 14f);
			((LayoutGroup)obj).padding = (RectOffset)(((Object)(object)component != (Object)null) ? ((object)((LayoutGroup)component).padding) : ((object)new RectOffset(0, 0, 0, 0)));
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			panels.Add(_root);
			_root.SetActive(false);
		}

		private void Update()
		{
			//IL_004d: 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)
			if ((Object)(object)_root == (Object)null || !_root.activeSelf)
			{
				return;
			}
			if (!MainMenuManager.IsInMenu)
			{
				_root.SetActive(false);
				return;
			}
			Page page = (CombatSession.IsSearching ? Page.Searching : _page);
			string text = $"{page}|{_mode}|{CombatSession.Mode}";
			if (text != _built)
			{
				Rebuild(page);
				_built = text;
			}
			UpdateNotice();
		}

		public bool HandleEscape()
		{
			if ((Object)(object)_root == (Object)null || !_root.activeSelf || !MainMenuManager.IsInMenu)
			{
				return false;
			}
			if (!CombatSession.IsSearching)
			{
				Back();
			}
			return true;
		}

		private void Rebuild(Page page)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0174: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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)
			foreach (Transform item in _root.transform)
			{
				((Component)item).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			switch (page)
			{
			case Page.Modes:
				Title(Lang.Text.CombatModes.ToUpperInvariant());
				foreach (GameMode mode in GameModes.All)
				{
					AddButton(Lang.Text.ModeLabel(mode), delegate
					{
						//IL_0007: Unknown result type (might be due to invalid IL or missing references)
						Choose(mode);
					});
				}
				AddButton(Lang.Text.Back, Back);
				break;
			case Page.Launch:
				Title(Lang.Text.ModeLabel(_mode).ToUpperInvariant());
				AddButton(Lang.Text.InviteFriends, delegate
				{
					Launch(quickMatch: false);
				});
				AddButton(Lang.Text.QuickMatch, delegate
				{
					Launch(quickMatch: true);
				});
				AddButton(Lang.Text.Back, Back);
				break;
			case Page.Searching:
				Title(Lang.Text.SearchingLobby(Lang.Text.ModeLabel(CombatSession.Mode)));
				AddButton(Lang.Text.Cancel, delegate
				{
					CombatLobby.Instance?.CancelSearch();
				});
				break;
			}
			_notice = GameUiKit.Label(_root.transform, "", 26f, GameInfo.RedColor, (TextAlignmentOptions)514);
			Size(_notice, 60f);
		}

		private void Title(string text)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			Size(GameUiKit.Label(_root.transform, text, 46f, GameUiKit.TextColor, (TextAlignmentOptions)514), 80f);
		}

		private void AddButton(string label, Action onClick)
		{
			GameUiKit.Fit((Component)(object)GameUiKit.Button(_root.transform, label, onClick), 70f);
		}

		private static void Size(TextMeshProUGUI label, float height)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			((TMP_Text)label).rectTransform.sizeDelta = new Vector2(640f, height);
		}

		private void UpdateNotice()
		{
			if (!((Object)(object)_notice == (Object)null))
			{
				((TMP_Text)_notice).text = ((Time.time <= CombatSession.NoticeUntil) ? CombatSession.Notice : "");
			}
		}

		private void Choose(GameMode mode)
		{
			//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)
			_mode = mode;
			_page = Page.Launch;
		}

		private void Launch(bool quickMatch)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			if (CombatLaunch.Launch(_mode, quickMatch) && !CombatSession.IsSearching)
			{
				_root.SetActive(false);
			}
		}

		private void Back()
		{
			if (_page == Page.Launch)
			{
				_page = Page.Modes;
				return;
			}
			_root.SetActive(false);
			List<GameObject> menuPanels = GameUiKit.MenuPanels;
			if (menuPanels != null && menuPanels.Count > 0)
			{
				menuPanels[0].SetActive(true);
			}
		}
	}
	internal static class ToneColors
	{
		public static Color Of(BannerTone tone)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Invalid comparison between Unknown and I4
			//IL_001f: 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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if ((int)tone != 0)
			{
				if ((int)tone == 1)
				{
					return GameInfo.RedColor;
				}
				return GameInfo.OrangeColor;
			}
			return GameInfo.GreenColor;
		}
	}
	internal sealed class EndScreen : NativeScreen
	{
		private const float RowHeight = 40f;

		private static readonly Color Dim = new Color(0f, 0f, 0f, 0.55f);

		protected override bool ShouldShow(CombatScreen screen)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			return (int)screen == 4;
		}

		protected override string Key()
		{
			if (CombatUiState.IsDuel)
			{
				WireState display = CombatSession.Display;
				if (!(display == (WireState)null))
				{
					return $"duel|{display.MatchWinner}|{display.HostScore}-{display.GuestScore}";
				}
				return "duel";
			}
			ArenaWire arenaDisplay = CombatSession.ArenaDisplay;
			if (!(arenaDisplay == (ArenaWire)null))
			{
				return string.Format("arena|{0}|{1}|{2}", arenaDisplay.Winner, string.Join(",", arenaDisplay.Players.Select((ArenaWirePlayer p) => $"{p.SteamId}:{p.Kills}:{p.Level}")), CombatSession.IsHost);
			}
			return "arena";
		}

		protected override void Build(RectTransform layer)
		{
			AddDim(layer);
			if (CombatUiState.IsDuel)
			{
				BuildDuel(layer);
			}
			else
			{
				BuildArena(layer);
			}
		}

		private static void AddDim(RectTransform layer)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("HTF_Dim", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent((Transform)(object)layer, false);
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = Vector2.zero;
			val2.offsetMax = Vector2.zero;
			Image obj = val.AddComponent<Image>();
			((Graphic)obj).color = Dim;
			((Graphic)obj).raycastTarget = false;
		}

		private static void BuildDuel(RectTransform layer)
		{
			//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_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_0049: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			WireState display = CombatSession.Display;
			if (!(display == (WireState)null))
			{
				Side localSide = CombatSession.LocalSide;
				Transform transform = ((Component)GameUiKit.Panel((Transform)(object)layer, new Vector2(700f, 360f))).transform;
				GameUiKit.Fit((Component)(object)GameUiKit.Label(transform, HudFormatter.MatchBanner(Lang.Text, display, localSide), 56f, ToneColors.Of(HudFormatter.MatchTone(display, localSide)), (TextAlignmentOptions)514), 90f);
				string text = HudFormatter.ScoreLine(display, localSide, CombatUiState.NameOf(CombatUiState.LocalId), CombatUiState.OpponentName);
				GameUiKit.Fit((Component)(object)GameUiKit.Label(transform, text, 30f, GameUiKit.TextColor, (TextAlignmentOptions)514), 50f);
				GameUiKit.Fit((Component)(object)GameUiKit.Button(transform, Lang.Text.Rematch, CombatActions.Replay), 64f);
				GameUiKit.Fit((Component)(object)GameUiKit.Button(transform, Lang.Text.Quit, CombatActions.Quit), 64f);
			}
		}

		private static void BuildArena(RectTransform layer)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			ArenaWire arenaDisplay = CombatSession.ArenaDisplay;
			if (!(arenaDisplay == (ArenaWire)null))
			{
				IReadOnlyList<ArenaWirePlayer> readOnlyList = ArenaHudFormatter.Ranking(arenaDisplay);
				Transform transform = ((Component)GameUiKit.Panel((Transform)(object)layer, new Vector2(760f, 330f + (float)readOnlyList.Count * 40f))).transform;
				BannerTone tone = (BannerTone)((arenaDisplay.Winner != CombatUiState.LocalId) ? 2 : 0);
				GameUiKit.Fit((Component)(object)GameUiKit.Label(transform, ArenaHudFormatter.EndBanner(Lang.Text, arenaDisplay, CombatUiState.LocalId, (Func<ulong, string>)CombatUiState.NameOf), 52f, ToneColors.Of(tone), (TextAlignmentOptions)514), 84f);
				for (int i = 0; i < readOnlyList.Count; i++)
				{
					Color color = ((readOnlyList[i].SteamId == CombatUiState.LocalId) ? GameInfo.GreenColor : GameUiKit.TextColor);
					GameUiKit.Fit((Component)(object)GameUiKit.Label(transform, ArenaHudFormatter.Row(Lang.Text, i + 1, readOnlyList[i], arenaDisplay.Mode, (Func<ulong, string>)CombatUiState.NameOf), 26f, color, (TextAlignmentOptions)514), 40f);
				}
				AddReplay(transform, arenaDisplay.Players.Count >= 2);
				GameUiKit.Fit((Component)(object)GameUiKit.Button(transform, Lang.Text.Quit, CombatActions.Quit), 64f);
			}
		}

		private static void AddReplay(Transform panel, bool enoughPlayers)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (!CombatSession.IsHost)
			{
				GameUiKit.Fit((Component)(object)GameUiKit.Label(panel, Lang.Text.WaitingForHost, 24f, GameInfo.OrangeColor, (TextAlignmentOptions)514), 40f);
				return;
			}
			Button obj = GameUiKit.Button(panel, Lang.Text.PlayAgain, CombatActions.Replay);
			((Selectable)obj).interactable = enoughPlayers;
			GameUiKit.Fit((Component)(object)obj, 64f);
		}
	}
	internal static class GameUiKit
	{
		private const string TemplateMethod = "QuitButton";

		private const float RetrySeconds = 5f;

		private static readonly FieldRef<CanvasManager, List<GameObject>> MenuPanelsOf = AccessTools.FieldRefAccess<CanvasManager, List<GameObject>>("_allMainMenuButtons");

		private static readonly FieldRef<PauseManager, GameObject> PauseHolderOf = AccessTools.FieldRefAccess<PauseManager, GameObject>("_pauseHolder");

		private static Button? _button;

		private static TMP_Text? _text;

		private static Image? _panel;

		private static List<GameObject>? _menuPanels;

		private static float _nextTry;

		private static bool _warned;

		private static string _lastSummary = "";

		public static bool ForceFallback { get; set; }

		public static bool MenuReady
		{
			get
			{
				if (!ForceFallback && Locate())
				{
					return _menuPanels != null;
				}
				return false;
			}
		}

		public static bool HudReady
		{
			get
			{
				if (!ForceFallback && Locate())
				{
					return (Object)(object)_panel != (Object)null;
				}
				return false;
			}
		}

		public static List<GameObject>? MenuPanels
		{
			get
			{
				if (!MenuReady)
				{
					return null;
				}
				return _menuPanels;
			}
		}

		public static Color TextColor
		{
			get
			{
				//IL_0018: 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 (!((Object)(object)_text != (Object)null))
				{
					return Color.white;
				}
				return ((Graphic)_text).color;
			}
		}

		private static bool Locate()
		{
			if ((Object)(object)_button != (Object)null && (Object)(object)_text != (Object)null && (Object)(object)_panel != (Object)null && _menuPanels != null)
			{
				return true;
			}
			if (Time.unscaledTime < _nextTry)
			{
				if ((Object)(object)_button != (Object)null)
				{
					return (Object)(object)_text != (Object)null;
				}
				return false;
			}
			_nextTry = Time.unscaledTime + 5f;
			try
			{
				Find();
			}
			catch (Exception arg)
			{
				CombatModesPlugin.Log.LogError((object)$"Could not read the game UI : {arg}");
			}
			int num;
			if ((Object)(object)_button != (Object)null)
			{
				num = (((Object)(object)_text != (Object)null) ? 1 : 0);
				if (num != 0)
				{
					goto IL_00c0;
				}
			}
			else
			{
				num = 0;
			}
			if (!_warned)
			{
				_warned = true;
				CombatModesPlugin.Log.LogWarning((object)"Game UI templates not found, using fallback display (IMGUI)");
			}
			goto IL_00c0;
			IL_00c0:
			return (byte)num != 0;
		}

		private static void Find()
		{
			if ((Object)(object)_button == (Object)null)
			{
				_button = ((IEnumerable<Button>)Resources.FindObjectsOfTypeAll<Button>()).FirstOrDefault((Func<Button, bool>)delegate(Button b)
				{
					//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)
					Scene scene = ((Component)b).gameObject.scene;
					return ((Scene)(ref scene)).IsValid() && CallsMethod(b, "QuitButton");
				});
				_text = (((Object)(object)_button != (Object)null) ? ((Component)_button).GetComponentInChildren<TMP_Text>(true) : null);
			}
			if (_menuPanels == null)
			{
				CanvasManager val = ((IEnumerable<CanvasManager>)Resources.FindObjectsOfTypeAll<CanvasManager>()).FirstOrDefault((Func<CanvasManager, bool>)delegate(CanvasManager c)
				{
					//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)
					Scene scene = ((Component)c).gameObject.scene;
					return ((Scene)(ref scene)).IsValid();
				});
				if ((Object)(object)val != (Object)null)
				{
					_menuPanels = MenuPanelsOf.Invoke(val);
				}
			}
			if ((Object)(object)_panel == (Object)null)
			{
				PauseManager val2 = ((IEnumerable<PauseManager>)Resources.FindObjectsOfTypeAll<PauseManager>()).FirstOrDefault((Func<PauseManager, bool>)delegate(PauseManager p)
				{
					//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)
					Scene scene = ((Component)p).gameObject.scene;
					return ((Scene)(ref scene)).IsValid();
				});
				GameObject val3 = (((Object)(object)val2 != (Object)null) ? PauseHolderOf.Invoke(val2) : null);
				if ((Object)(object)val3 != (Object)null)
				{
					_panel = LargestSpriteImage(val3);
				}
			}
			string text = $"Game UI: button={(Object)(object)_button != (Object)null}, text={(Object)(object)_text != (Object)null}, panel={(Object)(object)_panel != (Object)null}, menu={_menuPanels != null}";
			if (!(text == _lastSummary))
			{
				_lastSummary = text;
				CombatModesPlugin.Log.LogInfo((object)text);
			}
		}

		private static Image? LargestSpriteImage(GameObject root)
		{
			return (from i in root.GetComponentsInChildren<Image>(true)
				where (Object)(object)i.sprite != (Object)null
				select i).OrderByDescending(delegate(Image i)
			{
				//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_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				Rect rect = ((Graphic)i).rectTransform.rect;
				float width = ((Rect)(ref rect)).width;
				rect = ((Graphic)i).rectTransform.rect;
				return width * ((Rect)(ref rect)).height;
			}).FirstOrDefault();
		}

		public static bool CallsMethod(Button button, string method)
		{
			for (int i = 0; i < ((UnityEventBase)button.onClick).GetPersistentEventCount(); i++)
			{
				if (((UnityEventBase)button.onClick).GetPersistentMethodName(i) == method)
				{
					return true;
				}
			}
			return false;
		}

		public static Button Button(Transform parent, string label, Action onClick)
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			GameObject val = Object.Instantiate<GameObject>(((Component)_button).gameObject, parent, false);
			((Object)val).name = "HTF_Button";
			val.SetActive(true);
			foreach (MonoBehaviour item in from c in val.GetComponentsInChildren<MonoBehaviour>(true)
				where ((object)c).GetType().Name.Contains("Locali")
				select c)
			{
				Object.Destroy((Object)(object)item);
			}
			TMP_Text componentInChildren = val.GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = label;
			}
			Button component = val.GetComponent<Button>();
			component.onClick = new ButtonClickedEvent();
			((UnityEvent)component.onClick).AddListener((UnityAction)delegate
			{
				onClick();
			});
			return component;
		}

		public static TextMeshProUGUI Label(Transform parent, string text, float size, Color color, TextAlignmentOptions alignment = (TextAlignmentOptions)514)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("HTF_Label", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).font = _text.font;
			((TMP_Text)obj).fontSharedMaterial = _text.fontSharedMaterial;
			((TMP_Text)obj).fontSize = size;
			((Graphic)obj).color = color;
			((TMP_Text)obj).alignment = alignment;
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).text = text;
			return obj;
		}

		public static Image Panel(Transform parent, Vector2 size)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_008e: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			GameObject val = new GameObject("HTF_Panel", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(parent, false);
			Image val2 = val.AddComponent<Image>();
			val2.sprite = _panel.sprite;
			val2.type = _panel.type;
			((Graphic)val2).color = ((Graphic)_panel).color;
			((Graphic)val2).material = ((Graphic)_panel).material;
			val2.pixelsPerUnitMultiplier = _panel.pixelsPerUnitMultiplier;
			((Graphic)val2).raycastTarget = false;
			((Graphic)val2).rectTransform.sizeDelta = size;
			VerticalLayoutGroup obj = val.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj).padding = new RectOffset(32, 32, 28, 28);
			((HorizontalOrVerticalLayoutGroup)obj).spacing = 10f;
			((LayoutGroup)obj).childAlignment = (TextAnchor)1;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			val.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			return val2;
		}

		public static void Fit(Component element, float height)
		{
			LayoutElement val = element.GetComponent<LayoutElement>();
			if ((Object)(object)val == (Object)null)
			{
				val = element.gameObject.AddComponent<LayoutElement>();
			}
			val.minHeight = height;
			val.preferredHeight = height;
		}
	}
	internal sealed class LobbyScreen : NativeScreen
	{
		private const float RowHeight = 44f;

		protected override bool ShouldShow(CombatScreen screen)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			return (int)screen == 1;
		}

		protected override string Key()
		{
			return string.Format("{0}|{1}|{2}|{3}", Title(), string.Join(",", Names()), CombatSession.IsHost, CanStart());
		}

		protected override void Build(RectTransform layer)
		{
			//IL_001f: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<string> readOnlyList = Names();
			Transform transform = ((Component)GameUiKit.Panel((Transform)(object)layer, new Vector2(640f, 300f + (float)readOnlyList.Count * 44f))).transform;
			GameUiKit.Fit((Component)(object)GameUiKit.Label(transform, Title(), 40f, GameUiKit.TextColor, (TextAlignmentOptions)514), 60f);
			foreach (string item in readOnlyList)
			{
				GameUiKit.Fit((Component)(object)GameUiKit.Label(transform, item, 28f, GameUiKit.TextColor, (TextAlignmentOptions)514), 44f);
			}
			AddStatusOrStart(transform);
			GameUiKit.Fit((Component)(object)GameUiKit.Button(transform, Lang.Text.Quit, CombatActions.Quit), 64f);
		}

		private static void AddStatusOrStart(Transform panel)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			if (CombatUiState.IsDuel)
			{
				string text = HudFormatter.WaitingText(Lang.Text, CombatSession.Display, CombatSession.LocalSide);
				GameUiKit.Fit((Component)(object)GameUiKit.Label(panel, text, 24f, GameInfo.OrangeColor, (TextAlignmentOptions)514), 40f);
			}
			else if (!CombatSession.IsHost)
			{
				GameUiKit.Fit((Component)(object)GameUiKit.Label(panel, Lang.Text.WaitingForHost, 24f, GameInfo.OrangeColor, (TextAlignmentOptions)514), 40f);
			}
			else
			{
				Button obj = GameUiKit.Button(panel, Lang.Text.Start, CombatActions.Replay);
				((Selectable)obj).interactable = CanStart();
				GameUiKit.Fit((Component)(object)obj, 64f);
			}
		}

		private static string Title()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (CombatUiState.IsDuel)
			{
				return Lang.Text.DuelTitle;
			}
			ArenaWire? arenaDisplay = CombatSession.ArenaDisplay;
			int num = ((arenaDisplay != null) ? arenaDisplay.Players.Count : 0);
			return Lang.Text.LobbyTitle(Lang.Text.ModeLabel(CombatSession.Mode), num, 8);
		}

		private static bool CanStart()
		{
			ArenaWire? arenaDisplay = CombatSession.ArenaDisplay;
			return ((arenaDisplay != null) ? arenaDisplay.Players.Count : 0) >= 2;
		}

		private static IReadOnlyList<string> Names()
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			ulong owner = CombatUiState.OwnerId;
			object source;
			if (!CombatUiState.IsDuel)
			{
				ArenaWire? arenaDisplay = CombatSession.ArenaDisplay;
				source = ((arenaDisplay != null) ? arenaDisplay.Players.Select((ArenaWirePlayer p) => p.SteamId) : null) ?? Enumerable.Empty<ulong>();
			}
			else
			{
				source = new ulong[2]
				{
					CombatUiState.LocalId,
					CombatLobby.Opponent().m_SteamID
				}.Where((ulong id) => id != CSteamID.Nil.m_SteamID);
			}
			return ((IEnumerable<ulong>)source).Select((ulong id) => (id != owner) ? CombatUiState.NameOf(id) : (CombatUiState.NameOf(id) + Lang.Text.HostSuffix)).ToList();
		}
	}
	internal static class ModCanvas
	{
		private const int SortingOrder = 500;

		private static Canvas? _canvas;

		public static RectTransform Layer(string name)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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_0062: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			if ((Object)(object)_canvas == (Object)null)
			{
				Create();
			}
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(((Component)_canvas).transform, false);
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.offsetMin = Vector2.zero;
			val2.offsetMax = Vector2.zero;
			return val2;
		}

		private static void Create()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("HTF_CombatCanvas", new Type[1] { typeof(RectTransform) });
			Object.DontDestroyOnLoad((Object)val);
			_canvas = val.AddComponent<Canvas>();
			_canvas.renderMode = (RenderMode)0;
			_canvas.sortingOrder = 500;
			CanvasScaler obj = val.AddComponent<CanvasScaler>();
			obj.uiScaleMode = (ScaleMode)1;
			obj.referenceResolution = new Vector2(1920f, 1080f);
			obj.matchWidthOrHeight = 0.5f;
			val.AddComponent<GraphicRaycaster>();
		}
	}
	internal abstract class NativeScreen : MonoBehaviour
	{
		private RectTransform? _layer;

		private string _key = "";

		protected virtual bool ShowsDuringPause => false;

		protected abstract bool ShouldShow(CombatScreen screen);

		protected abstract string Key();

		protected abstract void Build(RectTransform layer);

		protected virtual void Tick()
		{
		}

		private void Update()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (!GameUiKit.HudReady || !CombatUiState.InCombat || !ShouldShow(CombatUiState.Screen) || (!ShowsDuringPause && PauseManager.IsPaused))
			{
				if ((Object)(object)_layer != (Object)null && ((Component)_layer).gameObject.activeSelf)
				{
					((Component)_layer).gameObject.SetActive(false);
				}
				_key = "";
				return;
			}
			if ((Object)(object)_layer == (Object)null)
			{
				_layer = ModCanvas.Layer(((object)this).GetType().Name);
			}
			if (!((Component)_layer).gameObject.activeSelf)
			{
				((Component)_layer).gameObject.SetActive(true);
			}
			try
			{
				string text = Key();
				if (text != _key)
				{
					Clear(_layer);
					Build(_layer);
					_key = text;
				}
				Tick();
			}
			catch (Exception arg)
			{
				CombatModesPlugin.Log.LogError((object)$"Screen {((object)this).GetType().Name} : {arg}");
				_key = "";
			}
		}

		private static void Clear(RectTransform layer)
		{
			//IL_000f: 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)
			foreach (Transform item in (Transform)layer)
			{
				((Component)item).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
		}
	}
	internal sealed class NoticeScreen : NativeScreen
	{
		private TextMeshProUGUI? _label;

		protected override bool ShowsDuringPause => true;

		protected override bool ShouldShow(CombatScreen screen)
		{
			if (Time.time <= CombatSession.NoticeUntil)
			{
				return CombatSession.Notice.Length > 0;
			}
			return false;
		}

		protected override string Key()
		{
			return "notice";
		}

		protected override void Build(RectTransform layer)
		{
			//IL_000c: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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)
			_label = GameUiKit.Label((Transform)(object)layer, "", 28f, GameInfo.OrangeColor, (TextAlignmentOptions)514);
			RectTransform rectTransform = ((TMP_Text)_label).rectTransform;
			rectTransform.anchorMin = new Vector2(0.5f, 1f);
			rectTransform.anchorMax = new Vector2(0.5f, 1f);
			rectTransform.pivot = new Vector2(0.5f, 1f);
			rectTransform.sizeDelta = new Vector2(1400f, 50f);
			rectTransform.anchoredPosition = new Vector2(0f, -180f);
		}

		protected override void Tick()
		{
			if ((Object)(object)_label != (Object)null)
			{
				((TMP_Text)_label).text = CombatSession.Notice;
			}
		}
	}
	internal sealed class ScoreboardScreen : NativeScreen
	{
		private const float RowHeight = 40f;

		protected override bool ShouldShow(CombatScreen screen)
		{
			//IL_0000: 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_0004: Invalid comparison between Unknown and I4
			if (screen - 2 <= 1)
			{
				return TabHeld();
			}
			return false;
		}

		private static bool TabHeld()
		{
			try
			{
				PlayerInput input = GameInfo.Input;
				return (Object)(object)input != (Object)null && input.actions["PlayerTab"].IsPressed();
			}
			catch (Exception)
			{
				return false;
			}
		}

		protected override string Key()
		{
			return string.Join("\n", from r in Rows()
				select r.Text);
		}

		protected override void Build(RectTransform layer)
		{
			//IL_001f: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			IReadOnlyList<(string, Color)> readOnlyList = Rows();
			Transform transform = ((Component)GameUiKit.Panel((Transform)(object)layer, new Vector2(720f, 130f + (float)readOnlyList.Count * 40f))).transform;
			GameUiKit.Fit((Component)(object)GameUiKit.Label(transform, CombatUiState.IsDuel ? Lang.Text.DuelTitle : Lang.Text.Scoreboard, 40f, GameUiKit.TextColor, (TextAlignmentOptions)514), 60f);
			foreach (var item in readOnlyList)
			{
				GameUiKit.Fit((Component)(object)GameUiKit.Label(transform, item.Item1, 26f, item.Item2, (TextAlignmentOptions)514), 40f);
			}
		}

		private static IReadOnlyList<(string Text, Color Color)> Rows()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (CombatUiState.IsDuel)
			{
				WireState display = CombatSession.Display;
				if (display == (WireState)null)
				{
					return new List<(string, Color)>();
				}
				return HudFormatter.ScoreboardRows(Lang.Text, display, CombatSession.LocalSide, CombatUiState.NameOf(CombatUiState.LocalId), CombatUiState.OpponentName).Select((string text, int i) => (text: text, (i == 0) ? GameInfo.GreenColor : GameUiKit.TextColor)).ToList();
			}
			ArenaWire w = CombatSession.ArenaDisplay;
			if (w == (ArenaWire)null)
			{
				return new List<(string, Color)>();
			}
			return ArenaHudFormatter.Ranking(w).Select((ArenaWirePlayer p, int i) => (ArenaHudFormatter.Row(Lang.Text, i + 1, p, w.Mode, (Func<ulong, string>)CombatUiState.NameOf), (p.SteamId == CombatUiState.LocalId) ? GameInfo.GreenColor : GameUiKit.TextColor)).ToList();
		}
	}
}
namespace HowToFish.CombatModes.Patches
{
	[HarmonyPatch(typeof(Server), "RpcLogic___RespawnPlayer___2210451296")]
	internal static class BlockGameRespawnPatch
	{
		private static bool Prefix()
		{
			return !CombatUiRules.BlocksGameRespawn(CombatSession.IsDuelLobby);
		}
	}
	[HarmonyPatch(typeof(Server), "RpcLogic___ResurrectPlayer___2247010027")]
	internal static class BlockCorpseResurrectionPatch
	{
		private static bool Prefix()
		{
			return !CombatUiRules.BlocksGameRespawn(CombatSession.IsDuelLobby);
		}
	}
	[HarmonyPatch(typeof(PlayerDying), "MouseClick")]
	internal static class BlockGiveUpClickPatch
	{
		private static bool Prefix()
		{
			return !CombatUiRules.BlocksGameRespawn(CombatUiState.InCombat);
		}
	}
	[HarmonyPatch(typeof(DeathUI), "ToggleDeathUI")]
	internal static class HideRespawnHintsPatch
	{
		private static readonly string[] HintFields = new string[4] { "_friendsHelpText", "_giveUpMask", "_giveUpEvent", "_giveUpHolderEvent" };

		private static readonly HashSet<GameObject> Hidden = new HashSet<GameObject>();

		private static void Postfix(DeathUI __instance, bool to)
		{
			if (!to)
			{
				return;
			}
			bool flag = CombatUiRules.BlocksGameRespawn(CombatUiState.InCombat);
			string[] hintFields = HintFields;
			foreach (string text in hintFields)
			{
				object value = Traverse.Create((object)__instance).Field(text).GetValue();
				Component val = (Component)((value is Component) ? value : null);
				GameObject val2 = (GameObject)((val != null) ? val.gameObject : ((value is GameObject) ? value : null));
				if (!((Object)(object)val2 == (Object)null))
				{
					if (flag && val2.activeSelf)
					{
						val2.SetActive(false);
						Hidden.Add(val2);
					}
					else if (!flag && Hidden.Remove(val2))
					{
						val2.SetActive(true);
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(PauseManager), "OnPause")]
	internal static class HideServerButtonsInCombatPatch
	{
		private static void Postfix()
		{
			if (!CombatUiRules.HidesServerButtons(CombatUiState.InCombat))
			{
				return;
			}
			PauseManager value = Traverse.Create<PauseManager>().Field("_instance").GetValue<PauseManager>();
			if ((Object)(object)value == (Object)null)
			{
				return;
			}
			List<GameObject> value2 = Traverse.Create((object)value).Field("_serverButtons").GetValue<List<GameObject>>();
			if (value2 == null)
			{
				return;
			}
			foreach (GameObject item in value2)
			{
				if ((Object)(object)item != (Object)null)
				{
					item.SetActive(false);
				}
			}
		}
	}
	[HarmonyPatch(typeof(PlayerVitals), "TakeDamage")]
	internal static class BlockDamageOutsideFightPatch
	{
		private static readonly FieldRef<PlayerVitals, Player> PlayerOf = AccessTools.FieldRefAccess<PlayerVitals, Player>("_player");

		private static bool Prefix(PlayerVitals __instance)
		{
			if (CombatSession.IsHostingArena)
			{
				if (!((Object)(object)ArenaHost.Instance == (Object)null))
				{
					return ArenaHost.Instance.AllowsDamage(PlayerOf.Invoke(__instance));
				}
				return true;
			}
			return !CombatSession.BlocksDamage;
		}
	}
	[HarmonyPatch(typeof(AchievementManager), "CheckAttachmentsAchievement")]
	internal static class NoAttachmentAchievementInDuelPatch
	{
		private static bool Prefix()
		{
			return !CombatSession.IsDuelLobby;
		}
	}
	[HarmonyPatch(typeof(Server), "RpcLogic___HitPlayer___2449261505")]
	internal static class TrackHitsPatch
	{
		private static void Prefix(Player __0, Player __5)
		{
			if (CombatSession.IsHostingArena)
			{
				HitTracker.Record(__0, __5, Time.time);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerDying), "ServerDie")]
	internal static class DetectDuelDeathPatch
	{
		private static readonly FieldRef<PlayerDying, Player> PlayerOf = AccessTools.FieldRefAccess<PlayerDying, Player>("_player");

		private static void Postfix(PlayerDying __instance)
		{
			Player val = PlayerOf.Invoke(__instance);
			if (CombatSession.IsHostingDuel)
			{
				DuelHost.Instance?.OnPlayerDied(val);
			}
			else if (CombatSession.IsHostingArena)
			{
				ArenaHost.Instance?.OnPlayerDied(val);
			}
		}
	}
	[HarmonyPatch(typeof(SteamManager), "CreateLobby")]
	internal static class CreateDuelLobbyPatch
	{
		private static bool Prefix()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or