Decompiled source of Free For All v1.1.1

FreeForAll.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Lua;
using MoonSharp.Interpreter;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace FreeForAllMod;

public static class FreeForAll
{
	private delegate bool CanSeeCheck(Actor a, Actor b, ref float distance);

	private delegate void UpdateSightCall(ActorManager self, Actor source, Actor target, float distance, bool hasLineOfSight);

	private delegate bool HeardCall(ActorManager self, Actor actor);

	private const float CARRIER_PASS_EVERY = 0.25f;

	private const float CARRIER_CLOSE = 30f;

	private const float CARRIER_FIRST_RANGE = 60f;

	private const float CARRIER_CLOSER_THREAT = 15f;

	public static bool On;

	public static bool Board = true;

	public static bool Patched;

	private static readonly bool[][,] sees = new bool[2][,];

	private static readonly int[] nextI;

	private static readonly int[] nextJ;

	private static CanSeeCheck canSee;

	private static UpdateSightCall updateSight;

	private static HeardCall canBeHeard;

	private static FieldInfo actorsOnTeamField;

	private static FieldInfo teamCountField;

	private static FieldInfo perFrameField;

	private static bool ready;

	private static bool failed;

	private static int errors;

	private static float nextErrorLog;

	public static bool CarrierFirst;

	public static Actor Carrier;

	private static float nextCarrierPass;

	private static MethodInfo setTarget;

	private static bool setTargetMissing;

	private static string customBoard;

	private static readonly Dictionary<int, int> kills;

	private static readonly Dictionary<int, string> names;

	private static string boardText;

	private static float nextBoard;

	private static GUIStyle style;

	public static bool SetOn(bool on, bool board, out string why)
	{
		why = "";
		if (on && (!Patched || !Init()))
		{
			why = "the Free For All plugin could not hook into this version of the game (details in the BepInEx log)";
			on = false;
		}
		if (on != On)
		{
			Plugin.Log.LogInfo((object)("[Free for all] " + (on ? "On: everyone fights everyone." : "Off.")));
		}
		else if (!on && why.Length > 0)
		{
			Plugin.Log.LogWarning((object)("[Free for all] Asked to switch on, but " + why + "."));
		}
		On = on;
		Board = board;
		for (int i = 0; i < 2; i++)
		{
			nextI[i] = 0;
			nextJ[i] = 1;
			if (sees[i] != null)
			{
				Array.Clear(sees[i], 0, sees[i].Length);
			}
		}
		kills.Clear();
		names.Clear();
		boardText = null;
		customBoard = null;
		Carrier = null;
		return On;
	}

	public static DynValue LuaSwitch(ScriptExecutionContext ctx, CallbackArguments args)
	{
		DynValue val = ((args.Count > 0) ? args[0] : null);
		DynValue val2 = ((args.Count > 1) ? args[1] : null);
		bool flag = val != null && val.CastToBool();
		bool board = val2 == null || val2.IsNil() || val2.CastToBool();
		string why;
		bool flag2 = SetOn(flag, board, out why);
		return DynValue.NewTuple((DynValue[])(object)new DynValue[2]
		{
			DynValue.NewBoolean(flag2),
			DynValue.NewString(why)
		});
	}

