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));
}
}
}