Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TeamWars v0.8.79
TeamWars.dll
Decompiled 13 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("TeamWars")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.8.79.0")] [assembly: AssemblyInformationalVersion("0.8.79+66899d9183ba9330d8b6c499abf2269595378e8c")] [assembly: AssemblyProduct("TeamWars")] [assembly: AssemblyTitle("TeamWars")] [assembly: AssemblyVersion("0.8.79.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TeamWars { internal static class ConfigSettings { public static ConfigEntry<float> GoalStartPercent; public static ConfigEntry<float> MinTimeBaseMinutes; public static ConfigEntry<float> MinTimePerModuleMinutes; public static ConfigEntry<float> CloseGraceMinutes; public static ConfigEntry<float> ValuableMultiplier; public static ConfigEntry<bool> VerboseLogs; public static ConfigEntry<bool> RequireCarts; public static ConfigEntry<float> CartRadius; public static ConfigEntry<bool> BlockRivalTouch; public static ConfigEntry<bool> AllowGrabHeldByRival; public static ConfigEntry<int> ReviveCostFlat; public static ConfigEntry<float> ReviveCostPercent; public static ConfigEntry<KeyCode> ReviveKey; public static void Bind(ConfigFile config) { GoalStartPercent = config.Bind<float>("Economy", "GoalStartPercent", 28f, "Starting goal for each team, as percent (0-100) of the level's total value."); MinTimeBaseMinutes = config.Bind<float>("Round", "MinTimeBaseMinutes", 10f, "Base minimum round time in minutes."); MinTimePerModuleMinutes = config.Bind<float>("Round", "MinTimePerModuleMinutes", 3f, "Extra minimum minutes per level module."); CloseGraceMinutes = config.Bind<float>("Round", "CloseGraceMinutes", 5f, "Extra minutes after the minimum time before players outside the truck stop blocking the round close."); ValuableMultiplier = config.Bind<float>("Spawning", "ValuableMultiplier", 2f, "How many times more valuables spawn per level (2 = double, one share per team). Only the host's value matters; it applies at level load."); VerboseLogs = config.Bind<bool>("Debug", "VerboseLogs", false, "Detailed per-tick logs (haul ticks, tally/roster syncs, blocked damage). Keep off: logging costs performance."); RequireCarts = config.Bind<bool>("Extraction", "RequireCarts", true, "Each extraction check needs both teams' main carts inside it to start; without them the button does nothing."); CartRadius = config.Bind<float>("Extraction", "CartRadius", 12f, "How close (meters) a main cart must be to the check to count as inside it."); BlockRivalTouch = config.Bind<bool>("Protection", "BlockRivalTouch", true, "Whether a team is blocked from grabbing or breaking what the other team owns (carts, claimed items, shop placements)."); AllowGrabHeldByRival = config.Bind<bool>("Protection", "AllowGrabHeldByRival", false, "Whether a player may grab an object someone from the other team is holding (contested grabs, steals from hands). Off forbids grabbing anything a rival holds, on top of the rival-touch block."); ReviveCostFlat = config.Bind<int>("Revive", "CostFlat", 5000, "Flat revive price in dollars, charged to the dead player's team purse. Ignored while CostPercent is over 0."); ReviveCostPercent = config.Bind<float>("Revive", "CostPercent", 5f, "Revive price as percent (0-100) of the round-start level total. Over 0 replaces the flat price."); ReviveKey = config.Bind<KeyCode>("Revive", "Key", (KeyCode)290, "Key a living player presses to buy back one dead teammate (the vanilla head revive, paid from the team purse)."); } } internal static class GameInternals { private static FieldRef<RunManager, bool> runLevelIsShopField = AccessTools.FieldRefAccess<RunManager, bool>("levelIsShop"); private static FieldRef<PlayerAvatar, string> playerNameField = AccessTools.FieldRefAccess<PlayerAvatar, string>("playerName"); private static FieldRef<ValuableObject, float> dollarValueCurrentField = AccessTools.FieldRefAccess<ValuableObject, float>("dollarValueCurrent"); private static FieldRef<ValuableObject, float> dollarValueOriginalField = AccessTools.FieldRefAccess<ValuableObject, float>("dollarValueOriginal"); private static FieldRef<PhysGrabObject, List<PhysGrabber>> grabbingListField = AccessTools.FieldRefAccess<PhysGrabObject, List<PhysGrabber>>("playerGrabbing"); private static FieldRef<PhysGrabObject, PlayerAvatar> lastGrabbingField = AccessTools.FieldRefAccess<PhysGrabObject, PlayerAvatar>("lastPlayerGrabbing"); private static FieldRef<PhysGrabber, PlayerAvatar> grabberAvatarField = AccessTools.FieldRefAccess<PhysGrabber, PlayerAvatar>("playerAvatar"); private static FieldRef<PhysGrabber, PhysGrabObject> lookingAtObjectField = AccessTools.FieldRefAccess<PhysGrabber, PhysGrabObject>("currentlyLookingAtPhysGrabObject"); private static FieldRef<PhysGrabber, ItemAttributes> lookingAtItemField = AccessTools.FieldRefAccess<PhysGrabber, ItemAttributes>("currentlyLookingAtItemAttributes"); private static FieldRef<RoomVolumeCheck, bool> inTruckField = AccessTools.FieldRefAccess<RoomVolumeCheck, bool>("inTruck"); private static FieldRef<PlayerAvatar, bool> playerDisabledField = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isDisabled"); private static FieldRef<PlayerAvatar, PlayerDeathHead> deathHeadField = AccessTools.FieldRefAccess<PlayerAvatar, PlayerDeathHead>("playerDeathHead"); private static FieldRef<LevelGenerator, int> moduleAmountField = AccessTools.FieldRefAccess<LevelGenerator, int>("ModuleAmount"); private static FieldRef<StatsManager, SortedDictionary<string, Dictionary<string, int>>> dictionariesField = AccessTools.FieldRefAccess<StatsManager, SortedDictionary<string, Dictionary<string, int>>>("dictionaryOfDictionaries"); private static FieldRef<RoomVolumeCheck, bool> inExtractionField = AccessTools.FieldRefAccess<RoomVolumeCheck, bool>("inExtractionPoint"); private static FieldRef<ValuableObject, RoomVolumeCheck> valuableRoomCheckField = AccessTools.FieldRefAccess<ValuableObject, RoomVolumeCheck>("roomVolumeCheck"); private static FieldRef<RoomVolumeCheck, List<RoomVolume>> currentRoomsField = AccessTools.FieldRefAccess<RoomVolumeCheck, List<RoomVolume>>("CurrentRooms"); private static FieldRef<RoomVolume, bool> extractionRoomField = AccessTools.FieldRefAccess<RoomVolume, bool>("Extraction"); private static FieldRef<MenuPlayerListed, PlayerAvatar> listedAvatarField = AccessTools.FieldRefAccess<MenuPlayerListed, PlayerAvatar>("playerAvatar"); private static FieldInfo escMenuPageField; private static FieldInfo pageActiveField; private const BindingFlags AnyField = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static FieldRef<ShopManager, List<ItemAttributes>> shoppingListField = AccessTools.FieldRefAccess<ShopManager, List<ItemAttributes>>("shoppingList"); private static FieldRef<ItemAttributes, RoomVolumeCheck> itemRoomCheckField = AccessTools.FieldRefAccess<ItemAttributes, RoomVolumeCheck>("roomVolumeCheck"); private static FieldRef<ItemAttributes, string> itemNameField = AccessTools.FieldRefAccess<ItemAttributes, string>("itemName"); private static FieldRef<ItemAttributes, int> itemValueField = AccessTools.FieldRefAccess<ItemAttributes, int>("value"); private static FieldRef<ExtractionPoint, int> haulGoalField = AccessTools.FieldRefAccess<ExtractionPoint, int>("haulGoal"); private static MethodInfo haulGoalSetMethod; private static MethodInfo currentCartMethod; private static MethodInfo rerollCostMethod; private static FieldInfo standStateStartField; private static FieldRef<RoundDirector, int> extractionSurplusField; private static FieldRef<PhysGrabObject, Vector3> spawnTorqueField; private static FieldRef<PhysGrabCart, List<PhysGrabObject>> cartItemsField; private static FieldRef<ValuableObject, int> dollarValueOverrideField; private static FieldInfo surplusLightActiveField; private static FieldInfo surplusLightIntensityField; private static FieldInfo surplusLightRangeField; private static FieldRef<ExtractionPoint, int> haulCurrentField = AccessTools.FieldRefAccess<ExtractionPoint, int>("haulCurrent"); private static FieldRef<RoundDirector, int> extractionPointsField = AccessTools.FieldRefAccess<RoundDirector, int>("extractionPoints"); private static FieldRef<RoundDirector, int> extractionPointsCompletedField = AccessTools.FieldRefAccess<RoundDirector, int>("extractionPointsCompleted"); private static FieldInfo screenGrabColliderField; private static FieldInfo itemPrefabField; private static FieldInfo extractionStateField; private static MethodInfo extractionStateSetMethod; public static SortedDictionary<string, Dictionary<string, int>> SaveDictionaries(StatsManager stats) { if (!((Object)(object)stats == (Object)null)) { return dictionariesField.Invoke(stats); } return null; } public static bool IsInTruck(RoomVolumeCheck check) { if ((Object)(object)check != (Object)null) { return inTruckField.Invoke(check); } return false; } public static PlayerAvatar ListedAvatar(MenuPlayerListed row) { if (!((Object)(object)row == (Object)null)) { return listedAvatarField.Invoke(row); } return null; } public static bool EscMenuOpen() { MenuPageEsc instance = MenuPageEsc.instance; if ((Object)(object)instance == (Object)null) { return false; } if (escMenuPageField == null) { escMenuPageField = typeof(MenuPageEsc).GetField("menuPage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } object? obj = escMenuPageField?.GetValue(instance); MenuPage val = (MenuPage)((obj is MenuPage) ? obj : null); if ((Object)(object)val == (Object)null) { return false; } if (pageActiveField == null) { pageActiveField = typeof(MenuPage).GetField("pageActive", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (pageActiveField != null) { object value = pageActiveField.GetValue(val); if (value is bool) { return (bool)value; } } return ((Component)val).gameObject.activeSelf; } public static bool IsInExtraction(ValuableObject valuable) { if ((Object)(object)valuable == (Object)null) { return false; } RoomVolumeCheck val = valuableRoomCheckField.Invoke(valuable); if ((Object)(object)val == (Object)null) { return false; } List<RoomVolume> list = currentRoomsField.Invoke(val); if (list == null) { return false; } foreach (RoomVolume item in list) { if ((Object)(object)item != (Object)null && extractionRoomField.Invoke(item)) { return true; } } return false; } public static bool IsInExtractionPoint(RoomVolumeCheck check) { if ((Object)(object)check != (Object)null) { return inExtractionField.Invoke(check); } return false; } public static bool IsPlayerDisabled(PlayerAvatar avatar) { if ((Object)(object)avatar != (Object)null) { return playerDisabledField.Invoke(avatar); } return false; } public static PlayerDeathHead DeathHead(PlayerAvatar avatar) { if ((Object)(object)avatar == (Object)null) { return null; } try { return deathHeadField.Invoke(avatar); } catch (Exception) { return null; } } public static int ModuleCount() { if (!((Object)(object)LevelGenerator.Instance == (Object)null)) { return moduleAmountField.Invoke(LevelGenerator.Instance); } return 0; } public static List<ItemAttributes> ShoppingList(ShopManager shop) { if (!((Object)(object)shop == (Object)null)) { return shoppingListField.Invoke(shop); } return null; } public static RoomVolumeCheck RoomCheck(ItemAttributes item) { if (!((Object)(object)item == (Object)null)) { return itemRoomCheckField.Invoke(item); } return null; } public static string ItemName(ItemAttributes item) { if (!((Object)(object)item == (Object)null)) { return itemNameField.Invoke(item); } return null; } public static int ItemValue(ItemAttributes item) { if (!((Object)(object)item == (Object)null)) { return itemValueField.Invoke(item); } return 0; } public static int RerollCost(UpgradeStand stand) { if ((Object)(object)stand == (Object)null) { return 0; } if (rerollCostMethod == null) { rerollCostMethod = typeof(UpgradeStand).GetMethod("get_RerollCost", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (rerollCostMethod != null) { object obj = rerollCostMethod.Invoke(stand, null); if (obj is int) { return (int)obj; } } return 0; } public static int ExtractionSurplus() { if ((Object)(object)RoundDirector.instance == (Object)null) { return 0; } if (extractionSurplusField == null) { extractionSurplusField = AccessTools.FieldRefAccess<RoundDirector, int>("extractionPointSurplus"); } return extractionSurplusField.Invoke(RoundDirector.instance); } public static void ClearExtractionSurplus() { if (!((Object)(object)RoundDirector.instance == (Object)null)) { if (extractionSurplusField == null) { extractionSurplusField = AccessTools.FieldRefAccess<RoundDirector, int>("extractionPointSurplus"); } extractionSurplusField.Invoke(RoundDirector.instance) = 0; } } public static void SetSpawnTorque(PhysGrabObject grabObject, Vector3 torque) { //IL_002b: 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) if (!((Object)(object)grabObject == (Object)null)) { if (spawnTorqueField == null) { spawnTorqueField = AccessTools.FieldRefAccess<PhysGrabObject, Vector3>("spawnTorque"); } spawnTorqueField.Invoke(grabObject) = torque; } } public static List<PhysGrabObject> CartItems(PhysGrabCart cart) { if ((Object)(object)cart == (Object)null) { return null; } if (cartItemsField == null) { cartItemsField = AccessTools.FieldRefAccess<PhysGrabCart, List<PhysGrabObject>>("itemsInCart"); } return cartItemsField.Invoke(cart); } public static GameObject SurplusPrefab(int value) { AssetManager instance = AssetManager.instance; if ((Object)(object)instance == (Object)null) { return null; } if (value > 10000) { return instance.surplusValuableBig; } if (value > 5000) { return instance.surplusValuableMedium; } return instance.surplusValuableSmall; } public static void SetValuableOverride(ValuableObject valuable, int value) { if (!((Object)(object)valuable == (Object)null)) { if (dollarValueOverrideField == null) { dollarValueOverrideField = AccessTools.FieldRefAccess<ValuableObject, int>("dollarValueOverride"); } dollarValueOverrideField.Invoke(valuable) = value; } } public static void SurplusLights(ExtractionPoint point) { if (!((Object)(object)point == (Object)null) && !((Object)(object)point.surplusLight == (Object)null)) { if (surplusLightActiveField == null) { surplusLightActiveField = AccessTools.Field(typeof(ExtractionPoint), "surplusLightActive"); surplusLightIntensityField = AccessTools.Field(typeof(ExtractionPoint), "surplusLightIntensity"); surplusLightRangeField = AccessTools.Field(typeof(ExtractionPoint), "surplusLightRange"); } if (surplusLightActiveField != null) { surplusLightActiveField.SetValue(point, true); } point.surplusLight.intensity = ((surplusLightIntensityField != null) ? ((float)surplusLightIntensityField.GetValue(point)) : 1f); if (surplusLightRangeField != null) { point.surplusLight.range = (float)surplusLightRangeField.GetValue(point); } } } public static int ButtonGrabberTeam(UpgradeStand stand) { if (!((Object)(object)stand == (Object)null)) { return ButtonGrabberTeam(stand.buttonGrabObject); } return -1; } public static int ButtonGrabberTeam(StaticGrabObject button) { if ((Object)(object)button == (Object)null || button.playerGrabbing == null) { return -1; } int result = -1; foreach (PhysGrabber item in button.playerGrabbing) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item.playerAvatar == (Object)null) && !((Object)(object)item.playerAvatar.photonView == (Object)null)) { int team = TeamState.GetTeam(item.playerAvatar.photonView.OwnerActorNr); if (team != -1) { result = team; } } } return result; } public static bool RerollPressStarted(UpgradeStand stand) { if ((Object)(object)stand == (Object)null) { return false; } if (standStateStartField == null) { standStateStartField = typeof(UpgradeStand).GetField("stateStart", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } bool flag = default(bool); int num; if (standStateStartField != null) { object value = standStateStartField.GetValue(stand); if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } public static PhysGrabCart CurrentCart(ValuableObject valuable) { if ((Object)(object)valuable == (Object)null) { return null; } if (currentCartMethod == null) { currentCartMethod = typeof(ValuableObject).GetMethod("GetCurrentCart", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } object? obj = currentCartMethod?.Invoke(valuable, null); return (PhysGrabCart)((obj is PhysGrabCart) ? obj : null); } public static void SetCheckGoal(ExtractionPoint point, int goal) { if (!((Object)(object)point == (Object)null)) { if (haulGoalSetMethod == null) { haulGoalSetMethod = typeof(ExtractionPoint).GetMethod("HaulGoalSet", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } haulGoalSetMethod?.Invoke(point, new object[1] { goal }); } } public static int CheckGoal(ExtractionPoint point) { if (!((Object)(object)point == (Object)null)) { return haulGoalField.Invoke(point); } return 0; } public static int CheckHaul(ExtractionPoint point) { if (!((Object)(object)point == (Object)null)) { return haulCurrentField.Invoke(point); } return 0; } public static int CompletedCheckCount() { RoundDirector instance = RoundDirector.instance; if (!((Object)(object)instance == (Object)null)) { return extractionPointsCompletedField.Invoke(instance); } return 0; } public static bool CheckComplete() { RoundDirector instance = RoundDirector.instance; if ((Object)(object)instance == (Object)null) { return false; } int num = extractionPointsField.Invoke(instance); if (num > 0 && extractionPointsCompletedField.Invoke(instance) >= num) { return true; } ExtractionPoint[] array = Object.FindObjectsOfType<ExtractionPoint>(); foreach (ExtractionPoint point in array) { if (CheckGoal(point) > 0 && CheckHaul(point) >= CheckGoal(point)) { return true; } } return false; } public static void SetCheckGoalLocal(ExtractionPoint point, int goal) { if ((Object)(object)point != (Object)null) { haulGoalField.Invoke(point) = goal; } } public static bool IsTruckScreen(GameObject grabbed) { if ((Object)(object)grabbed == (Object)null || (Object)(object)TruckScreenText.instance == (Object)null) { return false; } if (screenGrabColliderField == null) { screenGrabColliderField = typeof(TruckScreenText).GetField("staticGrabCollider", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } object? obj = screenGrabColliderField?.GetValue(TruckScreenText.instance); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)val != (Object)null) { return (Object)(object)val == (Object)(object)grabbed; } return false; } public static int DelivererTeam(PhysGrabObject grabObject) { if ((Object)(object)grabObject == (Object)null) { return -1; } PlayerAvatar val = Deliverer(grabObject); if ((Object)(object)val == (Object)null || (Object)(object)val.photonView == (Object)null) { return -1; } return TeamState.GetTeam(val.photonView.OwnerActorNr); } public static PhysGrabObject LookingAt(PhysGrabber grabber) { if (!((Object)(object)grabber == (Object)null)) { return lookingAtObjectField.Invoke(grabber); } return null; } public static void ClearLookingAt(PhysGrabber grabber) { if (!((Object)(object)grabber == (Object)null)) { lookingAtObjectField.Invoke(grabber) = null; lookingAtItemField.Invoke(grabber) = null; } } private static object PrefabRefOf(Item item) { if ((Object)(object)item == (Object)null) { return null; } if (itemPrefabField == null) { itemPrefabField = typeof(Item).GetField("prefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } return itemPrefabField?.GetValue(item); } public static string ItemResourcePath(Item item) { return PrefabMember(item, "ResourcePath") as string; } public static GameObject ItemPrefab(Item item) { object obj = PrefabMember(item, "Prefab"); return (GameObject)((obj is GameObject) ? obj : null); } private static object PrefabMember(Item item, string member) { object obj = PrefabRefOf(item); return obj?.GetType().GetProperty(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); } public static List<PlayerAvatar> CurrentHolders(PhysGrabObject grabObject) { List<PlayerAvatar> list = new List<PlayerAvatar>(); if ((Object)(object)grabObject == (Object)null) { return list; } List<PhysGrabber> list2 = grabbingListField.Invoke(grabObject); if (list2 == null) { return list; } foreach (PhysGrabber item in list2) { if (!((Object)(object)item == (Object)null)) { PlayerAvatar val = grabberAvatarField.Invoke(item); if ((Object)(object)val != (Object)null) { list.Add(val); } } } return list; } public static PlayerAvatar Deliverer(PhysGrabObject grabObject) { if ((Object)(object)grabObject == (Object)null) { return null; } List<PhysGrabber> list = grabbingListField.Invoke(grabObject); if (list != null && list.Count > 0) { PhysGrabber val = list[list.Count - 1]; if ((Object)(object)val != (Object)null) { return grabberAvatarField.Invoke(val); } } return lastGrabbingField.Invoke(grabObject); } public static float ValuableCurrentValue(ValuableObject valuable) { return dollarValueCurrentField.Invoke(valuable); } public static float ValuableOriginalValue(ValuableObject valuable) { return dollarValueOriginalField.Invoke(valuable); } public static int RunCurrency() { return SemiFunc.StatGetRunCurrency(); } public static void SetRunCurrency(int units) { SemiFunc.StatSetRunCurrency(units); } public static bool LevelIsShop(RunManager runManager) { if ((Object)(object)runManager == (Object)null) { return false; } return runLevelIsShopField.Invoke(runManager); } public static string PlayerName(PlayerAvatar avatar) { if ((Object)(object)avatar == (Object)null) { return "?"; } return playerNameField.Invoke(avatar); } public static ExtractionPoint ExtractionOf(Component component) { if (!((Object)(object)component != (Object)null)) { return null; } return component.GetComponentInParent<ExtractionPoint>(); } public static string ExtractionStateName(ExtractionPoint point) { if ((Object)(object)point == (Object)null) { return null; } if (extractionStateField == null) { extractionStateField = typeof(ExtractionPoint).GetField("currentState", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } try { return extractionStateField?.GetValue(point)?.ToString(); } catch (Exception) { return null; } } public static bool DriveExtraction(ExtractionPoint point, string target) { if ((Object)(object)point == (Object)null || string.IsNullOrEmpty(target)) { return false; } try { if (extractionStateField == null) { extractionStateField = typeof(ExtractionPoint).GetField("currentState", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (extractionStateSetMethod == null) { extractionStateSetMethod = typeof(ExtractionPoint).GetMethod("StateSet", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } Type type = extractionStateField?.FieldType; if (type == null || extractionStateSetMethod == null) { return false; } object obj = Enum.Parse(type, target); extractionStateSetMethod.Invoke(point, new object[1] { obj }); return true; } catch (Exception ex) { Plugin.Log("Extraction drive failed: " + ex.Message); return false; } } } [HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "InCartRPC")] internal static class CartClaimPatch { private static void Postfix(PhysGrabObjectImpactDetector __instance, bool inCartState) { if (inCartState) { TeamHaul.MarkIntoTeamCart(__instance); } } } [HarmonyPatch(typeof(ExtractionPoint), "StateSet")] internal static class CheckCartGatePatch { private static string lastRefusal; private static readonly Dictionary<ExtractionPoint, State> pendingRetries = new Dictionary<ExtractionPoint, State>(); public static void ClearPending() { pendingRetries.Clear(); } private static bool Prefix(ExtractionPoint __instance, State newState) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_010a: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) if ((int)newState != 4 && (int)newState != 6 && (int)newState != 7) { return true; } if ((Object)(object)RunManager.instance == (Object)null || !SemiFunc.RunIsLevel()) { return true; } if (ConfigSettings.RequireCarts != null && !ConfigSettings.RequireCarts.Value) { return true; } if ((Object)(object)__instance == (Object)null) { return true; } List<int> list = TeamCarts.CartsMissingAt(__instance); list.RemoveAll(TeamRound.TeamWiped); if (list.Count == 0) { lastRefusal = null; if (pendingRetries.TryGetValue(__instance, out var value) && value == newState) { pendingRetries.Remove(__instance); } Plugin.LogVerbose($"Cart gate: {((Object)__instance).name} -> {newState} allowed (both carts in)"); return true; } string arg = string.Join(" y ", list.ConvertAll((int team) => (team != 0) ? "Azul" : "Rojo").ToArray()); string text = $"{((Object)__instance).name} -> {newState} refused: {arg} cart(s) missing from the check"; pendingRetries[__instance] = newState; if (text != lastRefusal) { lastRefusal = text; Plugin.Log("Extraction held: " + text); } return false; } public unsafe static void RetryHeldExtractions() { //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Invalid comparison between Unknown and I4 //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Invalid comparison between Unknown and I4 //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Invalid comparison between Unknown and I4 //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Invalid comparison between Unknown and I4 //IL_01e1: Unknown result type (might be due to invalid IL or missing references) if (!TeamNetworking.IsMasterSession || (Object)(object)RunManager.instance == (Object)null || !SemiFunc.RunIsLevel()) { return; } if (ConfigSettings.RequireCarts != null && !ConfigSettings.RequireCarts.Value) { pendingRetries.Clear(); return; } ExtractionPoint[] array = Object.FindObjectsOfType<ExtractionPoint>(); foreach (ExtractionPoint val in array) { if ((Object)(object)val == (Object)null) { continue; } string text = GameInternals.ExtractionStateName(val); if (!(text != "Active") && GameInternals.CheckHaul(val) >= GameInternals.CheckGoal(val)) { List<int> list = TeamCarts.CartsMissingAt(val); list.RemoveAll(TeamRound.TeamWiped); if (list.Count <= 0) { Plugin.Log("Extraction kick: " + ((Object)val).name + " full in " + text + " with both carts, driving Warning"); GameInternals.DriveExtraction(val, "Warning"); } } } foreach (KeyValuePair<ExtractionPoint, State> item in new List<KeyValuePair<ExtractionPoint, State>>(pendingRetries)) { ExtractionPoint key = item.Key; State value = item.Value; if ((Object)(object)key == (Object)null) { pendingRetries.Remove(key); continue; } string text2 = GameInternals.ExtractionStateName(key); if (((int)value != 4 || !(text2 == "Active")) && (((int)value == 4 && text2 == "Surplus") || ((int)value == 6 && text2 == "Warning") || ((int)value == 7 && text2 == "Extracting"))) { List<int> list2 = TeamCarts.CartsMissingAt(key); list2.RemoveAll(TeamRound.TeamWiped); if (list2.Count <= 0) { Plugin.Log($"Extraction retry: {((Object)key).name} {text2} -> {value} (carts back, completing vanilla's own edge)"); GameInternals.DriveExtraction(key, ((object)(*(State*)(&value))/*cast due to .constrained prefix*/).ToString()); } } } } } [HarmonyPatch(typeof(ExtractionPoint), "HaulGoalSet")] internal static class CheckGoalSetPatch { private static void Prefix(ref int value) { int num = TeamRound.CheckGoal(); if (num > 0) { value = num; } } } [HarmonyPatch(typeof(ExtractionPoint), "HaulInternalStatsUpdate")] internal static class CheckGoalKeepPatch { private static void Postfix(ExtractionPoint __instance) { int num = TeamRound.CheckGoal(); if (num > 0 && GameInternals.CheckGoal(__instance) != num) { GameInternals.SetCheckGoalLocal(__instance, num); } } } [HarmonyPatch(typeof(PhysGrabber), "StartGrabbingPhysObject")] internal static class GrabOwnershipPatch { private static bool Prefix(PhysGrabber __instance, PhysGrabObject _physGrabObject) { return TeamOwnership.AllowsGrab(_physGrabObject, __instance); } } [HarmonyPatch(typeof(PhysGrabber), "RayCheck")] internal static class GrabAimPatch { private static void Postfix(PhysGrabber __instance) { PhysGrabObject val = GameInternals.LookingAt(__instance); if (!((Object)(object)val == (Object)null) && !TeamOwnership.AllowsGrab(val, __instance)) { GameInternals.ClearLookingAt(__instance); } } } [HarmonyPatch(typeof(TruckScreenText), "GotoNextLevel")] internal static class LevelClosePatch { private static bool Prefix() { if (TeamRound.CanClose(out var reason)) { return true; } Plugin.Log("Level close not allowed yet: " + reason); return false; } } [HarmonyPatch(typeof(StatsManager), "RunStartStats")] internal static class RunStartCartPatch { private const string FreeCartItem = "Item Cart Medium"; private static void Postfix(StatsManager __instance) { if (Drop(__instance.itemsPurchased) | Drop(__instance.itemsPurchasedTotal)) { Plugin.Log("Removed the free starter cart (each team gets its own)"); } } private static bool Drop(Dictionary<string, int> purchased) { return purchased?.Remove("Item Cart Medium") ?? false; } } [HarmonyPatch(typeof(StatsManager), "Start")] internal static class SaveRegisterPatch { private static void Postfix() { TeamSave.Register(); } } [HarmonyPatch(typeof(StatsManager), "SaveGame")] internal static class SavePushPatch { private static void Prefix() { if (TeamState.RoundActive) { TeamRound.Close(); } TeamSave.Push(); } } [HarmonyPatch(typeof(StatsManager), "LoadGame")] internal static class LoadClearPatch { private static void Prefix() { TeamSave.ClearBeforeLoad(); } } [HarmonyPatch(typeof(StatsManager), "LoadGame")] internal static class LoadRestorePatch { private static void Postfix() { TeamSave.Restore(); } } [HarmonyPatch(typeof(StatsManager), "RunStartStats")] internal static class RunStartAccountsPatch { private static void Postfix() { TeamSave.ClearRun(); } } [HarmonyPatch(typeof(StatsManager), "ResetAllStats")] internal static class ResetAllAccountsPatch { private static void Postfix() { TeamSave.ClearRun(); } } [HarmonyPatch(typeof(StaticGrabObject), "GrabStarted")] internal static class ScreenFinishPatch { private static void Prefix(StaticGrabObject __instance) { if (!((Object)(object)RunManager.instance == (Object)null) && SemiFunc.RunIsLevel() && GameInternals.IsTruckScreen(((Component)__instance).gameObject) && TeamRound.Winner() != -1) { if (!TeamRound.CanClose(out var reason)) { Plugin.Log("Round finish refused at the truck screen: " + reason); return; } Plugin.Log("Round finish requested at the truck screen"); TeamNetworking.Instance?.RequestRoundClose(); } } } [HarmonyPatch(typeof(ShopManager), "ShopCheck")] internal static class ShopCheckPatch { private static void Postfix(ShopManager __instance) { ShopPurchases.TagItemsInCheck(__instance); } } [HarmonyPatch(typeof(ExtractionPoint), "DestroyTheFirstPhysObjectsInShopList")] internal static class ShopTubePurchasePatch { private static string lastRefusal; private static bool Prefix() { if (!SemiFunc.RunIsShop()) { return true; } if (!ShopPurchases.AllowTubePurchase(out var reason)) { if (reason != lastRefusal) { lastRefusal = reason; Plugin.Log("Shop purchase refused: " + reason); } return false; } lastRefusal = null; return true; } private static void Postfix() { ShopPurchases.RecordTubePurchase(); } } [HarmonyPatch(typeof(ExtractionPoint), "DestroyAllPhysObjectsInShoppingList")] internal static class ShopBulkPurchasePatch { private static bool Prefix() { if (!SemiFunc.RunIsShop()) { return true; } ShopPurchases.AllowBulkPurchase(); return true; } private static void Postfix() { if (SemiFunc.RunIsShop()) { ShopPurchases.RecordBulkPurchase(); } } } [HarmonyPatch(typeof(ExtractionPoint), "SpawnTaxReturn")] internal static class SurplusSplitPatch { private static bool Prefix(ExtractionPoint __instance) { return TeamSurplus.Spawn(__instance); } } [HarmonyPatch(typeof(UpgradeStand), "StatePressSucceed")] internal static class UpgradeStandPatch { private static void Postfix(UpgradeStand __instance) { if (GameInternals.RerollPressStarted(__instance)) { ShopPurchases.RecordReroll(__instance); } } } [HarmonyPatch(typeof(ValuableObject), "AddToDollarHaulList")] internal static class ValuableHaulPatch { private static void Postfix(ValuableObject __instance) { TeamController.Instance?.PinDeliverer(__instance); } } [HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "Break")] internal static class ValuableProtectionPatch { private static bool Prefix(PhysGrabObjectImpactDetector __instance) { return TeamOwnership.AllowsDamage(__instance); } } [HarmonyPatch(typeof(ValuableDirector), "SetupHost")] internal static class ValuableSpawnPatch { private const BindingFlags AnyField = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly string[] CurveFields = new string[16] { "totalMaxValueCurve1", "totalMaxValueCurve2", "tinyMaxAmountCurve1", "tinyMaxAmountCurve2", "smallMaxAmountCurve1", "smallMaxAmountCurve2", "mediumMaxAmountCurve1", "mediumMaxAmountCurve2", "bigMaxAmountCurve1", "bigMaxAmountCurve2", "wideMaxAmountCurve1", "wideMaxAmountCurve2", "tallMaxAmountCurve1", "tallMaxAmountCurve2", "veryTallMaxAmountCurve1", "veryTallMaxAmountCurve2" }; private static readonly Dictionary<string, FieldInfo> fields = new Dictionary<string, FieldInfo>(); private static int snapshotForId; private static Dictionary<string, Keyframe[]> snapshotCurves; private static int snapshotTotalMaxAmount; private static void Prefix(ValuableDirector __instance) { if (!((Object)(object)__instance == (Object)null) && TeamNetworking.IsMasterSession) { float num = ((ConfigSettings.ValuableMultiplier != null) ? ConfigSettings.ValuableMultiplier.Value : 2f); if (num < 1f) { num = 1f; } int instanceID = ((Object)__instance).GetInstanceID(); if (snapshotCurves == null || snapshotForId != instanceID) { TakeSnapshot(__instance, instanceID); } else { RestoreSnapshot(__instance); } ApplyMultiplier(__instance, num); Plugin.Log($"Valuable spawn x{num}: totalMaxAmount {snapshotTotalMaxAmount} -> {CurrentTotalMaxAmount(__instance)}"); } } private static FieldInfo Field(string name) { if (!fields.TryGetValue(name, out var value)) { value = typeof(ValuableDirector).GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); fields[name] = value; } return value; } private static void TakeSnapshot(ValuableDirector director, int id) { snapshotForId = id; snapshotCurves = new Dictionary<string, Keyframe[]>(); string[] curveFields = CurveFields; foreach (string text in curveFields) { object? obj = Field(text)?.GetValue(director); AnimationCurve val = (AnimationCurve)((obj is AnimationCurve) ? obj : null); snapshotCurves[text] = ((val != null) ? val.keys : null); } snapshotTotalMaxAmount = CurrentTotalMaxAmount(director); } private static void RestoreSnapshot(ValuableDirector director) { string[] curveFields = CurveFields; foreach (string text in curveFields) { if (snapshotCurves.TryGetValue(text, out var value) && value != null) { object? obj = Field(text)?.GetValue(director); AnimationCurve val = (AnimationCurve)((obj is AnimationCurve) ? obj : null); if (val != null) { val.keys = value; } } } Field("totalMaxAmount")?.SetValue(director, snapshotTotalMaxAmount); } private static void ApplyMultiplier(ValuableDirector director, float multiplier) { //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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) int num = 0; string[] curveFields = CurveFields; foreach (string text in curveFields) { if (snapshotCurves.TryGetValue(text, out var value) && value != null) { Keyframe[] array = (Keyframe[])(object)new Keyframe[value.Length]; for (int j = 0; j < value.Length; j++) { Keyframe val = value[j]; ((Keyframe)(ref val)).value = ((Keyframe)(ref val)).value * multiplier; ((Keyframe)(ref val)).inTangent = ((Keyframe)(ref val)).inTangent * multiplier; ((Keyframe)(ref val)).outTangent = ((Keyframe)(ref val)).outTangent * multiplier; array[j] = val; } object? obj = Field(text)?.GetValue(director); AnimationCurve val2 = (AnimationCurve)((obj is AnimationCurve) ? obj : null); if (val2 != null) { val2.keys = array; num++; } } } Field("totalMaxAmount")?.SetValue(director, (int)((float)snapshotTotalMaxAmount * multiplier)); if (num != CurveFields.Length) { Plugin.Log($"Valuable spawn: only {num}/{CurveFields.Length} curves found; the value/size caps may not have doubled"); } } private static int CurrentTotalMaxAmount(ValuableDirector director) { object obj = Field("totalMaxAmount")?.GetValue(director); if (obj is int) { return (int)obj; } return 0; } } [BepInPlugin("cacus.TeamWars", "TeamWars", "0.8.79")] [BepInProcess("REPO.exe")] public class Plugin : BaseUnityPlugin { private static ManualLogSource logSource; public static Plugin Instance { get; private set; } private void Awake() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) Instance = this; ConfigSettings.Bind(((BaseUnityPlugin)this).Config); logSource = Logger.CreateLogSource("TeamWars"); new Harmony("cacus.TeamWars").PatchAll(); GameObject val = new GameObject("TeamWars"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; val.AddComponent<TeamController>(); val.AddComponent<TeamNetworking>(); val.AddComponent<TeamHud>(); Log("TeamWars v0.8.79 loaded"); } public static void Log(string message) { logSource.LogInfo((object)message); } public static void LogVerbose(string message) { if (ConfigSettings.VerboseLogs != null && ConfigSettings.VerboseLogs.Value) { logSource.LogInfo((object)message); } } } internal static class PluginInfo { public const string PLUGIN_GUID = "cacus.TeamWars"; public const string PLUGIN_NAME = "TeamWars"; public const string PLUGIN_VERSION = "0.8.79"; } internal static class ShopPurchases { private sealed class BulkItem { public readonly ItemAttributes Item; public readonly int Team; public readonly int Price; public readonly string Name; public BulkItem(ItemAttributes item, int team, int price, string name) { Item = item; Team = team; Price = price; Name = name; } } public const int DollarsPerMoneyUnit = 1000; private static ItemAttributes pendingItem; private static int pendingTeam = -1; private static int pendingPrice; private static string pendingName; private static readonly HashSet<int> shopPinned = new HashSet<int>(); private static HashSet<int> lastInCheck = new HashSet<int>(); private static float nextTag; private static List<BulkItem> pendingBulk; private static List<ItemAttributes> withheldBulk; public static void TagItemsInCheck(ShopManager shop) { if (!TeamNetworking.IsMasterSession || Time.time < nextTag) { return; } nextTag = Time.time + 0.5f; HashSet<int> hashSet = new HashSet<int>(); foreach (ItemAttributes item in ItemsInCheck(shop)) { int num = ViewIdOf(item); if (num != 0) { hashSet.Add(num); int num2 = HolderTeam(item); if (num2 != -1) { PinShop(item, num, num2); } } } ClearLeavers(hashSet); lastInCheck = hashSet; } public static void MirrorPot() { if (TeamNetworking.IsMasterSession && SemiFunc.RunIsShop()) { int num = (TeamState.Purse(0) + TeamState.Purse(1)) / 1000; if (num < 0) { num = 0; } int num2 = GameInternals.RunCurrency(); if (num2 < num) { GameInternals.SetRunCurrency(num); Plugin.Log($"Shop pot mirrored: {num2} -> {num} (team purses)"); } } } private static void EnsurePot(int units, string what) { int num = GameInternals.RunCurrency(); if (num < units) { GameInternals.SetRunCurrency(units); Plugin.Log($"Shop pot topped up: {num} -> {units} to cover {what}"); } } public static void ResetAttribution() { shopPinned.Clear(); lastInCheck = new HashSet<int>(); } private static void ClearLeavers(HashSet<int> seen) { foreach (int item in lastInCheck) { if (!seen.Contains(item) && shopPinned.Remove(item)) { TeamOwnership.RestoreShopTint(item); if (!((Object)(object)PhotonView.Find(item) == (Object)null)) { TeamOwnership.Set(item, -1); TeamNetworking.Instance?.BroadcastOwnership(new int[2] { item, -1 }, 2); Plugin.Log("Shop check: an item left the check; attribution reset"); } } } } private static int UnambiguousTeam() { int num = -1; foreach (int item in TeamState.AllActors()) { int team = TeamState.GetTeam(item); if (team == -1) { return -1; } if (num == -1) { num = team; } else if (num != team) { return -1; } } return num; } public static void AllowBulkPurchase() { pendingBulk = new List<BulkItem>(); withheldBulk = new List<ItemAttributes>(); if (!TeamNetworking.IsMasterSession) { return; } List<ItemAttributes> list = GameInternals.ShoppingList(ShopManager.instance); if (list == null) { return; } ItemAttributes[] array = list.ToArray(); foreach (ItemAttributes val in array) { if ((Object)(object)val == (Object)null) { continue; } int num = Placer(val); if (num == -1) { withheldBulk.Add(val); Plugin.Log("Shop bulk withheld: " + ((Object)val).name + " has no team attached yet (grab it again to attribute)"); continue; } int num2 = GameInternals.ItemValue(val) * 1000; if (TeamState.Purse(num) >= num2) { pendingBulk.Add(new BulkItem(val, num, num2, GameInternals.ItemName(val))); continue; } withheldBulk.Add(val); Plugin.Log($"Shop bulk refused: {TeamLabel(num)} cannot pay {((Object)val).name} ({num2}); purse is {TeamState.Purse(num)} (left in the check)"); } int num3 = 0; foreach (BulkItem item in pendingBulk) { num3 += item.Price / 1000; } EnsurePot(num3, "the authorized bulk purchase"); foreach (ItemAttributes item2 in withheldBulk) { list.Remove(item2); } } public static void RecordBulkPurchase() { List<BulkItem> list = pendingBulk; List<ItemAttributes> list2 = withheldBulk; pendingBulk = null; withheldBulk = null; if (!TeamNetworking.IsMasterSession || list == null) { return; } List<ItemAttributes> list3 = GameInternals.ShoppingList(ShopManager.instance); if (list3 != null && list2 != null) { foreach (ItemAttributes item in list2) { if ((Object)(object)item != (Object)null && !list3.Contains(item)) { list3.Add(item); } } } int num = 0; foreach (BulkItem item2 in list) { if ((Object)(object)item2.Item == (Object)null || list3 == null || !list3.Contains(item2.Item)) { TeamState.Spent[item2.Team] += item2.Price; TeamState.AddPurchased(item2.Name, item2.Team, 1); Plugin.Log($"Shop purchase: {item2.Name} (${item2.Price}) -> {TeamLabel(item2.Team)}"); num++; } } if (num > 0) { TeamNetworking.Instance?.BroadcastTally(); } } public static bool AllowTubePurchase(out string reason) { reason = null; if (!TeamNetworking.IsMasterSession) { return true; } ItemAttributes val = FirstShoppingItem(); if ((Object)(object)val == (Object)null) { return true; } if ((Object)(object)((Component)val).GetComponentInParent<PhysGrabObject>() == (Object)null) { return true; } int num = Placer(val); if (num == -1) { reason = ((Object)val).name + " has no team attached yet (grab it again to attribute)"; return false; } int num2 = GameInternals.ItemValue(val) * 1000; if (TeamState.Purse(num) >= num2) { pendingItem = val; pendingTeam = num; pendingPrice = num2; pendingName = GameInternals.ItemName(val); EnsurePot(GameInternals.ItemValue(val), ((Object)val).name); return true; } reason = $"{TeamLabel(num)} cannot pay {((Object)val).name} ({num2}); purse is {TeamState.Purse(num)}"; return false; } public static void RecordTubePurchase() { ItemAttributes val = pendingItem; int num = pendingTeam; int num2 = pendingPrice; string text = pendingName; pendingItem = null; pendingName = null; if (TeamNetworking.IsMasterSession && !((Object)(object)val == (Object)null)) { List<ItemAttributes> list = GameInternals.ShoppingList(ShopManager.instance); if (list != null && list.Contains(val)) { Plugin.Log("Shop tube: " + text + " was not consumed; not booked"); return; } TeamState.Spent[num] += num2; TeamState.AddPurchased(text, num, 1); Plugin.Log($"Shop purchase: {text} (${num2}) -> {TeamLabel(num)}"); TeamNetworking.Instance?.BroadcastTally(); } } public static void RecordReroll(UpgradeStand stand) { if (!TeamNetworking.IsMasterSession || (Object)(object)stand == (Object)null) { return; } int num = GameInternals.RerollCost(stand) * 1000; if (num > 0) { int num2 = GameInternals.ButtonGrabberTeam(stand); if (num2 == -1) { num2 = UnambiguousTeam(); } if (num2 == -1) { Plugin.Log($"Shop reroll (${num}) has no presser attached; not booked"); return; } TeamState.Spent[num2] += num; Plugin.Log($"Shop reroll (${num}) -> {TeamLabel(num2)}"); TeamNetworking.Instance?.BroadcastTally(); } } private static ItemAttributes FirstShoppingItem() { List<ItemAttributes> list = GameInternals.ShoppingList(ShopManager.instance); if (list == null || list.Count == 0) { return null; } ItemAttributes val = list[0]; if (!((Object)(object)val != (Object)null)) { return null; } return val; } internal static void PinShop(ItemAttributes item, int viewId, int team) { if (team == -1 || viewId == 0) { return; } PhysGrabObject componentInParent = ((Component)item).GetComponentInParent<PhysGrabObject>(); if (!((Object)(object)componentInParent == (Object)null)) { int num = TeamOwnership.TeamOf(componentInParent); if (num == team) { shopPinned.Add(viewId); } else if (num == -1 || shopPinned.Contains(viewId)) { TeamOwnership.Set(viewId, team); TeamOwnership.TintShop(((Component)componentInParent).gameObject, viewId, team); shopPinned.Add(viewId); TeamNetworking.Instance?.BroadcastOwnership(new int[2] { viewId, team }, 1); Plugin.Log("Shop check: " + ((Object)item).name + " belongs to " + TeamLabel(team) + " (last placed)"); } } } private static int ViewIdOf(ItemAttributes item) { PhysGrabObject val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponentInParent<PhysGrabObject>() : null); PhotonView val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<PhotonView>() : null); if (!((Object)(object)val2 != (Object)null)) { return 0; } return val2.ViewID; } private static int HolderTeam(ItemAttributes item) { return GameInternals.DelivererTeam(((Object)(object)item != (Object)null) ? ((Component)item).GetComponentInParent<PhysGrabObject>() : null); } private static int Placer(ItemAttributes item) { PhysGrabObject componentInParent = ((Component)item).GetComponentInParent<PhysGrabObject>(); int num = TeamOwnership.TeamOf(componentInParent); if (num != -1) { return num; } PlayerAvatar val = GameInternals.Deliverer(componentInParent); if ((Object)(object)val == (Object)null || (Object)(object)val.photonView == (Object)null) { return -1; } return TeamState.GetTeam(val.photonView.OwnerActorNr); } private static List<ItemAttributes> ItemsInCheck(ShopManager shop) { List<ItemAttributes> list = new List<ItemAttributes>(); List<ItemAttributes> list2 = GameInternals.ShoppingList(shop); if (list2 == null) { return list; } foreach (ItemAttributes item in list2) { RoomVolumeCheck val = GameInternals.RoomCheck(item); if (!((Object)(object)item == (Object)null) && !((Object)(object)val == (Object)null)) { val.CheckSet(); if (GameInternals.IsInExtractionPoint(val)) { list.Add(item); } } } return list; } private static string TeamLabel(int team) { return team switch { 1 => "Azul", 0 => "Rojo", _ => "sin equipo", }; } } internal static class TeamCarts { private const string CartItemName = "Item Cart Medium"; private static readonly List<int> cartOwnership = new List<int>(); private static readonly Dictionary<int, GameObject> cartObjects = new Dictionary<int, GameObject>(); private static readonly Dictionary<string, bool> lastGateState = new Dictionary<string, bool>(); private static readonly HashSet<int> wipeSkipLogged = new HashSet<int>(); public static int[] CartOwnershipSnapshot() { return cartOwnership.ToArray(); } public static GameObject MainCartObject(int team) { if (cartObjects.TryGetValue(team, out var value) && (Object)(object)value != (Object)null) { return value; } for (int i = 0; i + 1 < cartOwnership.Count; i += 2) { if (cartOwnership[i + 1] == team) { PhotonView val = PhotonView.Find(cartOwnership[i]); if (!((Object)(object)val != (Object)null)) { return null; } return ((Component)val).gameObject; } } return null; } public static List<int> CartsMissingAt(ExtractionPoint point) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) List<int> list = new List<int>(); if ((Object)(object)point == (Object)null) { return list; } float num = ((ConfigSettings.CartRadius != null) ? ConfigSettings.CartRadius.Value : 6f); int[] array = new int[2] { 0, 1 }; for (int i = 0; i < array.Length; i++) { int num2 = array[i]; string text = ((num2 == 0) ? "Rojo" : "Azul"); GameObject val = MainCartObject(num2); string text2 = "destroyed or lost"; bool flag = false; if ((Object)(object)val != (Object)null) { float num3 = Vector3.Distance(val.transform.position, ((Component)point).transform.position); RoomVolumeCheck component = val.GetComponent<RoomVolumeCheck>(); bool flag2 = (Object)(object)component == (Object)null || GameInternals.IsInExtractionPoint(component); flag = num3 <= num && flag2; text2 = string.Format("{0:F1}m of {1:F0}m, roomZone={2}", num3, num, ((Object)(object)component == (Object)null) ? "n/a" : flag2.ToString()); } string key = ((Object)point).GetInstanceID() + "x" + num2; if (lastGateState.TryGetValue(key, out var value) && value != flag) { Plugin.Log("Cart gate: " + text + " cart " + (flag ? "entered" : "left") + " " + ((Object)point).name + " (" + text2 + ")"); } lastGateState[key] = flag; if (!flag) { list.Add(num2); } } return list; } public static void SweepCartContents() { if (!TeamNetworking.IsMasterSession) { return; } for (int i = 0; i + 1 < cartOwnership.Count; i += 2) { PhotonView val = PhotonView.Find(cartOwnership[i]); if ((Object)(object)val == (Object)null) { continue; } PhysGrabCart component = ((Component)val).gameObject.GetComponent<PhysGrabCart>(); if ((Object)(object)component == (Object)null) { continue; } List<PhysGrabObject> list = GameInternals.CartItems(component); if (list == null) { continue; } int num = cartOwnership[i + 1]; foreach (PhysGrabObject item in list) { if (!((Object)(object)item == (Object)null) && TeamOwnership.TeamOf(item) == -1) { TeamHaul.Claim(((Component)item).gameObject, num); Plugin.Log("Cart claim (sweep): " + ((Object)item).name + " rides the " + ((num == 0) ? "Rojo" : "Azul") + " cart"); } } } } public static void Spawn() { //IL_0091: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) if (!TeamNetworking.IsMasterSession) { return; } cartOwnership.Clear(); cartObjects.Clear(); wipeSkipLogged.Clear(); lastGateState.Clear(); TruckHealer instance = TruckHealer.instance; if ((Object)(object)instance == (Object)null) { Plugin.Log("Team carts: no truck in this level; no team carts spawned"); return; } Transform transform = ((Component)instance).transform; Item val = FindCartItem(); if ((Object)(object)val == (Object)null) { Plugin.Log("Team carts: 'Item Cart Medium' not found in StatsManager.itemDictionary; no team carts this level"); return; } int[] array = new int[2] { 0, 1 }; int[] array2 = new int[array.Length * 2]; for (int i = 0; i < array.Length; i++) { Quaternion rotation; Vector3 position = BasePosition(transform, array[i], out rotation); GameObject val2 = InstantiateCart(val, position, rotation); if ((Object)(object)val2 == (Object)null) { Plugin.Log("Team carts: failed to spawn the " + ((array[i] == 0) ? "red" : "blue") + " cart"); continue; } Plugin.Log(string.Format("Team carts: {0} cart at {1}", (array[i] == 0) ? "red" : "blue", val2.transform.position)); cartObjects[array[i]] = val2; PhotonView component = val2.GetComponent<PhotonView>(); array2[i * 2] = (((Object)(object)component != (Object)null) ? component.ViewID : 0); array2[i * 2 + 1] = array[i]; if ((Object)(object)component != (Object)null && component.ViewID != 0) { cartOwnership.Add(component.ViewID); cartOwnership.Add(array[i]); TeamOwnership.Set(component.ViewID, array[i]); } else { Plugin.Log("Team carts: " + ((array[i] == 0) ? "red" : "blue") + " cart has no network view; skipping ownership"); } TeamOwnership.Tint(val2, array[i]); LogCartComponents(val2, array[i]); } if (PhotonNetwork.CurrentRoom != null) { TeamNetworking.Instance.BroadcastOwnership(array2, 1); Plugin.Log($"Team carts spawned at {transform.position}"); } } public static void EnsureCarts() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (!TeamNetworking.IsMasterSession || (Object)(object)RunManager.instance == (Object)null || !SemiFunc.RunIsLevel() || !TeamState.RoundActive) { return; } int[] array = new int[2] { 0, 1 }; foreach (int num in array) { string text = ((num == 0) ? "red" : "blue"); GameObject val = MainCartObject(num); if ((Object)(object)val != (Object)null && val.transform.position.y > -20f) { continue; } if (TeamRound.LivingCount(num) <= 0) { if (wipeSkipLogged.Add(num)) { Plugin.Log("Team carts: " + text + " cart gone but nobody alive on the team, no respawn (gate excused)"); } continue; } if ((Object)(object)val != (Object)null) { PhotonView component = val.GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && component.ViewID != 0 && SemiFunc.IsMultiplayer()) { PhotonNetwork.Destroy(component); } else { Object.Destroy((Object)(object)val); } } Respawn(num); } } private static Vector3 BasePosition(Transform truck, int team, out Quaternion rotation) { //IL_0006: 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) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_0033: 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_0047: 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_005b: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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) Transform obj = ResolveAnchor(truck); Vector3 val = Vector3.Cross(Vector3.up, truck.right); val.y = 0f; val = ((((Vector3)(ref val)).sqrMagnitude < 0.01f) ? Vector3.right : ((Vector3)(ref val)).normalized); rotation = Quaternion.Euler(0f, truck.eulerAngles.y, 0f); float num = ((team == 0) ? 1f : (-1f)); return obj.position + val * (1.5f * num) + Vector3.up * 0.2f; } private static void Respawn(int team) { //IL_0064: 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_0154: Unknown result type (might be due to invalid IL or missing references) string text = ((team == 0) ? "red" : "blue"); TruckHealer instance = TruckHealer.instance; if ((Object)(object)instance == (Object)null) { Plugin.Log("Team carts: no truck, cannot respawn the " + text + " cart"); return; } Item val = FindCartItem(); if ((Object)(object)val == (Object)null) { Plugin.Log("Team carts: 'Item Cart Medium' not found, cannot respawn the " + text + " cart"); return; } Quaternion rotation; GameObject val2 = InstantiateCart(val, BasePosition(((Component)instance).transform, team, out rotation), rotation); if ((Object)(object)val2 == (Object)null) { Plugin.Log("Team carts: failed to respawn the " + text + " cart"); return; } cartObjects[team] = val2; PhotonView component = val2.GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && component.ViewID != 0) { bool flag = false; for (int i = 0; i + 1 < cartOwnership.Count; i += 2) { if (cartOwnership[i + 1] == team) { cartOwnership[i] = component.ViewID; flag = true; break; } } if (!flag) { cartOwnership.Add(component.ViewID); cartOwnership.Add(team); } TeamOwnership.Set(component.ViewID, team); } TeamOwnership.Tint(val2, team); TeamNetworking.Instance?.BroadcastOwnership(CartOwnershipSnapshot(), 1); Plugin.Log($"Team carts: {text} cart respawned at base ({val2.transform.position})"); } public static void ApplyOwnership(int[] payload) { if (payload == null || payload.Length < 2) { return; } int num = payload[0]; for (int i = 1; i + 1 < payload.Length; i += 2) { int num2 = payload[i]; int team = payload[i + 1]; TeamOwnership.Set(num2, team); switch (num) { case 1: { PhotonView val = PhotonView.Find(num2); if ((Object)(object)val != (Object)null) { TeamOwnership.TintShop(((Component)val).gameObject, num2, team); } break; } case 2: TeamOwnership.RestoreShopTint(num2); break; } } } private static void LogCartComponents(GameObject cart, int team) { if (!((Object)(object)cart == (Object)null)) { List<string> list = new List<string>(); Component[] components = cart.GetComponents<Component>(); foreach (Component val in components) { list.Add(((Object)(object)val == (Object)null) ? "null" : ((object)val).GetType().FullName); } Plugin.LogVerbose("Cart components (" + ((team == 0) ? "Rojo" : "Azul") + "): " + string.Join(", ", list.ToArray())); } } private static GameObject InstantiateCart(Item item, Vector3 position, Quaternion rotation) { //IL_0040: 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_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) if (SemiFunc.IsMultiplayer()) { string text = GameInternals.ItemResourcePath(item); if (string.IsNullOrEmpty(text)) { Plugin.Log("Team carts: the cart item has no resource path"); return null; } return PhotonNetwork.InstantiateRoomObject(text, position, rotation, (byte)0, (object[])null); } GameObject val = GameInternals.ItemPrefab(item); if (!((Object)(object)val != (Object)null)) { return null; } return Object.Instantiate<GameObject>(val, position, rotation); } private static Transform ResolveAnchor(Transform truck) { string[] array = new string[3] { "Truck Safety Spawn Point", "Truck Interior", "Truck additional interior" }; foreach (string text in array) { GameObject val = GameObject.Find(text); if ((Object)(object)val != (Object)null) { Plugin.Log("Team carts: anchored to '" + text + "'"); return val.transform; } } Plugin.Log("Team carts: no truck interior anchor found, anchoring to the truck itself"); return truck; } private static Item FindCartItem() { StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null || instance.itemDictionary == null) { return null; } if (instance.itemDictionary.TryGetValue("Item Cart Medium", out var value) && (Object)(object)value != (Object)null) { return value; } foreach (Item value2 in instance.itemDictionary.Values) { if ((Object)(object)value2 != (Object)null && ((Object)value2).name == "Item Cart Medium") { return value2; } } return null; } } internal static class TeamCheckNotice { private static readonly Dictionary<ExtractionPoint, TextMeshPro> labels = new Dictionary<ExtractionPoint, TextMeshPro>(); private static readonly Dictionary<ExtractionPoint, string> lastTexts = new Dictionary<ExtractionPoint, string>(); private static float nextRefresh; public static void Poll() { //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<ExtractionPoint, TextMeshPro> item in new List<KeyValuePair<ExtractionPoint, TextMeshPro>>(labels)) { if ((Object)(object)item.Key == (Object)null || (Object)(object)item.Value == (Object)null) { labels.Remove(item.Key); lastTexts.Remove(item.Key); } else { FaceAway(item.Value); } } if (Time.time < nextRefresh) { return; } nextRefresh = Time.time + 0.25f; if (!SemiFunc.RunIsLevel()) { Clear(); return; } HashSet<ExtractionPoint> hashSet = new HashSet<ExtractionPoint>(); ExtractionPoint[] array = Object.FindObjectsOfType<ExtractionPoint>(); foreach (ExtractionPoint val in array) { if ((Object)(object)val == (Object)null) { continue; } hashSet.Add(val); List<int> list = TeamCarts.CartsMissingAt(val); list.RemoveAll(TeamRound.TeamWiped); string text = GameInternals.ExtractionStateName(val); int num; switch (text) { default: num = ((text == "Extracting") ? 1 : 0); break; case "Active": case "Warning": case "Surplus": num = 1; break; } bool flag = (byte)num != 0; if (list.Count == 0 || GameInternals.CheckHaul(val) <= 0 || !flag) { Hide(val); continue; } string text2 = string.Join(" Y ", list.ConvertAll((int team) => (team != 0) ? "AZUL" : "ROJO").ToArray()); string text3 = ((list.Count > 1) ? "FALTAN CARROS " : "FALTA CARRO ") + text2; TextMeshPro val2 = Ensure(val); val2.transform.position = ((Component)val).transform.position + Vector3.up * 3.2f; if (!lastTexts.TryGetValue(val, out var value) || value != text3) { lastTexts[val] = text3; ((TMP_Text)val2).text = text3; } if (!((Component)val2).gameObject.activeSelf) { ((Component)val2).gameObject.SetActive(true); } } foreach (KeyValuePair<ExtractionPoint, TextMeshPro> item2 in new List<KeyValuePair<ExtractionPoint, TextMeshPro>>(labels)) { if (!hashSet.Contains(item2.Key)) { Hide(item2.Key); } } } public static void Clear() { foreach (KeyValuePair<ExtractionPoint, TextMeshPro> label in labels) { if ((Object)(object)label.Value != (Object)null) { Object.Destroy((Object)(object)((Component)label.Value).gameObject); } } labels.Clear(); lastTexts.Clear(); } private static void Hide(ExtractionPoint point) { if (labels.TryGetValue(point, out var value) && (Object)(object)value != (Object)null && ((Component)value).gameObject.activeSelf) { ((Component)value).gameObject.SetActive(false); } } private static TextMeshPro Ensure(ExtractionPoint point) { //IL_001f: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if (labels.TryGetValue(point, out var value) && (Object)(object)value != (Object)null) { return value; } GameObject val = new GameObject("TeamWarsCheckNotice"); TextMeshPro val2 = val.AddComponent<TextMeshPro>(); ((TMP_Text)val2).alignment = (TextAlignmentOptions)514; ((TMP_Text)val2).fontSize = 48f; ((Graphic)val2).color = new Color(1f, 0.85f, 0.2f); val.transform.localScale = Vector3.one * 0.04f; val.SetActive(false); labels[point] = val2; return val2; } private static void FaceAway(TextMeshPro label) { //IL_0023: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) if (!((Component)label).gameObject.activeSelf) { return; } Vector3? val = TeamReviveTerminal.LocalEyes(); if (val.HasValue) { Vector3 val2 = label.transform.position - val.Value; if (((Vector3)(ref val2)).sqrMagnitude > 0.001f) { label.transform.rotation = Quaternion.LookRotation(val2); } return; } Camera main = Camera.main; if ((Object)(object)main != (Object)null) { Vector3 val3 = label.transform.position - ((Component)main).transform.position; if (((Vector3)(ref val3)).sqrMagnitude > 0.001f) { label.transform.rotation = Quaternion.LookRotation(val3); } } } } public class TeamController : MonoBehaviour { private const float ZoneAutoBalanceSeconds = 20f; private Level lastLevel; private float lobbyStartTime; private float nextAutoBalance; private float nextHaulTrack; private float nextLevelTotalCheck; private float nextExtractionRetry; private float nextShopTag; private float nextOwnershipReconcile; private float nextResync; public static TeamController Instance { get; private set; } private void Awake() { Instance = this; } private void Update() { RunManager instance = RunManager.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.levelCurrent == (Object)null || (Object)(object)GameDirector.instance == (Object)null) { return; } if (InLobby()) { CloseBehind(); UpdateLobby(); return; } lobbyStartTime = 0f; if (SemiFunc.RunIsLevel()) { UpdateLevel(instance); return; } CloseBehind(); if (!SemiFunc.RunIsShop()) { return; } PeriodicResync(); if (Time.time >= nextShopTag) { nextShopTag = Time.time + 1f; if ((Object)(object)ShopManager.instance != (Object)null) { ShopPurchases.TagItemsInCheck(ShopManager.instance); } ShopPurchases.MirrorPot(); } } private void CloseBehind() { if (!((Object)(object)lastLevel == (Object)null)) { if (TeamState.RoundActive) { TeamRound.Close(); } lastLevel = null; TeamState.RoundActive = false; TeamHaul.Reset(); TeamOwnership.Reset(); ShopPurchases.ResetAttribution(); } } private void UpdateLobby() { PeriodicResync(); if (Time.time >= nextOwnershipReconcile) { nextOwnershipReconcile = Time.time + 5f; TeamOwnership.ReconcilePurchases(); } if (lobbyStartTime <= 0f) { lobbyStartTime = Time.time; Plugin.Log("Lobby entered: players define teams from the lobby panel"); } if (TeamNetworking.IsMasterSession && !(Time.time - lobbyStartTime < 20f) && !(Time.time < nextAutoBalance)) { nextAutoBalance = Time.time + 3f; IReadOnlyList<int> readOnlyList = TeamState.AutoBalanceUnassigned(); if (readOnlyList.Count > 0) { Plugin.Log($"Lobby auto-balance assigned {readOnlyList.Count} player(s)"); TeamNetworking.Instance.BroadcastRoster(); } } } private void UpdateLevel(RunManager runManager) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Invalid comparison between Unknown and I4 //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 PeriodicResync(); TeamRevive.PollKey(); TeamReviveTerminal.Ensure(); gameState currentState = GameDirector.instance.currentState; if ((int)currentState == 5 || (int)currentState == 3 || (int)currentState == 4) { if (TeamState.RoundActive && (Object)(object)lastLevel == (Object)(object)runManager.levelCurrent) { TeamRound.Close(); } } else if ((int)currentState == 1 || (int)currentState == 2) { if ((Object)(object)runManager.levelCurrent != (Object)(object)lastLevel) { lastLevel = runManager.levelCurrent; SetupRound(); } if (Time.time >= nextHaulTrack) { nextHaulTrack = Time.time + 0.5f; TeamHaul.Track(); TeamCarts.SweepCartContents(); } if (Time.time >= nextExtractionRetry) { nextExtractionRetry = Time.time + 2f; CheckCartGatePatch.RetryHeldExtractions(); TeamCarts.EnsureCarts(); } TeamCheckNotice.Poll(); if (Time.time >= nextLevelTotalCheck) { nextLevelTotalCheck = Time.time + 10f; EnsureLevelTotal(); } if (Time.time >= nextOwnershipReconcile) { nextOwnershipReconcile = Time.time + 5f; TeamOwnership.ReconcilePurchases(); } } } private void PeriodicResync() { if (!(Time.time < nextResync)) { nextResync = Time.time + 10f; TeamNetworking.Instance?.BroadcastRoster(); TeamNetworking.Instance?.BroadcastOwnership(TeamOwnership.Snapshot(), 0); int[] array = TeamCarts.CartOwnershipSnapshot(); if (array.Length != 0) { TeamNetworking.Instance?.BroadcastOwnership(array, 1); } TeamNetworking.Instance?.BroadcastTally(); } } private void SetupRound() { TeamHaul.Reset(); TeamOwnership.Reset(); CheckCartGatePatch.ClearPending(); TeamCheckNotice.Clear(); ShopPurchases.ResetAttribution(); TeamMapFilter.Discover(); TeamMapFilter.ScanChecks(); TeamState.LevelTotal = ComputeLevelTotal(); TeamState.ResetRoundTallies(); TeamState.RoundActive = true; TeamRound.Arm(); if (TeamNetworking.IsMasterSession) { IReadOnlyList<int> readOnlyList = TeamState.AutoBalanceUnassigned(); if (readOnlyList.Count > 0) { Plugin.Log($"Level start: {readOnlyList.Count} unassigned player(s) auto-assigned"); } TeamState.StartPlayers[0] = TeamState.TeamCount(0); TeamState.StartPlayers[1] = TeamState.TeamCount(1); TeamState.RoundStartTotal = TeamState.LevelTotal; TeamNetworking.Instance.BroadcastRoster(); TeamNetworking.Instance.BroadcastTally(); TeamCarts.Spawn(); } Plugin.Log($"Round setup: level total value {TeamState.LevelTotal}"); } public void PinDeliverer(ValuableObject valuable) { TeamHaul.Pin(valuable); } private static int ComputeLevelTotal() { float num = 0f; ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>(); foreach (ValuableObject valuable in array) { num += GameInternals.ValuableOriginalValue(valuable); } return (int)num; } private static void EnsureLevelTotal() { if (TeamNetworking.IsMasterSession) { int num = ComputeLevelTotal(); if (num > TeamState.LevelTotal) { TeamState.LevelTotal = num; Plugin.Log($"Level total updated: {num} (valuables finished spawning)"); } } } private static bool InLobby() { if (!SemiFunc.RunIsLobby()) { return SemiFunc.RunIsLobbyMenu(); } return true; } } internal static class TeamHaul { private sealed class LimboEntry { public readonly int Team; public readonly int Value; public readonly string Name; public readonly float HeldSince; public LimboEntry(int team, Deliverable delivered) { Team = team; Value = delivered?.Value ?? 0; Name = ((delivered != null) ? delivered.Name : "?"); HeldSince = Time.time; } } private sealed class Deliverable { public readonly GameObject Object; public readonly int Value; public readonly string Name; public Deliverable(GameObject target, int value) { Object = target; Value = value; Name = (((Object)(object)target != (Object)null) ? ((Object)target).name : "?"); } } private static readonly Dictionary<int, int> creditByObject = new Dictionary<int, int>(); private static readonly Dictionary<int, Deliverable> pendingValue = new Dictionary<int, Deliverable>(); private static readonly HashSet<int> banked = new HashSet<int>(); private static readonly HashSet<int> warnedIds = new HashSet<int>(); private static readonly Dictionary<int, LimboEntry> limbo = new Dictionary<int, LimboEntry>(); private const float LimboSeconds = 10f; private static int lastCompletions; private static float lastCompletionTime = -1000f; public static void Reset() { if (TeamNetworking.IsMasterSession) { TeamState.Earned[0] += TeamState.Banked[0]; TeamState.Earned[1] += TeamState.Banked[1]; } creditByObject.Clear(); pendingValue.Clear(); banked.Clear(); warnedIds.Clear(); limbo.Clear(); lastCompletions = GameInternals.CompletedCheckCount(); lastCompletionTime = -1000f; TeamState.Haul[0] = 0; TeamState.Haul[1] = 0; TeamState.Banked[0] = 0; TeamState.Banked[1] = 0; } public static void Pin(ValuableObject valuable) { if (!((Object)(object)valuable == (Object)null) && TeamNetworking.IsMasterSession) { Credit(((Component)valuable).gameObject); Track(); } } public static void Track() { if (!TeamNetworking.IsMasterSession) { return; } RoundDirector instance = RoundDirector.instance; if ((Object)(object)instance == (Object)null) { return; } HashSet<int> hashSet = new HashSet<int>(); foreach (GameObject dollarHaul in instance.dollarHaulList) { if ((Object)(object)dollarHaul == (Object)null) { continue; } ValuableObject component = dollarHaul.GetComponent<ValuableObject>(); if (!((Object)(object)component == (Object)null) && GameInternals.IsInExtraction(component)) { int instanceID = ((Object)dollarHaul).GetInstanceID(); hashSet.Add(instanceID); if (Credit(dollarHaul) != -1) { pendingValue[instanceID] = new Deliverable(dollarHaul, (int)GameInternals.ValuableCurrentValue(component)); limbo.Remove(instanceID); } } } int num = GameInternals.CompletedCheckCount(); bool flag = num > lastCompletions; if (flag) { lastCompletions = num; lastCompletionTime = Time.time; } bool flag2 = flag || Time.time - lastCompletionTime <= 10f; foreach (KeyValuePair<int, Deliverable> item in new List<KeyValuePair<int, Deliverable>>(pendingValue)) { if (!hashSet.Contains(item.Key)) { pendingValue.Remove(item.Key); GameObject val = item.Value.Object; if ((Object)(object)val != (Object)null && (Object)(object)GameInternals.Deliverer(val.GetComponentInParent<PhysGrabObject>()) != (Object)null) { Plugin.Log("Haul credit dropped: " + ((Object)val).name + " was taken back out of the check"); } else if (!flag2) { limbo[item.Key] = new LimboEntry(TeamOf(item.Key), item.Value); Plugin.Log($"Haul waiting: {item.Value.Name} ({item.Value.Value}) left the check, banking on the next completion -> {TeamLabel(TeamOf(item.Key))}"); } else { Bank(item.Key, TeamOf(item.Key), item.Value.Value, item.Value.Name); } } } if (flag) { BankLimbo(); } ExpireLimbo(); int[] array = new int[2] { TeamState.Banked[0], TeamState.Banked[1] }; foreach (KeyValuePair<int, Deliverable> item2 in pendingValue) { array[TeamOf(item2.Key)] += item2.Value.Value; } bool num2 = array[0] != TeamState.Haul[0] || array[1] != TeamState.Haul[1]; TeamState.Haul[0] = array[0]; TeamState.Haul[1] = array[1]; int num3 = TeamState.Goal[0]; int num4 = TeamState.Goal[1]; TeamRound.RecalculateGoals(); if (num2 || TeamState.Goal[0] != num3 || TeamState.Goal[1] != num4) { TeamNetworking.Instance.BroadcastTally(); Plugin.LogVerbose($"Haul: Rojo {TeamState.Haul[0]}/{TeamState.Goal[0]} Azul {TeamState.Haul[1]}/{TeamState.Goal[1]}"); } } private static int TeamOf(int instanceId) { if (!creditByObject.TryGetValue(instanceId, out var value)) { return -1; } return value; } private static void Bank(int instanceId, int team, int value, string name) { if (team == 0 || team == 1) { TeamState.Banked[team] += value; banked.Add(instanceId); Plugin.Log($"Haul banked: {name} ({value}) -> {TeamLabel(team)}"); } } private static void BankLimbo() { if (limbo.Count == 0) { return; } int num = 0; int num2 = 0; foreach (LimboEntry value in limbo.Values) { if (value.Team == 0) { num += value.Value; } else if (value.Team == 1) { num2 += value.Value; } } foreach (int item in new List<int>(limbo.Keys)) { banked.Add(item); } limbo.Clear(); TeamState.Banked[0] += num; TeamState.Banked[1] += num2; Plugin.Log($"Check consumed: banked Rojo {num}, Azul {num2}"); } private static void ExpireLimbo() { foreach (KeyValuePair<int, LimboEntry> item in new List<KeyValuePair<int, LimboEntry>>(limbo)) { if (Time.time - item.Value.HeldSince > 10f) { limbo.Remove(item.Key); Plugin.Log($"Haul credit lost: {item.Value.Name} ({item.Value.Value}) broke inside the check before extraction; it stops counting"); } } } private static int Credit(GameObject hauledObject) { int instanceID = ((Object)hauledObject).GetInstanceID(); if (creditByObject.TryGetValue(instanceID, out var value)) { return value; } ValuableObject component = hauledObject.GetComponent<ValuableObject>(); PhysGrabObject componentInParent = hauledObject.GetComponentInParent<PhysGrabObject>(); int num = GameInternals.DelivererTeam(componentInParent); if (num == -1 && (Object)(object)component != (Object)null) { PhysGrabCart val = GameInternals.CurrentCart(component); PhysGrabObject val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<PhysGrabObject>() : null); num = TeamOwnership.TeamOf(val2); if (num == -1) { num = GameInternals.DelivererTeam(val2); } if (num != -1 && (Object)(object)val2 != (Object)null) { Plugin.Log("Haul credit: " + ((Object)hauledObject).name + " rides a cart -> " + TeamLabel(num)); } } if (num == -1) { WarnOnce(instanceID, ((Object)hauledObject).name); return -1; } creditByObject[instanceID] = num; Plugin.Log("Haul credit: " + ((Object)hauledObject).name + " -> " + TeamLabel(num) + " (" + GameInternals.PlayerName(GameInternals.Deliverer(componentInParent)) + ")"); Claim(hauledObject, num); return num; } public static void MarkIntoTeamCart(PhysGrabObjectImpactDetector detector) { if (!TeamNetworking.IsMasterSession || (Object)(object)detector == (Object)null) { return; } ValuableObject componentInParent = ((Component)detector).GetComponentInParent<ValuableObject>(); if ((Object)(object)componentInParent == (Object)null) { return; } PhysGrabObject componentInParent2 = ((Component)componentInParent).GetComponentInParent<PhysGrabObject>(); if (!((Object)(object)componentInParent2 == (Object)null) && TeamOwnership.TeamOf(componentInParent2) == -1) { PhysGrabCart val = GameInternals.CurrentCart(componentInParent); int num = TeamOwnership.TeamOf(((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<PhysGrabObject>() : null); if (num != -1) { creditByObject[((Object)((Component)componentInParent).gameObject).GetInstanceID()] = num; Claim(((Component)componentInParent).gameObject, num); Plugin.Log("Cart claim: " + ((Object)componentInParent).name + " entered the " + ((num == 0) ? "Rojo" : "Azul") + " cart"); } } } private static string TeamLabel(int team) { return team switch { 1 => "Azul", 0 => "Rojo", _ => "sin equipo", }; } public static int[] LevelContributions() { int[] array = new int[2] { TeamState.Banked[0], TeamState.Banked[1] }; foreach (KeyValuePair<int, Deliverable> item in pendingValue) { int num = TeamOf(item.Key); if (num == 0 || num == 1) { array[num] += item.Value.Value; } } foreach (LimboEntry value in limbo.Values) { if (value.Team == 0 || value.Team == 1) { array[value.Team] += value.Value; } } return array; } public static void Claim(GameObject target, int team) { if ((Object)(object)target == (Object)null || (team != 0 && team != 1)) { return; } int instanceID = ((Object)target).GetInstanceID(); if (!creditByObject.ContainsKey(instanceID)) { creditByObject[instanceID] = team; } PhysGrabObject componentInParent = target.GetComponentInParent<PhysGrabObject>(); if (!((Object)(object)componentInParent == (Object)null) && TeamOwnership.TeamOf(componentInParent) == -1) { PhotonView component = target.GetComponent<PhotonView>(); if (!((Object)(object)component == (Object)null)) { TeamOwnership.Set(component.ViewID, team); TeamNetworking.Instance?.BroadcastOwnership(new int[2] { component.ViewID, team }, 1); } } } private static void WarnOnce(int id, string name) { if (warnedIds.Add(id)) { Plugin.Log("Haul credit: " + name + " arrived with no team attached (nobody and no cart); it does not count yet"); } } } public class TeamHud : MonoBehaviour { private sealed class RowTeamButtons { public MenuPlayerListed row; public MenuPage page; public GameObject root; public Image redImage; public Image blueImage; public Color redBase; public Color blueBase; public Outline redFrame; public Outline blueFrame; public int actor = int.MinValue; } private Canvas rootCanvas; private TextMeshProUGUI scoreText; private Image tugBack; private Image tugRed; private Image tugBlue; private TextMeshProUGUI tugAscii; private TMP_FontAsset blockFont; private string lastTugAscii; private readonly List<TextMeshProUGUI> styledLabels = new List<TextMeshProUGUI>(); private bool styleAdopted; private string lastScore; private float nextRefresh; private float nextRowRefresh; private float nextStyleRetry; private bool vanillaCurrencyHidden; private float announcedOverlay = -1f; private readonly Dictionary<int, RowTeamButtons> rowButtons = new Dictionary<int, RowTeamButtons>(); private RowTeamButtons escSwitch; private void OnDestroy() { RestoreVanillaCurrency(); } private void Update() { RunManager instance = RunManager.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.levelCurrent == (Object)null || (Object)(object)GameDirector.instance == (Object)null) { SetScoreVisible(visible: false); RefreshTugBar(); ClearRowButtons(); RestoreVanillaCurrency(); return; } EnsureBuilt(); if (Time.time < TeamState.OverlayUntil) { SetScoreVisible(visible: false); RefreshTugBar(); ClearRowButtons(); MaybeAnnounceWinner(); RestoreVanillaCurrency(); } else if (SemiFunc.RunIsLobby() || SemiFunc.RunIsLobbyMenu()) { if (LobbyReady()) { SetScoreVisible(visible: true); RefreshLobbyStrip(); if (SemiFunc.RunIsLobbyMenu()) { RefreshRowButtons(); } else { ClearRowButtons(); } } else { SetScoreVisible(visible: false); RefreshTugBar(); ClearRowButtons(); } RefreshEscSwitch(); UpdateRowInteraction(); RestoreVanillaCurrency(); } else if (!SemiFunc.RunIsLevel() && !SemiFunc.RunIsShop()) { SetScoreVisible(visible: false); RefreshTugBar(); ClearRowButtons(); DestroyEscSwitch(); RestoreVanillaCurrency(); } else { SetScoreVisible(visible: true); ClearRowButtons(); RefreshEscSwitch(); UpdateRowInteraction(); SyncVanillaCurrency(); RefreshScore(); } } private void MaybeAnnounceWinner() { //IL_006d: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) int lastWinner = TeamState.LastWinner; if ((lastWinner != 0 && lastWinner != 1) || TeamState.OverlayUntil == announcedOverlay || (Object)(object)BigMessageUI.instance == (Object)null) { return; } announcedOverlay = TeamState.OverlayUntil; string text = ((lastWinner == 0) ? "GANA ROJO" : "GANA AZUL"); Color val = ((lastWinner == 0) ? new Color(1f, 0.3f, 0.25f) : new Color(0.3f, 0.6f, 1f)); try { SemiFunc.UIBigMessage(text, "{check}", 25f, val, Color.white); } catch (Exception ex) { Plugin.Log("Winner cartel failed: " + ex.Message); } } private void RefreshScore() { if (!(Time.time < nextRefresh)) { nextRefresh = Time.time + 0.25f; AdoptStyle(); string text = RoundCounterText(); if (text != lastScore) { lastScore = text; ((TMP_Text)scoreText).text = text; } RefreshTugBar(); } } private void RefreshLobbyStrip() { if (!(Time.time < nextRefresh)) { nextRefresh = Time.time + 0.5f; AdoptStyle(); string text = LobbyStripText(); if (text != lastScore) { lastScore = text; ((TMP_Text)scoreText).text = text; } RefreshTugBar(); } } private void RefreshRowButtons() { foreach (int item in new List<int>(rowButtons.Keys)) { if (!rowButtons.TryGetValue(item, out var value) || value == null) { rowButtons.Remove(item); continue; } if ((Object)(object)value.root == (Object)null || (Object)(object)value.row == (Object)null) { rowButtons.Remove(item); continue; } value.actor = RowActor(value.row); int team = TeamState.GetTeam(value.actor); ((Behaviour)value.redFrame).enabled = team == 0; ((Behaviour)value.blueFrame).enabled = team == 1; } if (rowButtons.Count > 0 && Time.time < nextRowRefresh) { return; } nextRowRefresh = Time.time + 0.5f; HashSet<int> hashSet = new HashSet<int>(); MenuPlayerListed[] array = Object.FindObjectsOfType<MenuPlayerListed>(); foreach (MenuPlayerListed val in array) { if ((Object)(object)val == (Object)null) { continue; } int instanceID = ((Object)val).GetInstanceID(); hashSet.Add(instanceID); if (!rowButtons.TryGetValue(instanceID, out var value2) || value2 == null || (Object)(object)value2.root == (Object)null) { value2 = BuildRowButtons(val); if (value2 == null) { continue; } rowButtons[instanceID] = value2; } value2.actor = RowActor(val); int team2 = TeamState.GetTeam(value2.actor); ((Behaviour)value2.redFrame).enabled = team2 == 0; ((Behaviour)value2.blueFrame).enabled = team2 == 1; } foreach (int item2 in new List<int>(rowButtons.Keys)) { if (!hashSet.Contains(item2)) { rowButtons.Remove(item2); } } } private void RefreshEscSwitch() { if (!GameInternals.EscMenuOpen() || (Object)(object)MenuPageEsc.instance == (Object)null) { DestroyEscSwitch(); return; } if (escSwitch == null || (Object)(object)escSwitch.root == (Object)null) { DestroyEscSwitch(); escSwitch = BuildEscSwitch(((Component)MenuPageEsc.instance).transform); } if (escSwitch != null) { escSwitch.actor = TeamState.LocalActor; int team = TeamState.GetTeam(escSwitch.actor); ((Behaviour)escSwitch.redFrame).enabled = team == 0; ((Behaviour)escSwitch.blueFrame).enabled = team == 1; } } private void DestroyEscSwitch() { if (escSwitch != null && (Object)(object)escSwitch.root != (Object)null) { Object.Destroy((Object)(object)escSwitch.root); } escSwitch = null; } private static RowTeamButtons BuildEscSwitch(Transform page) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0051: 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_006b: 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_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0113: 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) if ((Object)(object)page == (Object)null) { return null; } GameObject val = new GameObject("TeamWarsTeams"); RectTransform val2 = FindMainMenuRow(page); RowTeamButtons rowTeamButtons = new RowTeamButtons { root = val, actor = TeamState.LocalActor, page = ((Component)page).GetComponent<MenuPage>() }; rowTeamButtons.redBase = new Color(0.85f, 0.12f, 0.12f); rowTeamButtons.blueBase = new Color(0.15f, 0.4f, 0.95f); if ((Object)(object)val2 != (Object)null) { Rect rect = val2.rect; if (((Rect)(ref rect)).width > 200f) { val.transform.SetParent((Transform)(object)val2, false); rowTeamButtons.redImage = BuildTeamSquare(val.transform, "Red", rowTeamButtons.redBase, Vector2.zero, 16f, out rowTeamButtons.redFrame); rowTeamButtons.blueImage = BuildTeamSquare(val.transform, "Blue", rowTeamButtons.blueBase, Vector2.zero, 16f, out rowTeamButtons.blueFrame); CornerSquare(((Graphic)rowTeamButtons.redImage).rectTransform, new Vector2(-46f, 0f)); CornerSquare(((Graphic)rowTeamButtons.blueImage).rectTransform, new Vector2(-26f, 0f)); goto IL_01b7; } } val.transform.SetParent(page, false); rowTeamButtons.redImage = BuildTeamSquare(val.transform, "Red", rowTeamButtons.redBase, new Vector2(126f, 104f), 16f, out rowTeamButtons.redFrame); rowTeamButtons.blueImage = BuildTeamSquare(val.transform, "Blue", rowTeamButtons.blueBase, new Vector2(146f, 104f), 16f, out rowTeamButtons.blueFrame); goto IL_01b7; IL_01b7: return rowTeamButtons; } private static RectTransform FindMainMenuRow(Transform page) { TextMeshProUGUI[] componentsInChildren = ((Component)page).GetComponentsInChildren<TextMeshProUGUI>(true); foreach (TextMeshProUGUI val in componentsInChildren) { if ((Object)