	public static DynValue LuaSetBoard(ScriptExecutionContext ctx, CallbackArguments args)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Invalid comparison between Unknown and I4
		DynValue val = ((args.Count > 0) ? args[0] : null);
		string text = ((val != null && (int)val.Type == 4) ? val.String : null);
		customBoard = (string.IsNullOrEmpty(text) ? null : text);
		boardText = null;
		return DynValue.Nil;
	}

	public static DynValue LuaSetCarrier(ScriptExecutionContext ctx, CallbackArguments args)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Invalid comparison between Unknown and I4
		DynValue val = ((args.Count > 0) ? args[0] : null);
		int num = ((val != null && (int)val.Type == 3) ? ((int)val.Number) : (-1));
		Carrier = null;
		if (num >= 0)
		{
			try
			{
				ActorManager instance = ActorManager.instance;
				if ((Object)(object)instance != (Object)null && instance.actors != null)
				{
					foreach (Actor actor in instance.actors)
					{
						if (actor != null && actor.actorIndex == num)
						{
							Carrier = actor;
							break;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[Bag Tag] Could not find the bag carrier (" + num + "): " + ex.Message));
			}
		}
		nextCarrierPass = 0f;
		return DynValue.NewBoolean(CarrierFirst && Carrier != null);
	}

	public static void MatchOver()
	{
		if (On)
		{
			SetOn(on: false, Board, out var _);
		}
	}

	private static bool Init()
	{
		if (ready)
		{
			return true;
		}
		if (failed)
		{
			return false;
		}
		try
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(ActorManager), "DoCanSeeCheck", new Type[3]
			{
				typeof(Actor),
				typeof(Actor),
				typeof(float).MakeByRefType()
			}, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ActorManager), "UpdateSight", new Type[4]
			{
				typeof(Actor),
				typeof(Actor),
				typeof(float),
				typeof(bool)
			}, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(typeof(ActorManager), "ActorCanBeHeard", new Type[1] { typeof(Actor) }, (Type[])null);
			actorsOnTeamField = AccessTools.Field(typeof(ActorManager), "actorsOnTeam");
			teamCountField = AccessTools.Field(typeof(ActorManager), "nextActorIndexTeam");
			perFrameField = AccessTools.Field(typeof(ActorManager), "interactionUpdatesPerFrame");
			string text = ((methodInfo == null) ? "DoCanSeeCheck " : "") + ((methodInfo2 == null) ? "UpdateSight " : "") + ((methodInfo3 == null) ? "ActorCanBeHeard " : "") + ((actorsOnTeamField == null) ? "actorsOnTeam " : "") + ((teamCountField == null) ? "nextActorIndexTeam " : "") + ((perFrameField == null) ? "interactionUpdatesPerFrame" : "");
			if (text.Length > 0)
			{
				throw new Exception("not found in the game: " + text);
			}
			canSee = (CanSeeCheck)Delegate.CreateDelegate(typeof(CanSeeCheck), methodInfo);
			updateSight = (UpdateSightCall)Delegate.CreateDelegate(typeof(UpdateSightCall), methodInfo2);
			canBeHeard = (HeardCall)Delegate.CreateDelegate(typeof(HeardCall), methodInfo3);
			ready = true;
		}
		catch (Exception ex)
		{
			failed = true;
			Plugin.Log.LogError((object)("[Free for all] Could not reach the game's sight steps, so free for all stays off: " + ex));
		}
		return ready;
	}

	public static bool SameTeam(int a, int b)
	{
		if (a == b)
		{
			return !On;
		}
		return false;
	}

	public static bool SameTeamSees(Actor a, Actor b)
	{
		int team = ((Hurtable)a).team;
		if (team < 0 || team > 1)
		{
			return false;
		}
		bool[,] array = sees[team];
		int teamActorIndex = a.teamActorIndex;
		int teamActorIndex2 = b.teamActorIndex;
		if (array == null || teamActorIndex < 0 || teamActorIndex2 < 0 || teamActorIndex >= array.GetLength(0) || teamActorIndex2 >= array.GetLength(1))
		{
			return false;
		}
		return array[teamActorIndex, teamActorIndex2];
	}

	private static void Grow(int t, int size)
	{
		bool[,] array = sees[t];
		if (array != null && array.GetLength(0) >= size)
		{
			return;
		}
		int num = Math.Max(64, size + 16);
		bool[,] array2 = new bool[num, num];
		if (array != null)
		{
			for (int i = 0; i < array.GetLength(0); i++)
			{
				for (int j = 0; j < array.GetLength(1); j++)
				{
					array2[i, j] = array[i, j];
				}
			}
		}
		sees[t] = array2;
	}

	private static long Tri(int n)
	{
		if (n >= 2)
		{
			return (long)n * (long)(n - 1) / 2;
		}
		return 0L;
	}

	public static void SameTeamPass(ActorManager am)
	{
		if (!On || (Object)(object)am == (Object)null || !ready)
		{
			return;
		}
		List<Actor>[] array = actorsOnTeamField.GetValue(am) as List<Actor>[];
		int[] array2 = teamCountField.GetValue(am) as int[];
		if (array != null && array2 != null && array.Length >= 2 && array2.Length >= 2 && array[0] != null && array[1] != null)
		{
			int num = Math.Min(array[0].Count, array2[0]);
			int num2 = Math.Min(array[1].Count, array2[1]);
			int num3 = Math.Max(1, (int)perFrameField.GetValue(am));
			long num4 = (long)num * (long)num2;
			long num5 = Tri(num);
			long num6 = Tri(num2);
			long num7 = num5 + num6;
			if (num7 != 0)
			{
				double num8 = ((num4 > 0) ? ((double)num7 / (double)num4) : 1.0);
				int num9 = (int)Math.Min(400.0, Math.Max(1.0, Math.Ceiling((double)num3 * num8)));
				int num10 = ((num5 > 0) ? Math.Max(1, (int)Math.Round((double)num9 * (double)num5 / (double)num7)) : 0);
				int budget = ((num6 > 0) ? Math.Max(1, num9 - num10) : 0);
				Step(am, 0, array[0], num, num10);
				Step(am, 1, array[1], num2, budget);
			}
		}
	}

	private static void Step(ActorManager am, int t, List<Actor> list, int n, int budget)
	{
		if (n < 2 || budget <= 0)
		{
			return;
		}
		Grow(t, n);
		int num = nextI[t];
		int num2 = nextJ[t];
		if (num < 0 || num2 <= num || num2 >= n)
		{
			num = 0;
			num2 = 1;
		}
		long num3 = Tri(n);
		int num4 = 0;
		for (long num5 = 0L; num5 < num3; num5++)
		{
			if (num4 >= budget)
			{
				break;
			}
			try
			{
				if (Pair(am, t, list[num], list[num2]))
				{
					num4++;
				}
			}
			catch (Exception ex)
			{
				errors++;
				if (Time.unscaledTime >= nextErrorLog)
				{
					nextErrorLog = Time.unscaledTime + 60f;
					Plugin.Log.LogWarning((object)("[Free for all] Skipped a pair of soldiers after an error (" + errors + " since the last message): " + ex));
					errors = 0;
				}
			}
			num2++;
			if (num2 >= n)
			{
				num++;
				num2 = num + 1;
				if (num2 >= n)
				{
					num = 0;
					num2 = 1;
				}
			}
		}
		nextI[t] = num;
		nextJ[t] = num2;
	}

	private static bool SameVehicle(Actor a, Actor b)
	{
		Seat seat = a.seat;
		Seat seat2 = b.seat;
		if (seat == null || seat2 == null)
		{
			return false;
		}
		if ((Object)(object)seat.vehicle != (Object)null)
		{
			return (Object)(object)seat.vehicle == (Object)(object)seat2.vehicle;
		}
		return false;
	}

	private static bool Pair(ActorManager am, int t, Actor a, Actor b)
	{
		if (a == null || b == null)
		{
			return false;
		}
		int teamActorIndex = a.teamActorIndex;
		int teamActorIndex2 = b.teamActorIndex;
		if (teamActorIndex < 0 || teamActorIndex2 < 0 || teamActorIndex == teamActorIndex2)
		{
			return false;
		}
		Grow(t, Math.Max(teamActorIndex, teamActorIndex2) + 1);
		bool flag = false;
		bool result = false;
		float distance = 0f;
		if (!a.dead && !b.dead && !SameVehicle(a, b))
		{
			result = true;
			flag = canSee(a, b, ref distance);
			if (distance <= 50f)
			{
				bool[] actorCanHearEnemy = am.actorCanHearEnemy;
				if (actorCanHearEnemy != null)
				{
					if (b.actorIndex >= 0 && b.actorIndex < actorCanHearEnemy.Length && canBeHeard(am, a))
					{
						actorCanHearEnemy[b.actorIndex] = true;
					}
					if (a.actorIndex >= 0 && a.actorIndex < actorCanHearEnemy.Length && canBeHeard(am, b))
					{
						actorCanHearEnemy[a.actorIndex] = true;
					}
				}
			}
			if (a.closestEnemyDistancePreShift > distance)
			{
				a.closestEnemyDistancePreShift = distance;
			}
			if (b.closestEnemyDistancePreShift > distance)
			{
				b.closestEnemyDistancePreShift = distance;
			}
		}
		bool[,] array = sees[t];
		array[teamActorIndex, teamActorIndex2] = flag;
		array[teamActorIndex2, teamActorIndex] = flag;
		updateSight(am, a, b, distance, flag);
		updateSight(am, b, a, distance, flag);
		return result;
	}

	public static bool SeesCarrier(AiActorController ai, Actor carrier)
	{
		//IL_0036: 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)
		if (ai == null || carrier == null || carrier.dead)
		{
			return false;
		}
		Actor actor = ((ActorController)ai).actor;
		if (actor == null || (Object)(object)actor == (Object)(object)carrier || actor.dead || actor.IsSeated())
		{
			return false;
		}
		float num = Vector3.Distance(actor.Position(), carrier.Position());
		if (num > 60f)
		{
			return false;
		}
		if (!ActorManager.ActorsCanSeeEachOther(actor, carrier))
		{
			return false;
		}
		if (num <= 30f)
		{
			return true;
		}
		return ai.CanSeeActorFOV(carrier);
	}

	public static bool OtherComesFirst(Actor bot, Actor other, Actor carrier)
	{
		//IL_0023: 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_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_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		if (bot == null || other == null || (Object)(object)other == (Object)(object)carrier || (Object)(object)other == (Object)(object)bot || other.dead)
		{
			return false;
		}
		Vector3 val = bot.Position();
		return Vector3.Distance(val, other.Position()) + 15f < Vector3.Distance(val, carrier.Position());
	}

	public static void CarrierPass()
	{
		if (!On || !CarrierFirst || Carrier == null || Time.time < nextCarrierPass)
		{
			return;
		}
		nextCarrierPass = Time.time + 0.25f;
		Actor carrier = Carrier;
		if (carrier.dead)
		{
			return;
		}
		if (setTarget == null)
		{
			if (setTargetMissing)
			{
				return;
			}
			setTarget = AccessTools.Method(typeof(AiActorController), "SetTarget", new Type[1] { typeof(Actor) }, (Type[])null);
			if (setTarget == null)
			{
				setTargetMissing = true;
				Plugin.Log.LogWarning((object)"[Bag Tag] The bots' SetTarget was not found in the game, so bots do not switch to the carrier.");
				return;
			}
		}
		ActorManager instance = ActorManager.instance;
		if ((Object)(object)instance == (Object)null || instance.actors == null)
		{
			return;
		}
		foreach (Actor actor in instance.actors)
		{
			try
			{
				if (actor != null && !actor.dead && actor.aiControlled && !((Object)(object)actor == (Object)(object)carrier))
				{
					ActorController controller = actor.controller;
					AiActorController val = (AiActorController)(object)((controller is AiActorController) ? controller : null);
					if (val != null && !((Object)(object)val.target == (Object)(object)carrier) && SeesCarrier(val, carrier) && !OtherComesFirst(actor, val.target, carrier))
					{
						setTarget.Invoke(val, new object[1] { carrier });
					}
				}
			}
			catch (Exception ex)
			{
				errors++;
				if (Time.unscaledTime >= nextErrorLog)
				{
					nextErrorLog = Time.unscaledTime + 60f;
					Plugin.Log.LogWarning((object)("[Bag Tag] Skipped a bot after an error (" + errors + " since the last message): " + ex));
					errors = 0;
				}
			}
		}
	}

	public static void CountKill(Actor killer, Actor victim)
	{
		if (killer != null && victim != null && !((Object)(object)killer == (Object)(object)victim))
		{
			int actorIndex = killer.actorIndex;
			kills.TryGetValue(actorIndex, out var value);
			kills[actorIndex] = value + 1;
			string text = null;
			try
			{
				text = killer.name;
			}
			catch (Exception)
			{
			}
			names[actorIndex] = (string.IsNullOrEmpty(text) ? ("Soldier " + actorIndex) : text);
		}
	}

	public static void OnGUI()
	{
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Expected O, but got Unknown
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: 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_0060: Expected O, but got Unknown
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Expected O, but got Unknown
		if (On && Board)
		{
			if (boardText == null || Time.unscaledTime >= nextBoard)
			{
				nextBoard = Time.unscaledTime + 0.5f;
				boardText = customBoard ?? BoardText();
			}
			if (style == null)
			{
				style = new GUIStyle(GUI.skin.box);
				style.alignment = (TextAnchor)0;
				style.fontSize = 14;
				style.padding = new RectOffset(10, 10, 6, 6);
			}
			Vector2 val = style.CalcSize(new GUIContent(boardText));
			GUI.Box(new Rect((float)Screen.width - val.x - 12f, 70f, val.x, val.y), boardText, style);
		}
	}

	private static string BoardText()
	{
		List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>(kills);
		list.Sort((KeyValuePair<int, int> x, KeyValuePair<int, int> y) => y.Value.CompareTo(x.Value));
		int num = -1;
		try
		{
			if ((Object)(object)LocalPlayer.actor != (Object)null)
			{
				num = LocalPlayer.actor.actorIndex;
			}
		}
		catch (Exception)
		{
		}
		StringBuilder stringBuilder = new StringBuilder("FREE FOR ALL: KILLS\n");
		int num2 = 0;
		for (int num3 = 0; num3 < list.Count; num3++)
		{
			if (num2 >= 5)
			{
				break;
			}
			names.TryGetValue(list[num3].Key, out var value);
			stringBuilder.Append(num3 + 1).Append(". ").Append((list[num3].Key == num) ? "YOU" : value)
				.Append("   ")
				.Append(list[num3].Value)
				.Append('\n');
			num2++;
		}
		if (num2 == 0)
		{
			stringBuilder.Append("nobody yet\n");
		}
		kills.TryGetValue(num, out var value2);
		int num4 = 1;
		for (int num5 = 0; num5 < list.Count; num5++)
		{
			if (list[num5].Value > value2)
			{
				num4++;
			}
		}
		stringBuilder.Append("You: ").Append(value2).Append((value2 == 1) ? " kill" : " kills");
		if (value2 > 0)
		{
			stringBuilder.Append(" (").Append(num4).Append(Ordinal(num4))
				.Append(" place)");
		}
		return stringBuilder.ToString();
	}

	private static string Ordinal(int n)
	{
		int num = n % 100;
		if (num >= 11 && num <= 13)
		{
			return "th";
		}
		return (n % 10) switch
		{
			1 => "st", 
			2 => "nd", 
			3 => "rd", 
			_ => "th", 
		};
	}

	static FreeForAll()
	{
		int[] array = new int[2];
		nextI = array;
		nextJ = new int[2] { 1, 1 };
		kills = new Dictionary<int, int>();
		names = new Dictionary<int, string>();
	}
}
internal static class TeamCompare
{
	private static readonly MethodInfo sameTeam = AccessTools.Method(typeof(FreeForAll), "SameTeam", (Type[])null, (Type[])null);

