Decompiled source of SharingIsCaring v0.3.0

plugins/SharingIsCaring/SharingIsCaring.dll

Decompiled 12 hours ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace SharingIsCaring;

[BepInPlugin("mvffyn.SharingIsCaring", "Sharing Is Caring", "0.3.0")]
public class Plugin : BaseUnityPlugin
{
	public const string GUID = "mvffyn.SharingIsCaring";

	public const string NAME = "Sharing Is Caring";

	public const string VERSION = "0.3.0";

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> SharedUpgrades;

	internal static ConfigEntry<bool> SharedHealing;

	internal static ConfigEntry<float> SharedHealingFraction;

	internal static ConfigEntry<float> ShopPriceMultiplier;

	internal static ConfigEntry<int> MaxPlayers;

	private void Awake()
	{
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Expected O, but got Unknown
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Expected O, but got Unknown
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Expected O, but got Unknown
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Expected O, but got Unknown
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d8: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		SharedUpgrades = ((BaseUnityPlugin)this).Config.Bind<bool>("Sharing", "SharedUpgrades", true, "When any player uses an upgrade item, every player in the lobby receives the same upgrade.");
		SharedHealing = ((BaseUnityPlugin)this).Config.Bind<bool>("Sharing", "SharedHealing", true, "When any player uses a health pack, every other player is healed too.");
		SharedHealingFraction = ((BaseUnityPlugin)this).Config.Bind<float>("Sharing", "SharedHealingFraction", 1f, new ConfigDescription("How much of the health pack's healing the other players receive (1 = full amount, 0.5 = half).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		ShopPriceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Shop", "PriceMultiplier", 0.5f, new ConfigDescription("Multiplier on every shop price. 0.5 = half price, 1 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>()));
		MaxPlayers = ((BaseUnityPlugin)this).Config.Bind<int>("Lobby", "MaxPlayers", 10, new ConfigDescription("Maximum players in a lobby you host. Vanilla is 6; the game's networking allows up to 20. Beyond 6, players share spawn spots and colours.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
		TeamConfig.Bind(((BaseUnityPlugin)this).Config);
		Harmony val = new Harmony("mvffyn.SharingIsCaring");
		if (MaxPlayers.Value != 6)
		{
			val.PatchAll(typeof(LobbyPatches));
		}
		if (TeamConfig.ManyHands.Value)
		{
			val.PatchAll(typeof(ManyHandsPatch));
		}
		if (TeamConfig.SafetyInNumbers.Value)
		{
			val.PatchAll(typeof(SafetyPatches));
		}
		if (TeamConfig.RescueRewards.Value)
		{
			val.PatchAll(typeof(RescuePatches));
		}
		if (TeamConfig.TeamMilestones.Value || TeamConfig.ClutchMode.Value)
		{
			val.PatchAll(typeof(RoundPatches));
		}
		TeamEvents.Register();
		if (TeamConfig.Ping.Value)
		{
			GameObject val2 = new GameObject("SharingIsCaring.Ping");
			Object.DontDestroyOnLoad((Object)(object)val2);
			val2.AddComponent<PingInput>();
		}
		if (SharedUpgrades.Value)
		{
			UpgradePatches.Apply(val);
		}
		if (SharedHealing.Value)
		{
			val.PatchAll(typeof(HealingPatches));
		}
		if (Math.Abs(ShopPriceMultiplier.Value - 1f) > 0.001f)
		{
			val.PatchAll(typeof(ShopPatches));
		}
		Log.LogInfo((object)"Sharing Is Caring 0.3.0 loaded. Host-side rules need only the host; Safety In Numbers, head-carry speed, messages and pings need the mod on each player.");
	}

	internal static bool IsHost()
	{
		try
		{
			return SemiFunc.IsMasterClientOrSingleplayer();
		}
		catch
		{
			return true;
		}
	}

	internal static List<PlayerAvatar> Players()
	{
		try
		{
			return SemiFunc.PlayerGetAll() ?? new List<PlayerAvatar>();
		}
		catch
		{
			return new List<PlayerAvatar>();
		}
	}
}
internal static class UpgradePatches
{
	private static readonly string[] Methods = new string[13]
	{
		"UpgradePlayerHealth", "UpgradePlayerEnergy", "UpgradePlayerExtraJump", "UpgradeMapPlayerCount", "UpgradePlayerTumbleLaunch", "UpgradePlayerTumbleClimb", "UpgradeDeathHeadBattery", "UpgradePlayerTumbleWings", "UpgradePlayerSprintSpeed", "UpgradePlayerCrouchRest",
		"UpgradePlayerGrabStrength", "UpgradePlayerThrowStrength", "UpgradePlayerGrabRange"
	};

	[ThreadStatic]
	private static bool reentrant;

	internal static void Apply(Harmony harmony)
	{
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Expected O, but got Unknown
		int num = 0;
		string[] methods = Methods;
		foreach (string text in methods)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(PunManager), text, new Type[2]
			{
				typeof(string),
				typeof(int)
			}, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Log.LogWarning((object)("Upgrade method not found: " + text));
				continue;
			}
			MethodInfo methodInfo2 = methodInfo;
			HarmonyMethod val = new HarmonyMethod(typeof(UpgradePatches), "Postfix", (Type[])null);
			harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			num++;
		}
		Plugin.Log.LogInfo((object)("Shared upgrades: hooked " + num + " upgrade types."));
	}

	private static void Postfix(PunManager __instance, MethodBase __originalMethod, string _steamID, int value)
	{
		if (reentrant || value <= 0 || !Plugin.IsHost())
		{
			return;
		}
		reentrant = true;
		try
		{
			int num = 0;
			foreach (PlayerAvatar item in Plugin.Players())
			{
				if (!((Object)(object)item == (Object)null))
				{
					string text = SemiFunc.PlayerGetSteamID(item);
					if (!string.IsNullOrEmpty(text) && !(text == _steamID))
					{
						__originalMethod.Invoke(__instance, new object[2] { text, value });
						num++;
					}
				}
			}
			if (num > 0)
			{
				Plugin.Log.LogInfo((object)(__originalMethod.Name + " shared with " + num + " other player(s)."));
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Shared upgrade failed: " + ex));
		}
		finally
		{
			reentrant = false;
		}
	}
}
[HarmonyPatch]
internal static class HealingPatches
{
	private static bool inHealthPack;

	private static bool reentrant;

	[HarmonyPatch(typeof(ItemHealthPack), "Update")]
	[HarmonyPrefix]
	private static void HealthPackPrefix()
	{
		inHealthPack = true;
	}

	[HarmonyPatch(typeof(ItemHealthPack), "Update")]
	[HarmonyPostfix]
	private static void HealthPackPostfix()
	{
		inHealthPack = false;
	}

	[HarmonyPatch(typeof(PlayerHealth), "HealOther", new Type[]
	{
		typeof(int),
		typeof(bool)
	})]
	[HarmonyPostfix]
	private static void HealOtherPostfix(PlayerHealth __instance, int healAmount, bool effect)
	{
		if (!inHealthPack || reentrant || healAmount <= 0 || !Plugin.IsHost())
		{
			return;
		}
		int num = Mathf.RoundToInt((float)healAmount * Plugin.SharedHealingFraction.Value);
		if (num <= 0)
		{
			return;
		}
		reentrant = true;
		try
		{
			int num2 = 0;
			foreach (PlayerAvatar item in Plugin.Players())
			{
				if (!((Object)(object)item == (Object)null) && !((Object)(object)item.playerHealth == (Object)null) && !((Object)(object)item.playerHealth == (Object)(object)__instance))
				{
					item.playerHealth.HealOther(num, effect);
					num2++;
				}
			}
			if (num2 > 0)
			{
				Plugin.Log.LogInfo((object)("Health pack shared: +" + num + " to " + num2 + " other player(s)."));
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Shared healing failed: " + ex));
		}
		finally
		{
			reentrant = false;
		}
	}
}
[HarmonyPatch(typeof(ShopManager), "Awake")]
internal static class ShopPatches
{
	private static readonly FieldInfo MultField = AccessTools.Field(typeof(ShopManager), "itemValueMultiplier");

	[HarmonyPostfix]
	private static void Postfix(ShopManager __instance)
	{
		try
		{
			if (MultField == null)
			{
				Plugin.Log.LogWarning((object)"itemValueMultiplier not found.");
				return;
			}
			float num = (float)MultField.GetValue(__instance);
			float num2 = num * Plugin.ShopPriceMultiplier.Value;
			MultField.SetValue(__instance, num2);
			Plugin.Log.LogInfo((object)("Shop price multiplier " + num + " -> " + num2));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Shop price patch failed: " + ex));
		}
	}
}
[HarmonyPatch(typeof(GameManager), "Awake")]
internal static class LobbyPatches
{
	[HarmonyPostfix]
	private static void Postfix(GameManager __instance)
	{
		try
		{
			int num = (__instance.maxPlayers = Mathf.Clamp(Plugin.MaxPlayers.Value, 1, Mathf.Max(1, __instance.maxPlayersPhoton)));
			Plugin.Log.LogInfo((object)("Lobby size set to " + num + " players."));
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)("Lobby size patch failed: " + ex));
		}
	}
}
internal static class TeamConfig
{
	internal static ConfigEntry<bool> ManyHands;

	internal static ConfigEntry<float> ManyHandsStrengthPerHelper;

	internal static ConfigEntry<float> ManyHandsFragility;

	internal static ConfigEntry<bool> SafetyInNumbers;

	internal static ConfigEntry<float> SafetyRadius;

	internal static ConfigEntry<float> SafetyStaminaRegen;

	internal static ConfigEntry<float> SafetyDamageReduction;

	internal static ConfigEntry<bool> RescueRewards;

	internal static ConfigEntry<float> HeadCarrySpeed;

	internal static ConfigEntry<int> ReviveHeal;

	internal static ConfigEntry<bool> TeamMilestones;

	internal static ConfigEntry<int> MilestoneHeal;

	internal static ConfigEntry<bool> ClutchMode;

	internal static ConfigEntry<int> ClutchHeal;

	internal static ConfigEntry<bool> Ping;

	internal static ConfigEntry<string> PingKey;

	internal static ConfigEntry<float> PingDuration;

	internal static void Bind(ConfigFile cfg)
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Expected O, but got Unknown
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Expected O, but got Unknown
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Expected O, but got Unknown
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Expected O, but got Unknown
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Expected O, but got Unknown
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Expected O, but got Unknown
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ca: Expected O, but got Unknown
		//IL_0209: Unknown result type (might be due to invalid IL or missing references)
		//IL_0213: Expected O, but got Unknown
		//IL_0252: Unknown result type (might be due to invalid IL or missing references)
		//IL_025c: Expected O, but got Unknown
		//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ce: Expected O, but got Unknown
		ManyHands = cfg.Bind<bool>("Many Hands", "Enabled", true, "Two or more players grabbing the same object get bonus grab strength and the object is harder to break. Host-side.");
		ManyHandsStrengthPerHelper = cfg.Bind<float>("Many Hands", "StrengthPerHelper", 0.3f, new ConfigDescription("Extra grab strength per additional grabber (0.3 = +30% each).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		ManyHandsFragility = cfg.Bind<float>("Many Hands", "FragilityMultiplier", 0.5f, new ConfigDescription("Impact damage multiplier for objects held by 2+ players (0.5 = half damage).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		SafetyInNumbers = cfg.Bind<bool>("Safety In Numbers", "Enabled", true, "Players near a living teammate regain stamina faster and take less damage. Client-side: each player needs the mod for their own buff.");
		SafetyRadius = cfg.Bind<float>("Safety In Numbers", "Radius", 8f, new ConfigDescription("How close (meters) a teammate must be.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 30f), Array.Empty<object>()));
		SafetyStaminaRegen = cfg.Bind<float>("Safety In Numbers", "StaminaRegenPerSecond", 6f, new ConfigDescription("Extra stamina per second while near a teammate (full bar is 40).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 40f), Array.Empty<object>()));
		SafetyDamageReduction = cfg.Bind<float>("Safety In Numbers", "DamageReduction", 0.2f, new ConfigDescription("Fraction of incoming damage removed while near a teammate.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.75f), Array.Empty<object>()));
		RescueRewards = cfg.Bind<bool>("Rescue Rewards", "Enabled", true, "Carrying a teammate's head makes you faster; a revive heals both the revived player and the nearest rescuer.");
		HeadCarrySpeed = cfg.Bind<float>("Rescue Rewards", "HeadCarrySpeedMultiplier", 1.35f, new ConfigDescription("Sprint speed multiplier while carrying a death head (client-side).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 2.5f), Array.Empty<object>()));
		ReviveHeal = cfg.Bind<int>("Rescue Rewards", "ReviveHeal", 30, new ConfigDescription("Health given to the revived player and the nearest living rescuer (host-side).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
		TeamMilestones = cfg.Bind<bool>("Team Milestones", "Enabled", true, "At 25/50/75/100% of the haul goal everyone gets a message and a small heal. Host-side; message shows for players with the mod.");
		MilestoneHeal = cfg.Bind<int>("Team Milestones", "Heal", 10, new ConfigDescription("Health given to every player at each milestone.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>()));
		ClutchMode = cfg.Bind<bool>("Clutch Mode", "Enabled", true, "When only one player is left alive they get a one-time heal and full stamina. Host-side; stamina refill needs the mod on that player.");
		ClutchHeal = cfg.Bind<int>("Clutch Mode", "Heal", 40, new ConfigDescription("Health given to the last player standing.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
		Ping = cfg.Bind<bool>("Ping", "Enabled", true, "Press the ping key to mark what you're looking at for every player who has the mod.");
		PingKey = cfg.Bind<string>("Ping", "Key", "MiddleButton", "Key that places a ping. Mouse: LeftButton, RightButton, MiddleButton, ForwardButton, BackButton. Keyboard: any Unity InputSystem key name, e.g. T, G, F, Q, CapsLock.");
		PingDuration = cfg.Bind<float>("Ping", "Duration", 6f, new ConfigDescription("Seconds a ping marker stays visible.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 20f), Array.Empty<object>()));
	}
}
internal static class Team
{
	internal static readonly Color Pink = new Color(1f, 0.55f, 0.75f);

	internal static readonly Color Mint = new Color(0.55f, 1f, 0.8f);

	internal static readonly Color Gold = new Color(1f, 0.85f, 0.3f);

	internal static bool IsAlive(PlayerAvatar a)
	{
		if ((Object)(object)a == (Object)null)
		{
			return false;
		}
		try
		{
			Traverse val = Traverse.Create((object)a);
			return !val.Field<bool>("deadSet").Value && !val.Field<bool>("isDisabled").Value;
		}
		catch
		{
			return true;
		}
	}

	internal static PlayerAvatar Local()
	{
		try
		{
			return SemiFunc.PlayerAvatarLocal();
		}
		catch
		{
			return null;
		}
	}

	internal static bool InLevel()
	{
		try
		{
			return SemiFunc.RunIsLevel();
		}
		catch
		{
			return false;
		}
	}

	internal static bool TeammateNear(PlayerAvatar me, float radius)
	{
		//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_0060: 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_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)me == (Object)null)
		{
			return false;
		}
		Vector3 position = ((Component)me).transform.position;
		foreach (PlayerAvatar item in Plugin.Players())
		{
			if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)me) && IsAlive(item))
			{
				Vector3 val = ((Component)item).transform.position - position;
				if (((Vector3)(ref val)).sqrMagnitude <= radius * radius)
				{
					return true;
				}
			}
		}
		return false;
	}

	internal static PlayerAvatar NearestAlive(Vector3 pos, PlayerAvatar exclude, float maxDist)
	{
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		PlayerAvatar result = null;
		float num = maxDist * maxDist;
		foreach (PlayerAvatar item in Plugin.Players())
		{
			if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)exclude) && IsAlive(item))
			{
				Vector3 val = ((Component)item).transform.position - pos;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				if (sqrMagnitude < num)
				{
					num = sqrMagnitude;
					result = item;
				}
			}
		}
		return result;
	}

	internal static void BigMessage(string text, string emoji, Color color)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			SemiFunc.UIBigMessage(text, emoji, 25f, color, Color.white);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("BigMessage failed: " + ex.Message));
		}
	}

	internal static void FocusText(string text, Color color, float time)
	{
		//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)
		try
		{
			SemiFunc.UIFocusText(text, color, Color.white, time);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("FocusText failed: " + ex.Message));
		}
	}

	internal static void RefillLocalStamina()
	{
		try
		{
			PlayerController instance = PlayerController.instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.EnergyCurrent = instance.EnergyStart;
			}
		}
		catch
		{
		}
	}
}
internal class TeamEvents : IOnEventCallback
{
	internal const byte EventCode = 92;

	internal const byte KindMilestone = 1;

	internal const byte KindClutch = 2;

	internal const byte KindPing = 3;

	private static TeamEvents instance;

	internal static void Register()
	{
		if (instance == null)
		{
			instance = new TeamEvents();
			PhotonNetwork.AddCallbackTarget((object)instance);
		}
	}

	internal static void Broadcast(byte kind, string text, Vector3 pos)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Expected O, but got Unknown
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		Handle(kind, text, pos);
		try
		{
			if (PhotonNetwork.InRoom)
			{
				RaiseEventOptions val = new RaiseEventOptions();
				val.Receivers = (ReceiverGroup)0;
				RaiseEventOptions val2 = val;
				PhotonNetwork.RaiseEvent((byte)92, (object)new object[3] { kind, text, pos }, val2, SendOptions.SendReliable);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Team event send failed: " + ex.Message));
		}
	}

	public void OnEvent(EventData photonEvent)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		if (photonEvent.Code != 92)
		{
			return;
		}
		try
		{
			if (photonEvent.CustomData is object[] array && array.Length >= 3)
			{
				Handle((byte)array[0], array[1] as string, (Vector3)array[2]);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Team event receive failed: " + ex.Message));
		}
	}

	private static void Handle(byte kind, string text, Vector3 pos)
	{
		//IL_0020: 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_0066: Unknown result type (might be due to invalid IL or missing references)
		switch (kind)
		{
		case 1:
			Team.BigMessage(text, "{heart}", Team.Mint);
			break;
		case 2:
		{
			Team.BigMessage(text, "{fire}", Team.Gold);
			PlayerAvatar val = Team.Local();
			if ((Object)(object)val != (Object)null && Team.IsAlive(val))
			{
				Team.RefillLocalStamina();
			}
			break;
		}
		case 3:
			PingMarker.Show(pos, text);
			break;
		}
	}
}
[HarmonyPatch(typeof(PhysGrabObject), "FixedUpdate")]
internal static class ManyHandsPatch
{
	[HarmonyPostfix]
	private static void Postfix(PhysGrabObject __instance)
	{
		try
		{
			if (Plugin.IsHost() && __instance.playerGrabbing != null)
			{
				int count = __instance.playerGrabbing.Count;
				if (count >= 2)
				{
					float num = 1f + TeamConfig.ManyHandsStrengthPerHelper.Value * (float)(count - 1);
					__instance.OverrideGrabStrength(num, 0.1f);
					__instance.OverrideFragility(TeamConfig.ManyHandsFragility.Value);
				}
			}
		}
		catch
		{
		}
	}
}
[HarmonyPatch]
internal static class SafetyPatches
{
	internal static bool nearTeammate;

	private static float checkTimer;

	[HarmonyPatch(typeof(PlayerController), "Update")]
	[HarmonyPostfix]
	private static void ControllerUpdate(PlayerController __instance)
	{
		try
		{
			if (!Team.InLevel())
			{
				nearTeammate = false;
				return;
			}
			checkTimer -= Time.deltaTime;
			if (checkTimer <= 0f)
			{
				checkTimer = 0.5f;
				nearTeammate = Team.TeammateNear(Team.Local(), TeamConfig.SafetyRadius.Value);
			}
			if (nearTeammate && __instance.EnergyCurrent < __instance.EnergyStart)
			{
				__instance.EnergyCurrent = Mathf.Min(__instance.EnergyStart, __instance.EnergyCurrent + TeamConfig.SafetyStaminaRegen.Value * Time.deltaTime);
			}
		}
		catch
		{
		}
	}

	[HarmonyPatch(typeof(PlayerHealth), "Hurt", new Type[]
	{
		typeof(int),
		typeof(bool),
		typeof(int),
		typeof(bool)
	})]
	[HarmonyPrefix]
	private static void HurtPrefix(PlayerHealth __instance, ref int damage)
	{
		try
		{
			if (nearTeammate && damage > 0)
			{
				PlayerAvatar val = Team.Local();
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.playerHealth != (Object)(object)__instance))
				{
					int num = Mathf.Max(1, Mathf.RoundToInt((float)damage * (1f - TeamConfig.SafetyDamageReduction.Value)));
					damage = num;
				}
			}
		}
		catch
		{
		}
	}
}
[HarmonyPatch]
internal static class RescuePatches
{
	private static bool boosting;

	private static float savedSprint;

	[HarmonyPatch(typeof(PlayerController), "Update")]
	[HarmonyPostfix]
	private static void CarryBoost(PlayerController __instance)
	{
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			bool flag = false;
			PlayerAvatar val = Team.Local();
			if ((Object)(object)val != (Object)null && (Object)(object)val.physGrabber != (Object)null && val.physGrabber.grabbed)
			{
				PhysGrabObject value = Traverse.Create((object)val.physGrabber).Field<PhysGrabObject>("grabbedPhysGrabObject").Value;
				flag = (Object)(object)value != (Object)null && (Object)(object)((Component)value).GetComponent<PlayerDeathHead>() != (Object)null;
			}
			if (flag && !boosting)
			{
				boosting = true;
				savedSprint = __instance.SprintSpeed;
				__instance.SprintSpeed = savedSprint * TeamConfig.HeadCarrySpeed.Value;
				Team.FocusText("Carrying a friend: speed boost!", Team.Mint, 2f);
			}
			else if (!flag && boosting)
			{
				boosting = false;
				__instance.SprintSpeed = savedSprint;
			}
		}
		catch
		{
		}
	}

	[HarmonyPatch(typeof(PlayerAvatar), "ReviveRPC")]
	[HarmonyPostfix]
	private static void ReviveHeal(PlayerAvatar __instance, bool _revivedByTruck)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (Plugin.IsHost() && !_revivedByTruck && TeamConfig.ReviveHeal.Value > 0)
			{
				if ((Object)(object)__instance.playerHealth != (Object)null)
				{
					__instance.playerHealth.HealOther(TeamConfig.ReviveHeal.Value, true);
				}
				PlayerAvatar val = Team.NearestAlive(((Component)__instance).transform.position, __instance, 8f);
				if ((Object)(object)val != (Object)null && (Object)(object)val.playerHealth != (Object)null)
				{
					val.playerHealth.HealOther(TeamConfig.ReviveHeal.Value, true);
				}
				Plugin.Log.LogInfo((object)("Rescue reward: healed revived player" + ((!((Object)(object)val != (Object)null)) ? string.Empty : " and rescuer") + "."));
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Revive heal failed: " + ex.Message));
		}
	}
}
[HarmonyPatch]
internal static class RoundPatches
{
	private static readonly int[] Thresholds = new int[4] { 25, 50, 75, 100 };

	private static readonly string[] Messages = new string[4] { "Quarter of the way there, team!", "Halfway! Keep it up!", "Almost there!! 75%!", "GOAL REACHED! Everybody back to the truck!" };

	private static int nextMilestone;

	private static bool clutchFired;

	private static float pollTimer;

	[HarmonyPatch(typeof(LevelGenerator), "GenerateDone")]
	[HarmonyPostfix]
	private static void ResetForLevel()
	{
		nextMilestone = 0;
		clutchFired = false;
	}

	[HarmonyPatch(typeof(RoundDirector), "Update")]
	[HarmonyPostfix]
	private static void Poll(RoundDirector __instance)
	{
		//IL_0291: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!Plugin.IsHost() || !Team.InLevel())
			{
				return;
			}
			pollTimer -= Time.deltaTime;
			if (pollTimer > 0f)
			{
				return;
			}
			pollTimer = 0.5f;
			if (TeamConfig.TeamMilestones.Value && nextMilestone < Thresholds.Length)
			{
				Traverse val = Traverse.Create((object)__instance);
				int value = val.Field<int>("currentHaul").Value;
				int value2 = val.Field<int>("haulGoal").Value;
				if (value2 > 0)
				{
					int num = Mathf.FloorToInt(100f * (float)value / (float)value2);
					while (nextMilestone < Thresholds.Length && num >= Thresholds[nextMilestone])
					{
						string text = Messages[nextMilestone];
						nextMilestone++;
						TeamEvents.Broadcast(1, text, Vector3.zero);
						if (TeamConfig.MilestoneHeal.Value > 0)
						{
							foreach (PlayerAvatar item in Plugin.Players())
							{
								if ((Object)(object)item != (Object)null && (Object)(object)item.playerHealth != (Object)null && Team.IsAlive(item))
								{
									item.playerHealth.HealOther(TeamConfig.MilestoneHeal.Value, true);
								}
							}
						}
						Plugin.Log.LogInfo((object)("Team milestone: " + text));
					}
				}
			}
			if (!TeamConfig.ClutchMode.Value || clutchFired)
			{
				return;
			}
			List<PlayerAvatar> list = Plugin.Players();
			if (list.Count < 2)
			{
				return;
			}
			PlayerAvatar val2 = null;
			int num2 = 0;
			foreach (PlayerAvatar item2 in list)
			{
				if (Team.IsAlive(item2))
				{
					num2++;
					val2 = item2;
				}
			}
			if (num2 == 1 && (Object)(object)val2 != (Object)null)
			{
				clutchFired = true;
				if (TeamConfig.ClutchHeal.Value > 0 && (Object)(object)val2.playerHealth != (Object)null)
				{
					val2.playerHealth.HealOther(TeamConfig.ClutchHeal.Value, true);
				}
				string text2 = "Someone";
				try
				{
					text2 = SemiFunc.PlayerGetName(val2);
				}
				catch
				{
				}
				TeamEvents.Broadcast(2, text2 + " is the last one standing! Everyone's counting on you!", Vector3.zero);
				Plugin.Log.LogInfo((object)("Clutch mode for " + text2));
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Round poll failed: " + ex.Message));
		}
	}
}
internal class PingInput : MonoBehaviour
{
	private float cooldown;

	private static bool warned;

	private static bool PingPressed()
	{
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		string text = TeamConfig.PingKey.Value ?? string.Empty;
		try
		{
			Mouse current = Mouse.current;
			if (current != null)
			{
				switch (text.ToLowerInvariant())
				{
				case "middlebutton":
				case "mouse2":
					return current.middleButton.wasPressedThisFrame;
				case "leftbutton":
				case "mouse0":
					return current.leftButton.wasPressedThisFrame;
				case "rightbutton":
				case "mouse1":
					return current.rightButton.wasPressedThisFrame;
				case "forwardbutton":
				case "mouse4":
					return current.forwardButton.wasPressedThisFrame;
				case "backbutton":
				case "mouse3":
					return current.backButton.wasPressedThisFrame;
				}
			}
			Keyboard current2 = Keyboard.current;
			if (current2 == null)
			{
				return false;
			}
			if (!Enum.TryParse<Key>(text, ignoreCase: true, out Key result))
			{
				if (!warned)
				{
					warned = true;
					Plugin.Log.LogWarning((object)("Unknown ping key '" + text + "', using T."));
				}
				result = (Key)34;
			}
			return ((ButtonControl)current2[result]).wasPressedThisFrame;
		}
		catch (Exception ex)
		{
			if (!warned)
			{
				warned = true;
				Plugin.Log.LogWarning((object)("Ping input unavailable: " + ex.Message));
			}
			return false;
		}
	}

	private void Update()
	{
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: 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_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		cooldown -= Time.deltaTime;
		if (cooldown > 0f || !PingPressed())
		{
			return;
		}
		try
		{
			if (!SemiFunc.NoTextInputsActive())
			{
				return;
			}
			PlayerAvatar val = Team.Local();
			Camera main = Camera.main;
			if (!((Object)(object)val == (Object)null) && !((Object)(object)main == (Object)null) && Team.IsAlive(val))
			{
				Ray val2 = default(Ray);
				((Ray)(ref val2))..ctor(((Component)main).transform.position, ((Component)main).transform.forward);
				Vector3 pos = ((Ray)(ref val2)).origin + ((Ray)(ref val2)).direction * 25f;
				RaycastHit val3 = default(RaycastHit);
				if (Physics.Raycast(val2, ref val3, 60f, -1, (QueryTriggerInteraction)1))
				{
					pos = ((RaycastHit)(ref val3)).point;
				}
				string text = "Someone";
				try
				{
					text = SemiFunc.PlayerGetName(val);
				}
				catch
				{
				}
				cooldown = 1f;
				TeamEvents.Broadcast(3, text, pos);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Ping failed: " + ex.Message));
		}
	}
}
internal static class PingMarker
{
	internal static void Show(Vector3 pos, string who)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: 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_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Expected O, but got Unknown
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			GameObject val = GameObject.CreatePrimitive((PrimitiveType)0);
			((Object)val).name = "TeamPing";
			Object.Destroy((Object)(object)val.GetComponent<Collider>());
			val.transform.position = pos + Vector3.up * 0.3f;
			val.transform.localScale = Vector3.one * 0.35f;
			Renderer component = val.GetComponent<Renderer>();
			Shader val2 = Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Unlit/Color") ?? Shader.Find("Standard");
			if ((Object)(object)val2 != (Object)null)
			{
				component.material = new Material(val2);
				component.material.color = Team.Gold;
			}
			Light val3 = val.AddComponent<Light>();
			val3.type = (LightType)2;
			val3.color = Team.Gold;
			val3.range = 4f;
			val3.intensity = 3f;
			val.AddComponent<PingLife>().life = TeamConfig.PingDuration.Value;
			Team.FocusText(who + " pinged something!", Team.Gold, 2.5f);
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Ping marker failed: " + ex.Message));
		}
	}
}
internal class PingLife : MonoBehaviour
{
	internal float life = 6f;

	private float t;

	private Vector3 baseScale;

	private void Start()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		baseScale = ((Component)this).transform.localScale;
	}

	private void Update()
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		t += Time.deltaTime;
		float num = 1f + 0.35f * Mathf.Sin(t * 6f);
		((Component)this).transform.localScale = baseScale * num;
		Transform transform = ((Component)this).transform;
		transform.position += Vector3.up * Mathf.Sin(t * 3f) * 0.002f;
		if (t >= life)
		{
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}
	}
}