Decompiled source of FusionGunGame v1.0.0

mods/FusionGunGame.dll

Decompiled 15 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.Json;
using BoneLib;
using BonelabGunGame;
using HarmonyLib;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Combat;
using Il2CppSLZ.Marrow.Data;
using Il2CppSLZ.Marrow.Warehouse;
using LabFusion.Entities;
using LabFusion.Extensions;
using LabFusion.Network;
using LabFusion.Network.Serialization;
using LabFusion.Player;
using LabFusion.RPC;
using LabFusion.SDK.Gamemodes;
using LabFusion.SDK.Modules;
using MelonLoader;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonInfo(typeof(FusionGunGameMod), "Fusion Gun Game", "1.0.8", "Local Mod", null)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("FusionGunGame")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("FusionGunGame")]
[assembly: AssemblyTitle("FusionGunGame")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BonelabGunGame
{
	public sealed class FusionGunGameMod : MelonMod
	{
		private Harmony _harmony;

		public override void OnInitializeMelon()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			_harmony = new Harmony("local.bonelab.fusiongungame");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			if (MelonBase.FindMelon("LabFusion", "Lakatrazz") == null)
			{
				((MelonBase)this).LoggerInstance.Warning("Fusion was not found; Gun Game will stay inactive.");
				return;
			}
			ModuleManager.RegisterModule<GunGameModule>();
			GamemodeRegistration.LoadGamemodes(Assembly.GetExecutingAssembly());
			((MelonBase)this).LoggerInstance.Msg("Fusion Gun Game loaded. Select it from Fusion's gamemode list.");
		}

		public override void OnDeinitializeMelon()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	public sealed class GunGameModule : Module
	{
		public override string Name => "Fusion Gun Game";

		public override string Author => "Local Mod";

		public override Version Version => new Version(1, 0, 8);

		public override ConsoleColor Color => ConsoleColor.Cyan;

		protected override void OnModuleRegistered()
		{
			ModuleMessageManager.RegisterHandler<KillReportMessage>();
			ModuleMessageManager.RegisterHandler<TierUpdateMessage>();
		}
	}
	public sealed class FusionGunGameMode : Gamemode
	{
		private static readonly string[] FallbackBarcodes = new string[10] { "c1534c5a-fcfc-4f43-8fb0-d29531393131", "c1534c5a-bcb7-4f02-a4f5-da9550333530", "c1534c5a-2a4f-481f-8542-cc9545646572", "c1534c5a-d00c-4aa8-adfd-3495534d474d", "c1534c5a-8d03-42de-93c7-f595534d4755", "c1534c5a-4c47-428d-b5a5-b05747756e56", "c1534c5a-40e5-40e0-8139-194347756e55", "c1534c5a-a6b5-4177-beb8-04d947756e41", "c1534c5a-9112-49e5-b022-9c955269666c", "c1534c5a-c061-4c5c-a5e2-3d955269666c" };

		private const int MatchTiers = 10;

		private static readonly Random Random = new Random();

		private static FusionGunGameMode _instance;

		private readonly GunGameProgression _progression = new GunGameProgression(10);

		private byte? _lastAttacker;

		private float _lastAttackTime;

		private ushort? _currentNetworkWeaponId;

		private string _currentBarcode;

		private readonly Dictionary<byte, (int tier, string barcode)> _lastAppliedUpdate = new Dictionary<byte, (int, string)>();

		private readonly Dictionary<byte, float> _recentVictimDeaths = new Dictionary<byte, float>();

		private int _respawnGeneration;

		internal static float LocalInvulnerabilityUntil = float.NegativeInfinity;

		public override string Title => "Gun Game";

		public override string Author => "Local Mod";

		public override string Description => "Get a player kill to advance to the next weapon. First to finish the weapon ladder wins.";

		public override void OnGamemodeRegistered()
		{
			_instance = this;
			((Gamemode)this).OnGamemodeRegistered();
		}

		public override void OnGamemodeUnregistered()
		{
			if (_instance == this)
			{
				_instance = null;
			}
			((Gamemode)this).OnGamemodeUnregistered();
		}

		public override void OnGamemodeStarted()
		{
			SdkGunCatalog.Load();
			_progression.ResetMatch();
			_lastAppliedUpdate.Clear();
			_recentVictimDeaths.Clear();
			_lastAttacker = null;
			LocalHealth.OnAttackedByPlayer += OnAttackedByPlayer;
			LocalHealth.OnRespawn += OnLocalRespawn;
			SpawnLocalTier(0, PickGun());
			((Gamemode)this).OnGamemodeStarted();
		}

		public override void OnGamemodeStopped()
		{
			LocalHealth.OnAttackedByPlayer -= OnAttackedByPlayer;
			LocalHealth.OnRespawn -= OnLocalRespawn;
			LocalInvulnerabilityUntil = float.NegativeInfinity;
			_lastAttacker = null;
			_respawnGeneration++;
			DespawnCurrentWeapon();
			_progression.ResetMatch();
			_lastAppliedUpdate.Clear();
			_recentVictimDeaths.Clear();
			((Gamemode)this).OnGamemodeStopped();
		}

		protected override void OnPlayerLeft(PlayerID player)
		{
			_progression.ResetPlayer(player.SmallID);
			((Gamemode)this).OnPlayerLeft(player);
		}

		private void OnAttackedByPlayer(Attack attack, BodyPart bodyPart, PlayerID attacker)
		{
			if (attacker != null && attacker.IsValid)
			{
				_lastAttacker = attacker.SmallID;
				_lastAttackTime = Time.time;
			}
		}

		private void OnLocalRespawn()
		{
			_lastAttacker = null;
			LocalInvulnerabilityUntil = Time.time + 3f;
			MelonLogger.Msg("Gun Game: 3-second respawn immunity started.");
			MelonCoroutines.Start(RespawnRoutine(++_respawnGeneration));
		}

		private IEnumerator RespawnRoutine(int generation)
		{
			yield return (object)new WaitForSeconds(0.8f);
			if (generation == _respawnGeneration && ((Gamemode)this).IsStarted && PlayerIDManager.LocalID != null)
			{
				MoveToRandomSpawn();
				SpawnLocalTier(_progression.GetTier(PlayerIDManager.LocalID.SmallID), _currentBarcode ?? PickGun());
			}
		}

		internal static void OnLocalPlayerDeath(Player_Health health)
		{
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			FusionGunGameMode instance = _instance;
			if (instance == null || !((Gamemode)instance).IsStarted || (Object)(object)health == (Object)null)
			{
				return;
			}
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager == (Object)null || (Object)(object)((Component)health).transform == (Object)null || (Object)(object)((Component)health).transform.root != (Object)(object)((Component)rigManager).transform.root)
			{
				return;
			}
			if (!instance._lastAttacker.HasValue || Time.time - instance._lastAttackTime > 10f)
			{
				instance._lastAttacker = null;
				return;
			}
			PlayerID localID = PlayerIDManager.LocalID;
			byte value = instance._lastAttacker.Value;
			instance._lastAttacker = null;
			if (localID != null && value != localID.SmallID)
			{
				KillReportData killReportData = new KillReportData
				{
					KillerId = value,
					VictimId = localID.SmallID
				};
				if (NetworkInfo.IsHost)
				{
					ProcessKillReport(killReportData);
				}
				else
				{
					MessageRelay.RelayModule<KillReportMessage, KillReportData>(killReportData, new MessageRoute((RelayType)1, (NetworkChannel)0));
				}
				MelonLogger.Msg($"Gun Game: reported player kill {value} -> {localID.SmallID}.");
			}
		}

		internal static void HandleKillReport(KillReportData data, ReceivedMessage received)
		{
			FusionGunGameMode instance = _instance;
			if (instance != null && ((Gamemode)instance).IsStarted && NetworkInfo.IsHost && (!((ReceivedMessage)(ref received)).Sender.HasValue || ((ReceivedMessage)(ref received)).Sender.Value == data.VictimId))
			{
				ProcessKillReport(data);
			}
		}

		private static void ProcessKillReport(KillReportData data)
		{
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			FusionGunGameMode instance = _instance;
			if (instance == null || !((Gamemode)instance).IsStarted || !NetworkInfo.IsHost || data.KillerId == data.VictimId)
			{
				return;
			}
			float time = Time.time;
			if (!instance._recentVictimDeaths.TryGetValue(data.VictimId, out var value) || !(time - value < 3f))
			{
				instance._recentVictimDeaths[data.VictimId] = time;
				if (instance._progression.RecordKill(data.KillerId, data.VictimId, out var newTier))
				{
					bool isWinner = instance._progression.HasWon(data.KillerId);
					MelonLogger.Msg($"Gun Game: player {data.KillerId} advanced to weapon tier {newTier + 1}.");
					string barcode = instance.PickGun();
					TierUpdateData tierUpdateData = new TierUpdateData
					{
						PlayerId = data.KillerId,
						Tier = newTier,
						IsWinner = isWinner,
						Barcode = barcode
					};
					instance.ApplyTier(tierUpdateData.PlayerId, tierUpdateData.Tier, tierUpdateData.IsWinner, tierUpdateData.Barcode);
					MessageRelay.RelayModule<TierUpdateMessage, TierUpdateData>(tierUpdateData, new MessageRoute((RelayType)2, (NetworkChannel)0));
				}
			}
		}

		internal static void HandleTierUpdate(TierUpdateData data)
		{
			_instance?.ApplyTier(data.PlayerId, data.Tier, data.IsWinner, data.Barcode);
		}

		private void ApplyTier(byte playerId, int tier, bool winner, string barcode)
		{
			if (_lastAppliedUpdate.TryGetValue(playerId, out (int, string) value) && value.Item1 == tier && string.Equals(value.Item2, barcode, StringComparison.Ordinal))
			{
				return;
			}
			_lastAppliedUpdate[playerId] = (tier, barcode);
			_progression.ApplyAuthoritativeTier(playerId, tier);
			if (playerId == PlayerIDManager.LocalID.SmallID)
			{
				if (winner)
				{
					MelonLogger.Msg("Gun Game: you won the match!");
				}
				else
				{
					SpawnLocalTier(tier, barcode);
				}
			}
			if (winner)
			{
				MelonLogger.Msg($"Gun Game: player {playerId} won the match.");
				if (NetworkInfo.IsHost)
				{
					GamemodeManager.StopGamemode();
				}
			}
		}

		private string PickGun()
		{
			IReadOnlyList<string> barcodes = SdkGunCatalog.Barcodes;
			if (barcodes.Count <= 0)
			{
				return FallbackBarcodes[Random.Next(FallbackBarcodes.Length)];
			}
			return barcodes[Random.Next(barcodes.Count)];
		}

		private void SpawnLocalTier(int tier, string barcode)
		{
			//IL_007a: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			//IL_00c9: Expected O, but got Unknown
			//IL_00cb: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			if (tier < 0 || tier >= _progression.TierCount)
			{
				return;
			}
			try
			{
				DespawnCurrentWeapon();
				RigManager rigManager = Player.RigManager;
				if ((Object)(object)rigManager == (Object)null || (Object)(object)rigManager.physicsRig == (Object)null || (Object)(object)((Rig)rigManager.physicsRig).m_head == (Object)null)
				{
					return;
				}
				Transform head = ((Rig)rigManager.physicsRig).m_head;
				Vector3 val = head.position + head.forward * 0.8f;
				if (string.IsNullOrWhiteSpace(barcode))
				{
					barcode = PickGun();
				}
				_currentBarcode = barcode;
				if (NetworkInfo.HasServer)
				{
					Spawnable spawnable = new Spawnable
					{
						crateRef = new SpawnableCrateReference(barcode)
					};
					NetworkAssetSpawner.Spawn(new SpawnRequestInfo
					{
						Spawnable = spawnable,
						Position = val,
						Rotation = head.rotation,
						SpawnEffect = false,
						SpawnSource = (EntitySource)0,
						SpawnCallback = delegate(SpawnCallbackInfo info)
						{
							//IL_0000: 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_0056: Unknown result type (might be due to invalid IL or missing references)
							if (info.Entity != null)
							{
								_currentNetworkWeaponId = info.Entity.ID;
								MelonLogger.Msg($"Gun Game: spawned synchronized weapon tier {tier + 1} (entity {info.Entity.ID}).");
							}
							else
							{
								MelonLogger.Warning("Gun Game: Fusion returned a weapon spawn without a network entity.");
							}
						}
					});
					MelonLogger.Msg($"Gun Game: requesting synchronized weapon tier {tier + 1}.");
				}
				else
				{
					HelperMethods.SpawnCrate(barcode, val, head.rotation, Vector3.one, false, (Action<GameObject>)null, (Action<GameObject>)null);
				}
			}
			catch (Exception value)
			{
				MelonLogger.Error($"Gun Game could not spawn tier {tier + 1}: {value}");
			}
		}

		private void MoveToRandomSpawn()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				RigManager rigManager = Player.RigManager;
				if ((Object)(object)rigManager == (Object)null)
				{
					return;
				}
				Vector3 position = ((Component)rigManager).transform.position;
				List<Vector3> list = new List<Vector3>();
				RaycastHit val = default(RaycastHit);
				for (int i = 0; i < 40; i++)
				{
					float num = (float)Random.NextDouble() * ((float)Math.PI * 2f);
					float num2 = 12f + (float)Random.NextDouble() * 33f;
					float num3 = position.x + Mathf.Cos(num) * num2;
					float num4 = position.z + Mathf.Sin(num) * num2;
					if (Physics.Raycast(new Vector3(num3, position.y + 100f, num4), Vector3.down, ref val, 250f) && !((Object)(object)((RaycastHit)(ref val)).collider == (Object)null) && !(((RaycastHit)(ref val)).normal.y < 0.65f))
					{
						Vector3 val2 = ((RaycastHit)(ref val)).point + Vector3.up * 0.2f;
						if (Vector3.Distance(val2, position) >= 10f)
						{
							list.Add(val2);
						}
					}
				}
				if (list.Count == 0)
				{
					MelonLogger.Warning("Gun Game: no walkable ground found near the map's respawn point; keeping the normal location.");
					return;
				}
				Vector3 val3 = list[Random.Next(list.Count)];
				RigManagerExtensions.TeleportToPosition(rigManager, val3, true);
				MelonLogger.Msg($"Gun Game: teleported from respawn point {position} to random point {val3}.");
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Gun Game: random respawn failed; using normal respawn location. " + ex.Message);
			}
		}

		private void DespawnCurrentWeapon()
		{
			//IL_0025: 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)
			ushort? currentNetworkWeaponId = _currentNetworkWeaponId;
			_currentNetworkWeaponId = null;
			try
			{
				if (NetworkInfo.HasServer && currentNetworkWeaponId.HasValue)
				{
					NetworkAssetSpawner.Despawn(new DespawnRequestInfo
					{
						EntityID = currentNetworkWeaponId.Value,
						DespawnEffect = false
					});
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Gun Game could not remove the previous weapon: " + ex.Message);
			}
		}
	}
	public sealed class KillReportData : INetSerializable
	{
		public byte KillerId;

		public byte VictimId;

		public int? GetSize()
		{
			return 2;
		}

		public void Serialize(INetSerializer serializer)
		{
			serializer.SerializeValue(ref KillerId);
			serializer.SerializeValue(ref VictimId);
		}
	}
	public sealed class KillReportMessage : ModuleMessageHandler
	{
		protected override void OnHandleMessage(ReceivedMessage received)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			FusionGunGameMode.HandleKillReport(((ReceivedMessage)(ref received)).ReadData<KillReportData>(), received);
		}
	}
	internal static class SdkGunCatalog
	{
		private static readonly List<string> _barcodes = new List<string>();

		public static IReadOnlyList<string> Barcodes => _barcodes;

		public static void Load()
		{
			if (_barcodes.Count > 0)
			{
				return;
			}
			string text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "LocalLow", "Stress Level Zero", "BONELAB", "Mods");
			if (!Directory.Exists(text))
			{
				MelonLogger.Warning("Gun Game: SDK mods folder was not found at " + text + "; using fallback firearms.");
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			int num = 0;
			int num2 = 0;
			foreach (string item in Directory.EnumerateFiles(text, "*.pallet.json", SearchOption.AllDirectories))
			{
				try
				{
					using JsonDocument jsonDocument = JsonDocument.Parse(File.ReadAllText(item));
					if (!jsonDocument.RootElement.TryGetProperty("objects", out var value) || value.ValueKind != JsonValueKind.Object)
					{
						continue;
					}
					if (IsExcludedPallet(item, value))
					{
						num++;
						continue;
					}
					foreach (JsonProperty item2 in value.EnumerateObject())
					{
						JsonElement value2 = item2.Value;
						if (!value2.TryGetProperty("isa", out var value3) || !value3.TryGetProperty("type", out var value4) || !(value4.GetString() ?? string.Empty).StartsWith("crate-spawnable", StringComparison.OrdinalIgnoreCase) || !value2.TryGetProperty("tags", out var value5) || value5.ValueKind != JsonValueKind.Array || !value5.EnumerateArray().Any((JsonElement tag) => string.Equals(tag.GetString(), "Gun", StringComparison.OrdinalIgnoreCase)))
						{
							continue;
						}
						JsonElement value6;
						if (HasExcludedGunName(value2))
						{
							num2++;
						}
						else if (value2.TryGetProperty("barcode", out value6) && value6.ValueKind == JsonValueKind.String)
						{
							string text2 = value6.GetString();
							if (!string.IsNullOrWhiteSpace(text2))
							{
								hashSet.Add(text2);
							}
						}
					}
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("Gun Game: couldn't read SDK pallet " + Path.GetFileName(item) + ": " + ex.Message);
				}
			}
			_barcodes.AddRange(hashSet);
			MelonLogger.Msg($"Gun Game: loaded {_barcodes.Count} SDK guns; skipped {num} excluded pallets and {num2} excluded guns.");
			if (_barcodes.Count == 0)
			{
				MelonLogger.Warning("Gun Game: no Gun-tagged SDK crates found; using fallback firearms.");
			}
		}

		private static bool IsExcludedPallet(string file, JsonElement objects)
		{
			if (ContainsExcludedPack(file.Replace('\\', '/')))
			{
				return true;
			}
			foreach (JsonProperty item in objects.EnumerateObject())
			{
				JsonElement value = item.Value;
				if (value.TryGetProperty("isa", out var value2) && value2.TryGetProperty("type", out var value3) && (value3.GetString() ?? string.Empty).StartsWith("pallet", StringComparison.OrdinalIgnoreCase))
				{
					return ContainsExcludedPack(GetString(value, "title")) || ContainsExcludedPack(GetString(value, "barcode"));
				}
			}
			return false;
		}

		private static bool HasExcludedGunName(JsonElement crate)
		{
			string value = GetString(crate, "title");
			string text = GetString(crate, "barcode");
			if (!ContainsTranqName(value) && !ContainsTranqName(text))
			{
				return text.StartsWith("CyberBlue.LowPolyWeaponPack.", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static bool ContainsExcludedPack(string value)
		{
			if (!value.Contains("deadpool", StringComparison.OrdinalIgnoreCase) && !value.Contains("lowpoly", StringComparison.OrdinalIgnoreCase))
			{
				return value.Contains("low poly", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static bool ContainsTranqName(string value)
		{
			if (!value.Contains("tranq", StringComparison.OrdinalIgnoreCase))
			{
				return value.Contains("tranquil", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static string GetString(JsonElement element, string property)
		{
			if (!element.TryGetProperty(property, out var value) || value.ValueKind != JsonValueKind.String)
			{
				return string.Empty;
			}
			return value.GetString() ?? string.Empty;
		}
	}
	public sealed class TierUpdateData : INetSerializable
	{
		public byte PlayerId;

		public int Tier;

		public bool IsWinner;

		public string Barcode;

		public int? GetSize()
		{
			return null;
		}

		public void Serialize(INetSerializer serializer)
		{
			serializer.SerializeValue(ref PlayerId);
			serializer.SerializeValue(ref Tier);
			serializer.SerializeValue(ref IsWinner);
			serializer.SerializeValue(ref Barcode);
		}
	}
	public sealed class TierUpdateMessage : ModuleMessageHandler
	{
		protected override void OnHandleMessage(ReceivedMessage received)
		{
			FusionGunGameMode.HandleTierUpdate(((ReceivedMessage)(ref received)).ReadData<TierUpdateData>());
		}
	}
	[HarmonyPatch(typeof(Player_Health), "Death")]
	internal static class PlayerDeathPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Player_Health __instance)
		{
			FusionGunGameMode.OnLocalPlayerDeath(__instance);
		}
	}
	[HarmonyPatch(typeof(Player_Health), "TAKEDAMAGE")]
	internal static class RespawnImmunityPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Player_Health __instance)
		{
			if (Time.time >= FusionGunGameMode.LocalInvulnerabilityUntil || (Object)(object)__instance == (Object)null || (Object)(object)((Component)__instance).transform == (Object)null)
			{
				return true;
			}
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager == (Object)null || (Object)(object)((Component)__instance).transform.root != (Object)(object)((Component)rigManager).transform.root)
			{
				return true;
			}
			return false;
		}
	}
	public sealed class GunGameProgression
	{
		private readonly Dictionary<int, int> _tiers = new Dictionary<int, int>();

		private readonly int _tierCount;

		public int TierCount => _tierCount;

		public GunGameProgression(int tierCount)
		{
			if (tierCount <= 0)
			{
				throw new ArgumentOutOfRangeException("tierCount");
			}
			_tierCount = tierCount;
		}

		public int GetTier(int playerId)
		{
			if (!_tiers.TryGetValue(playerId, out var value))
			{
				return 0;
			}
			return value;
		}

		public bool RecordKill(int killerId, int victimId, out int newTier)
		{
			newTier = GetTier(killerId);
			if (killerId == victimId || newTier >= _tierCount)
			{
				return false;
			}
			newTier++;
			_tiers[killerId] = newTier;
			return true;
		}

		public void ApplyAuthoritativeTier(int playerId, int tier)
		{
			if (tier < 0 || tier > _tierCount)
			{
				throw new ArgumentOutOfRangeException("tier");
			}
			if (tier == 0)
			{
				_tiers.Remove(playerId);
			}
			else
			{
				_tiers[playerId] = tier;
			}
		}

		public bool HasWon(int playerId)
		{
			return GetTier(playerId) >= _tierCount;
		}

		public void ResetPlayer(int playerId)
		{
			_tiers.Remove(playerId);
		}

		public void ResetMatch()
		{
			_tiers.Clear();
		}
	}
}