	private static bool LoadsTeam(CodeInstruction c)
	{
		if (c.opcode != OpCodes.Ldfld)
		{
			return false;
		}
		FieldInfo fieldInfo = c.operand as FieldInfo;
		if (fieldInfo != null && fieldInfo.Name == "team")
		{
			return fieldInfo.DeclaringType == typeof(Hurtable);
		}
		return false;
	}

	public static List<CodeInstruction> Replace(IEnumerable<CodeInstruction> instructions, string kind, int expected, string what, out bool done)
	{
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Expected O, but got Unknown
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Expected O, but got Unknown
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		List<CodeInstruction> list2 = new List<CodeInstruction>(list);
		int num = 0;
		for (int i = 1; i < list2.Count; i++)
		{
			if (!LoadsTeam(list2[i - 1]))
			{
				continue;
			}
			CodeInstruction val = list2[i];
			OpCode opcode = val.opcode;
			OpCode opCode;
			if (kind == "beq" && (opcode == OpCodes.Beq || opcode == OpCodes.Beq_S))
			{
				opCode = OpCodes.Brtrue;
			}
			else if (kind == "bne" && (opcode == OpCodes.Bne_Un || opcode == OpCodes.Bne_Un_S))
			{
				opCode = OpCodes.Brfalse;
			}
			else
			{
				if (!(kind == "ceq") || !(opcode == OpCodes.Ceq))
				{
					continue;
				}
				opCode = OpCodes.Nop;
			}
			CodeInstruction val2 = new CodeInstruction(OpCodes.Call, (object)sameTeam);
			val2.labels.AddRange(val.labels);
			val2.blocks.AddRange(val.blocks);
			list2[i] = val2;
			if (opCode != OpCodes.Nop)
			{
				list2.Insert(i + 1, new CodeInstruction(opCode, val.operand));
			}
			num++;
		}
		done = num == expected;
		if (!done)
		{
			Plugin.Log.LogWarning((object)("[Free for all] " + what + ": expected " + expected + " team check(s) in the game's code but found " + num + ", so it is left as it is (free for all will not switch on)."));
			return list;
		}
		return list2;
	}
}
[HarmonyPatch(typeof(ActorManager), "UpdateActorInteractions")]
internal static class FfaSightPass
{
	[HarmonyPostfix]
	private static void Postfix(ActorManager __instance)
	{
		if (FreeForAll.On)
		{
			FreeForAll.SameTeamPass(__instance);
		}
	}
}
[HarmonyPatch(typeof(ActorManager), "ActorsCanSeeEachOther")]
internal static class FfaSeesEachOther
{
	[HarmonyPrefix]
	private static bool Prefix(Actor a, Actor b, ref bool __result)
	{
		if (!FreeForAll.On || a == null || b == null || ((Hurtable)a).team != ((Hurtable)b).team)
		{
			return true;
		}
		__result = (Object)(object)a != (Object)(object)b && FreeForAll.SameTeamSees(a, b);
		return false;
	}
}
[HarmonyPatch(typeof(AiActorController), "SetTarget")]
internal static class FfaSetTarget
{
	public static bool Found;

	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		return TeamCompare.Replace(instructions, "beq", 1, "Bots taking a target", out Found);
	}
}
[HarmonyPatch]
internal static class FfaHoldFire
{
	public static bool Found;

