Decompiled source of ArenaRaceMultiplayerSpawnFix v4.0.0

ArenaRaceMultiplayerSpawnFix.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("REPOJP")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("zabuMod")]
[assembly: AssemblyTitle("zabuMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 REPOJP.ArenaRaceMultiplayerSpawnFix
{
	[BepInPlugin("REPOJP.ArenaRaceMultiplayerSpawnFix", "ArenaRaceMultiplayerSpawnFix", "1.5.0")]
	public class ArenaRaceMultiplayerSpawnFixPlugin : BaseUnityPlugin
	{
		private sealed class LayoutPlan
		{
			public readonly List<Vector3> Positions = new List<Vector3>();
		}

		[HarmonyPatch(typeof(LevelGenerator), "PlayerSpawn")]
		private static class LevelGeneratorPlayerSpawnPatch
		{
			private static void Postfix()
			{
				try
				{
					if (!ShouldApplyLayout())
					{
						return;
					}
					lastStartBoostArenaRaceInstanceId = 0;
					List<PlayerAvatar> randomizedPlayers = GetRandomizedPlayers();
					if (randomizedPlayers.Count != 0)
					{
						LayoutPlan layoutPlan = BuildLayoutPlan(randomizedPlayers);
						if (layoutPlan != null && layoutPlan.Positions.Count != 0)
						{
							ApplyLayout(randomizedPlayers, layoutPlan);
						}
					}
				}
				catch (Exception ex)
				{
					LogError("Failure: PlayerSpawn Postfix\n" + ex);
				}
			}
		}

		[HarmonyPatch(typeof(ArenaRace), "StateSetRPC")]
		private static class ArenaRaceStateSetRPCPatch
		{
			private static void Postfix(ArenaRace __instance, States _state)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0004: Invalid comparison between Unknown and I4
				try
				{
					if ((int)_state == 2)
					{
						ApplyStartBoostOnce(__instance);
					}
				}
				catch (Exception ex)
				{
					LogError("Failure: ArenaRace.StateSetRPC Postfix\n" + ex);
				}
			}
		}

		public const string PluginGuid = "REPOJP.ArenaRaceMultiplayerSpawnFix";

		public const string PluginName = "ArenaRaceMultiplayerSpawnFix";

		public const string PluginVersion = "1.5.0";

		private const float FixedHorizontalSpacing = 0.8f;

		private const float FixedVerticalSpacing = 3f;

		private const float FixedHeightOffset = 5f;

		private const float FixedBackwardOffset = 10f;

		private static ManualLogSource logSource;

		private static Harmony harmony;

		private static ConfigEntry<bool> enabledConfig;

		private static ConfigEntry<int> horizontalPlayersConfig;

		private static ConfigEntry<int> verticalPlayersConfig;

		private static ConfigEntry<bool> startBoostEnabledConfig;

		private static ConfigEntry<float> startBoostMultiplierConfig;

		private static FieldInfo arenaRacePlayerVehicleMapField;

		private static FieldInfo arenaRaceVehiclesField;

		private static MethodInfo itemVehicleOverrideYawMethod;

		private static MethodInfo itemVehicleOverridePitchMethod;

		private static int lastStartBoostArenaRaceInstanceId;

		private void Awake()
		{
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			try
			{
				((Component)this).transform.parent = null;
				((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
				logSource = ((BaseUnityPlugin)this).Logger;
				BindConfig();
				arenaRacePlayerVehicleMapField = AccessTools.Field(typeof(ArenaRace), "playerVehicleMap");
				arenaRaceVehiclesField = AccessTools.Field(typeof(ArenaRace), "vehicles");
				itemVehicleOverrideYawMethod = AccessTools.Method(typeof(ItemVehicle), "OverrideYaw", new Type[3]
				{
					typeof(Vector3),
					typeof(float),
					typeof(int)
				}, (Type[])null);
				itemVehicleOverridePitchMethod = AccessTools.Method(typeof(ItemVehicle), "OverridePitch", new Type[2]
				{
					typeof(float),
					typeof(float)
				}, (Type[])null);
				harmony = new Harmony("REPOJP.ArenaRaceMultiplayerSpawnFix");
				harmony.PatchAll();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failure: Awake\n" + ex));
			}
		}

		private void OnDestroy()
		{
			try
			{
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception ex)
			{
				LogError("Failure: OnDestroy\n" + ex);
			}
		}

		private void BindConfig()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			enabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable this mod.このMODを有効にします");
			horizontalPlayersConfig = ((BaseUnityPlugin)this).Config.Bind<int>("Spawn Layout", "HorizontalPlayers", 10, new ConfigDescription("Number of players per horizontal row.横1行に並べる人数", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			verticalPlayersConfig = ((BaseUnityPlugin)this).Config.Bind<int>("Spawn Layout", "VerticalPlayers", 2, new ConfigDescription("Number of vertical rows.縦方向の行数", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			startBoostEnabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Start Boost", "StartBoostEnabled", true, "Apply a temporary level 3 boost after the countdown.321後に一時的なレベル3ブーストを付与します");
			startBoostMultiplierConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Start Boost", "StartBoostMultiplier", 10f, new ConfigDescription("Level 3 boost power multiplier.レベル3ブーストの強さ倍率", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		}

		private static bool ShouldApplyLayout()
		{
			if (enabledConfig == null || !enabledConfig.Value)
			{
				return false;
			}
			if (!SafeIsMasterClientOrSingleplayer())
			{
				return false;
			}
			if ((Object)(object)LevelGenerator.Instance == (Object)null || SafeLevelGenerated())
			{
				return false;
			}
			if ((Object)(object)ArenaRace.instance == (Object)null)
			{
				return false;
			}
			if (!SafeRunIsArena())
			{
				return false;
			}
			return true;
		}

		private static List<PlayerAvatar> GetRandomizedPlayers()
		{
			List<PlayerAvatar> list = new List<PlayerAvatar>();
			if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null)
			{
				return list;
			}
			foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
			{
				if ((Object)(object)player != (Object)null && (Object)(object)((Component)player).gameObject != (Object)null)
				{
					list.Add(player);
				}
			}
			return ShufflePlayers(list);
		}

		private static LayoutPlan BuildLayoutPlan(List<PlayerAvatar> players)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			LayoutPlan layoutPlan = new LayoutPlan();
			if (players == null || players.Count == 0)
			{
				return layoutPlan;
			}
			Vector3 val = GetVanillaCenter(players) + Vector3.up * 5f;
			Vector3 courseForward = GetCourseForward();
			Vector3 val2 = Vector3.Cross(Vector3.up, courseForward);
			Vector3 val3 = ((Vector3)(ref val2)).normalized;
			if (((Vector3)(ref val3)).sqrMagnitude < 0.0001f)
			{
				val3 = Vector3.right;
			}
			int num = Mathf.Clamp((horizontalPlayersConfig != null) ? horizontalPlayersConfig.Value : 10, 1, 20);
			int num2 = Mathf.Clamp((verticalPlayersConfig != null) ? verticalPlayersConfig.Value : 2, 1, 20);
			int num3 = Mathf.CeilToInt((float)players.Count / (float)Mathf.Max(1, num));
			num2 = Mathf.Max(num2, num3);
			List<Vector3> list = new List<Vector3>();
			for (int i = 0; i < num2; i++)
			{
				for (int j = 0; j < num; j++)
				{
					float num4 = ((float)j - ((float)num - 1f) * 0.5f) * 0.8f;
					float num5 = 0f - ((float)i * 3f + 10f);
					Vector3 item = val + val3 * num4 + courseForward * num5;
					list.Add(item);
				}
			}
			list = ShufflePositions(list);
			for (int k = 0; k < players.Count && k < list.Count; k++)
			{
				layoutPlan.Positions.Add(list[k]);
			}
			return layoutPlan;
		}

		private static List<Vector3> ShufflePositions(List<Vector3> positions)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>(positions);
			Random random = new Random(Guid.NewGuid().GetHashCode() ^ Environment.TickCount ^ list.Count);
			for (int num = list.Count - 1; num > 0; num--)
			{
				int index = random.Next(num + 1);
				Vector3 value = list[num];
				list[num] = list[index];
				list[index] = value;
			}
			return list;
		}

		private static void ApplyLayout(List<PlayerAvatar> players, LayoutPlan plan)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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)
			int num = Mathf.Min(players.Count, plan.Positions.Count);
			for (int i = 0; i < num; i++)
			{
				PlayerAvatar val = players[i];
				if (!((Object)(object)val == (Object)null))
				{
					Vector3 val2 = plan.Positions[i];
					Quaternion rotation = ((Component)val).transform.rotation;
					val.Spawn(val2, rotation);
				}
			}
		}

		private static Vector3 GetVanillaCenter(List<PlayerAvatar> players)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: 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_0038: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Vector3.zero;
			int num = 0;
			foreach (PlayerAvatar player in players)
			{
				if (!((Object)(object)player == (Object)null))
				{
					val += ((Component)player).transform.position;
					num++;
				}
			}
			if (num == 0)
			{
				return Vector3.zero;
			}
			return val / (float)num;
		}

		private static Vector3 GetCourseForward()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 forward = Vector3.forward;
			try
			{
				if ((Object)(object)ArenaRace.instance != (Object)null && (Object)(object)ArenaRace.instance.trackSpawnOrigin != (Object)null)
				{
					forward = ArenaRace.instance.trackSpawnOrigin.forward;
				}
			}
			catch
			{
			}
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f)
			{
				forward = Vector3.forward;
			}
			return ((Vector3)(ref forward)).normalized;
		}

		private static List<PlayerAvatar> ShufflePlayers(List<PlayerAvatar> players)
		{
			List<PlayerAvatar> list = new List<PlayerAvatar>(players);
			Random random = new Random(Guid.NewGuid().GetHashCode() ^ Environment.TickCount);
			for (int num = list.Count - 1; num > 0; num--)
			{
				int index = random.Next(num + 1);
				PlayerAvatar value = list[num];
				list[num] = list[index];
				list[index] = value;
			}
			return list;
		}

		private static void ApplyStartBoostOnce(ArenaRace arenaRace)
		{
			if ((Object)(object)arenaRace == (Object)null || enabledConfig == null || !enabledConfig.Value || startBoostEnabledConfig == null || !startBoostEnabledConfig.Value || !SafeIsMasterClientOrSingleplayer() || !SafeRunIsArena())
			{
				return;
			}
			int instanceID = ((Object)arenaRace).GetInstanceID();
			if (lastStartBoostArenaRaceInstanceId == instanceID)
			{
				return;
			}
			lastStartBoostArenaRaceInstanceId = instanceID;
			List<ItemVehicle> arenaRaceVehicles = GetArenaRaceVehicles(arenaRace);
			foreach (ItemVehicle item in arenaRaceVehicles)
			{
				ApplyStartBoostToVehicle(arenaRace, item);
			}
		}

		private static List<ItemVehicle> GetArenaRaceVehicles(ArenaRace arenaRace)
		{
			List<ItemVehicle> list = new List<ItemVehicle>();
			if ((Object)(object)arenaRace == (Object)null)
			{
				return list;
			}
			try
			{
				if (arenaRacePlayerVehicleMapField != null)
				{
					object value = arenaRacePlayerVehicleMapField.GetValue(arenaRace);
					if (value is Dictionary<PlayerAvatar, ItemVehicle> dictionary)
					{
						foreach (KeyValuePair<PlayerAvatar, ItemVehicle> item in dictionary)
						{
							if ((Object)(object)item.Value != (Object)null && !list.Contains(item.Value))
							{
								list.Add(item.Value);
							}
						}
					}
				}
			}
			catch
			{
			}
			try
			{
				if (list.Count == 0 && arenaRaceVehiclesField != null)
				{
					object value2 = arenaRaceVehiclesField.GetValue(arenaRace);
					if (value2 is List<ItemVehicle> list2)
					{
						foreach (ItemVehicle item2 in list2)
						{
							if ((Object)(object)item2 != (Object)null && !list.Contains(item2))
							{
								list.Add(item2);
							}
						}
					}
				}
			}
			catch
			{
			}
			return list;
		}

		private static bool ApplyStartBoostToVehicle(ArenaRace arenaRace, ItemVehicle vehicle)
		{
			//IL_008b: 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_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)arenaRace == (Object)null || (Object)(object)vehicle == (Object)null)
			{
				return false;
			}
			if (itemVehicleOverrideYawMethod == null)
			{
				return false;
			}
			float driftBoostLevel3ForwardBoost = vehicle.driftBoostLevel3ForwardBoost;
			float driftBoostLevel3SpeedBoostMultiplier = vehicle.driftBoostLevel3SpeedBoostMultiplier;
			try
			{
				float num = ((startBoostMultiplierConfig != null) ? startBoostMultiplierConfig.Value : 10f);
				float num2 = Mathf.Clamp(num, 0f, 100f);
				vehicle.driftBoostLevel3ForwardBoost = driftBoostLevel3ForwardBoost * num2;
				vehicle.driftBoostLevel3SpeedBoostMultiplier = driftBoostLevel3SpeedBoostMultiplier * num2;
				Vector3 startBoostForward = GetStartBoostForward(arenaRace, vehicle);
				float num3 = Mathf.Asin(Mathf.Clamp(startBoostForward.y, -1f, 1f)) * 57.29578f;
				Vector3 val = startBoostForward;
				val.y = 0f;
				if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
				{
					val = ((Component)vehicle).transform.forward;
					val.y = 0f;
				}
				if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
				{
					val = Vector3.forward;
				}
				((Vector3)(ref val)).Normalize();
				itemVehicleOverrideYawMethod.Invoke(vehicle, new object[3] { val, 0.25f, 3 });
				if (itemVehicleOverridePitchMethod != null)
				{
					itemVehicleOverridePitchMethod.Invoke(vehicle, new object[2] { num3, 1f });
				}
				return true;
			}
			catch (Exception ex)
			{
				LogError("Failure: ApplyStartBoostToVehicle\n" + ex);
				return false;
			}
			finally
			{
				vehicle.driftBoostLevel3ForwardBoost = driftBoostLevel3ForwardBoost;
				vehicle.driftBoostLevel3SpeedBoostMultiplier = driftBoostLevel3SpeedBoostMultiplier;
			}
		}

		private static Vector3 GetStartBoostForward(ArenaRace arenaRace, ItemVehicle vehicle)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)arenaRace != (Object)null && (Object)(object)arenaRace.trackSpawnOrigin != (Object)null)
				{
					return arenaRace.trackSpawnOrigin.forward;
				}
				if ((Object)(object)vehicle != (Object)null)
				{
					return ((Component)vehicle).transform.forward;
				}
			}
			catch
			{
			}
			return Vector3.forward;
		}

		private static bool SafeIsMasterClientOrSingleplayer()
		{
			try
			{
				return SemiFunc.IsMasterClientOrSingleplayer();
			}
			catch
			{
				return false;
			}
		}

		private static bool SafeRunIsArena()
		{
			try
			{
				return SemiFunc.RunIsArena();
			}
			catch
			{
				return false;
			}
		}

		private static bool SafeLevelGenerated()
		{
			try
			{
				return (Object)(object)LevelGenerator.Instance != (Object)null && LevelGenerator.Instance.Generated;
			}
			catch
			{
				return false;
			}
		}

		private static void LogError(string message)
		{
			if (logSource != null)
			{
				logSource.LogError((object)message);
			}
		}
	}
}