	private static MethodBase TargetMethod()
	{
		Type[] nestedTypes = typeof(AiActorController).GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic);
		foreach (Type type in nestedTypes)
		{
			if (type.Name.StartsWith("<AiWeapon>d__", StringComparison.Ordinal))
			{
				return AccessTools.Method(type, "MoveNext", (Type[])null, (Type[])null);
			}
		}
		Plugin.Log.LogWarning((object)"[Free for all] The bots' weapon routine (AiWeapon) was not found in the game.");
		return null;
	}

	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		return TeamCompare.Replace(instructions, "ceq", 1, "Bots holding fire for their own team", out Found);
	}
}
[HarmonyPatch(typeof(Actor), "OnGotKill")]
internal static class FfaScoreboard
{
	public static bool Found;

	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		return TeamCompare.Replace(instructions, "bne", 1, "Scoreboard kills", out Found);
	}
}
[HarmonyPatch(typeof(Projectile), "CanHitHitbox")]
internal static class FfaBulletsHitEveryone
{
	[HarmonyPrefix]
	private static bool Prefix(Projectile __instance, Hitbox hitbox, ref bool __result)
	{
		if (!FreeForAll.On || hitbox == null)
		{
			return true;
		}
		__result = (Object)(object)hitbox.parent != (Object)(object)__instance.killCredit;
		return false;
	}
}
[HarmonyPatch(typeof(ActorManager), "Explode", new Type[]
{
	typeof(ExplosionInfo),
	typeof(bool)
})]
internal static class FfaFullExplosions
{
	[HarmonyPrefix]
	private static void Prefix(ref bool reduceFriendlyDamage)
	{
		if (FreeForAll.On)
		{
			reduceFriendlyDamage = false;
		}
	}
}
[HarmonyPatch(typeof(AiActorController), "ReceivedDamage")]
internal static class FfaHitMakesAlert
{
	[HarmonyPrefix]
	private static void Prefix(ref bool friendlyFire)
	{
		if (FreeForAll.On)
		{
			friendlyFire = false;
		}
	}
}
[HarmonyPatch(typeof(Actor), "Die")]
internal static class FfaKillCount
{
	[HarmonyPostfix]
	private static void Postfix(Actor __instance, DamageInfo info, bool isSilentKill)
	{
		if (!FreeForAll.On || isSilentKill)
		{
			return;
		}
		try
		{
			FreeForAll.CountKill(info.sourceActor, __instance);
		}
		catch (Exception)
		{
		}
	}
}
[HarmonyPatch(typeof(AiActorController), "SetTarget")]
internal static class FfaCarrierFirst
{
	[HarmonyPrefix]
	private static void Prefix(AiActorController __instance, ref Actor target)
	{
		if (!FreeForAll.On)
		{
			return;
		}
		Actor carrier = FreeForAll.Carrier;
		if (carrier == null || (Object)(object)target == (Object)(object)carrier)
		{
			return;
		}
		try
		{
			if (FreeForAll.SeesCarrier(__instance, carrier) && !FreeForAll.OtherComesFirst(((ActorController)__instance).actor, target, carrier))
			{
				target = carrier;
			}
		}
		catch (Exception)
		{
		}
	}
}
[HarmonyPatch(typeof(ActorManager), "OnExitGameScene")]
internal static class FfaMatchOver
{
	[HarmonyPostfix]
	private static void Postfix()
	{
		FreeForAll.MatchOver();
	}
}
[BepInPlugin("com.kvatchhero.freeforall", "Free For All", "1.1.1")]
public class Plugin : BaseUnityPlugin
{
	public const string GUID = "com.kvatchhero.freeforall";

	public const string NAME = "Free For All";

	public const string VERSION = "1.1.1";

	internal static ManualLogSource Log;

	private void Awake()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		Harmony harmony = new Harmony("com.kvatchhero.freeforall");
		FreeForAll.Patched = (Patch(harmony, typeof(FfaSightPass), "sight between soldiers of the same team") & Patch(harmony, typeof(FfaSeesEachOther), "can these two see each other") & Patch(harmony, typeof(FfaSetTarget), "bots take targets on their own team") & Patch(harmony, typeof(FfaHoldFire), "bots do not hold fire for their own team") & Patch(harmony, typeof(FfaScoreboard), "scoreboard kills") & Patch(harmony, typeof(FfaBulletsHitEveryone), "bullets hit everyone") & Patch(harmony, typeof(FfaFullExplosions), "full explosion damage") & Patch(harmony, typeof(FfaHitMakesAlert), "hits make bots alert") & Patch(harmony, typeof(FfaKillCount), "kill board") & Patch(harmony, typeof(FfaMatchOver), "off when a match ends")) && FfaSetTarget.Found && FfaHoldFire.Found && FfaScoreboard.Found;
		FreeForAll.CarrierFirst = Patch(harmony, typeof(FfaCarrierFirst), "bots aim at the bag carrier first");
		if (FreeForAll.Patched)
		{
			Log.LogInfo((object)"Free For All 1.1.1 loaded. Tick the Free For All mutator to play a match where everyone fights everyone.");
		}
		else
		{
			Log.LogWarning((object)"Free For All 1.1.1: not every part could hook into this version of the game, so free for all will not switch on.");
		}
	}

	private static bool Patch(Harmony harmony, Type type, string label)
	{
		try
		{
			harmony.CreateClassProcessor(type).Patch();
			Log.LogInfo((object)("Patched: " + label));
			return true;
		}
		catch (Exception ex)
		{
			Log.LogError((object)("Could not patch " + label + ": " + ex));
			return false;
		}
	}

	private void Update()
	{
		LuaBridge.Update();
		FreeForAll.CarrierPass();
	}

	private void OnGUI()
	{
		FreeForAll.OnGUI();
	}
}
internal static class LuaBridge
{
	public const int VERSION = 2;

	private static Script registeredIn;

	private static float nextCheck;

	private static int failures;

	private static readonly FieldInfo scriptField = AccessTools.Field(typeof(ScriptEngine), "script");

	public static void Update()
	{
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Expected O, but got Unknown
		if (Time.unscaledTime < nextCheck)
		{
			return;
		}
		nextCheck = Time.unscaledTime + 1f;
		if (failures >= 3)
		{
			return;
		}
		try
		{
			if ((Object)(object)RavenscriptManager.instance == (Object)null)
			{
				return;
			}
			ScriptEngine engine = RavenscriptManager.engine;
			if (!((Object)(object)engine == (Object)null) && !(scriptField == null))
			{
				object? value = scriptField.GetValue(engine);
				Script val = (Script)((value is Script) ? value : null);
				if (val != null && !object.ReferenceEquals(val, registeredIn))
				{
					Table val2 = new Table(val);
					val2.Set("version", DynValue.NewNumber(2.0));
					val2.Set("Switch", DynValue.NewCallback((Func<ScriptExecutionContext, CallbackArguments, DynValue>)FreeForAll.LuaSwitch, "Switch"));
					val2.Set("SetBoard", DynValue.NewCallback((Func<ScriptExecutionContext, CallbackArguments, DynValue>)FreeForAll.LuaSetBoard, "SetBoard"));
					val2.Set("SetCarrier", DynValue.NewCallback((Func<ScriptExecutionContext, CallbackArguments, DynValue>)FreeForAll.LuaSetCarrier, "SetCarrier"));
					val.Globals.Set("FreeForAllPlugin", DynValue.NewTable(val2));
					registeredIn = val;
					Plugin.Log.LogInfo((object)("[Lua] Added the FreeForAllPlugin table to the game's Lua (free for all: " + (FreeForAll.Patched ? "ready" : "not available in this version of the game") + ")."));
				}
			}
		}
		catch (Exception ex)
		{
			failures++;
			Plugin.Log.LogError((object)("[Lua] Could not add the FreeForAllPlugin table to the game's Lua: " + ex));
		}
	}
}