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 Guest Role v2.7.4
BepInEx/plugins/Guest-Role/Guest-Role.dll
Decompiled 19 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.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Photon.Pun; using UnityEngine; [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("Guest-Role")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.7.4.0")] [assembly: AssemblyInformationalVersion("2.7.4")] [assembly: AssemblyProduct("Guest-Role")] [assembly: AssemblyTitle("Guest-Role")] [assembly: AssemblyVersion("2.7.4.0")] namespace GuestMedic; [BepInPlugin("Guest-Role", "Guest-Role", "2.7.4")] [BepInIncompatibility("local.bei.guesthealthpackgrant")] [BepInDependency("REPOLib", "4.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { private sealed class BirdSaveState { internal SortedDictionary<string, Dictionary<string, int>> Original; internal SortedDictionary<string, Dictionary<string, int>> Copy; } private sealed class MeleeSpecialistHitState { internal Plugin Owner; internal int Original; } private sealed class RoleAnnouncement { internal PlayerAvatar Player; internal string Id; internal string Text; internal int Role; internal object Room; internal float Deadline; } private sealed class TankRevival { internal int Health; internal float Deadline; internal bool Requested; } private sealed class TankDamageContext { internal PlayerAvatar Victim; internal int Before; internal int Incoming; internal bool Handled; internal bool Local; internal bool Enemy; internal bool Ignore; internal bool Resisted; internal bool InitialHealth; internal int NativeMaximum; internal int RestoredMaximum; internal int Upgrades; } private string birdSelected; private string birdError; private object birdRoom; private BirdBonus birdJump; private BirdBonus birdWings; private Harmony birdPatches; private ConfigEntry<int> birdWingsLevel; private ConfigEntry<float> birdAirTurn; private float birdNextTurn; private static readonly FieldInfo birdStats = AccessTools.Field(typeof(StatsManager), "dictionaryOfDictionaries"); private static readonly FieldInfo birdTumble = AccessTools.Field(typeof(PlayerAvatar), "tumble"); private static readonly FieldInfo birdGrounded = AccessTools.Field(typeof(PlayerAvatar), "isGrounded"); private static readonly FieldInfo birdTumbling = AccessTools.Field(typeof(PlayerAvatar), "isTumbling"); private static readonly FieldInfo birdInput = AccessTools.Field(typeof(PlayerAvatar), "InputDirectionRaw"); private static readonly FieldInfo birdVelocity = AccessTools.Field(typeof(PlayerAvatar), "rbVelocityRaw"); private static readonly FieldInfo birdWingsActive = AccessTools.Field(typeof(PlayerAvatar), "upgradeTumbleWingsVisualsActive"); private static readonly FieldInfo birdFallTimer = AccessTools.Field(typeof(PlayerAvatar), "fallDamageResetTimer"); private Harmony roleUpgradePatches; private readonly BirdFlightRules birdFlight = new BirdFlightRules(); private ConfigEntry<float> birdFlightSeconds; private ConfigEntry<float> birdFlightSpeed; private ConfigEntry<float> birdFlightUpwardAngle; private PlayerAvatar birdFlightPlayer; private object birdFlightStage; private ItemUpgradePlayerTumbleWingsLogic birdFlyingLogic; private static readonly FieldInfo wingsFetched = AccessTools.Field(typeof(ItemUpgradePlayerTumbleWingsLogic), "fetchComplete"); private static readonly FieldInfo wingsReady = AccessTools.Field(typeof(ItemUpgradePlayerTumbleWingsLogic), "lateStartDone"); private static readonly FieldInfo wingsTimer = AccessTools.Field(typeof(ItemUpgradePlayerTumbleWingsLogic), "tumbleWingTimer"); private static readonly FieldInfo wingsPinkTimer = AccessTools.Field(typeof(ItemUpgradePlayerTumbleWingsLogic), "tumbleWingPinkTimer"); private static readonly FieldInfo tumbleTriggered = AccessTools.Field(typeof(PlayerTumble), "isPlayerInputTriggered"); private static readonly FieldInfo tumbleForced = AccessTools.Field(typeof(PlayerTumble), "tumbleOverride"); private NativeEngineer engineer; private Harmony engineerPatches; private string engineerError; private readonly RepairRules repairRules = new RepairRules(); private NativeRepair repair; private RoleShuffleBridge roleBridge; private string repairError; private string mechanicName = "None"; private int selectedRole; internal const int RepairExpressionIndex = 3; private string meleeSpecialistSelected; private string meleeSpecialistError; private object meleeSpecialistRoom; private BirdBonus meleeSpecialistLaunch; private Harmony meleeSpecialistPatches; private readonly HashSet<HurtCollider> meleeSpecialistHits = new HashSet<HurtCollider>(); private readonly Stack<PlayerAvatar> meleeSpecialistExplosions = new Stack<PlayerAvatar>(); private static readonly FieldInfo meleeSpecialistLastHolder = AccessTools.Field(typeof(PhysGrabObject), "lastPlayerGrabbing"); private static readonly FieldInfo meleeSpecialistGrabTimer = AccessTools.Field(typeof(PhysGrabObject), "grabbedTimer"); private static readonly FieldInfo meleeSpecialistTumbleSetup = AccessTools.Field(typeof(PlayerTumble), "setup"); public const string PluginId = "Guest-Role"; internal const int ExpressionIndex = 3; internal static Plugin Current; private readonly GrantRules rules = new GrantRules(); private readonly List<InputKey> blockHostInput = new List<InputKey>(); private ConfigEntry<bool> modEnabled; private ConfigEntry<int> reviveHealth; private ConfigEntry<int> medicHealingLimit; private ConfigEntry<int> medicReviveLimit; private ConfigEntry<int> medicHealingHotkey; private int medicExpression = 3; private ConfigEntry<KeyboardShortcut> menuKey; private NativeContext native; private bool menuOpen; private bool hostReady; private bool mapReady; private int selectedRow; private float nextRosterRefresh; private float noticeUntil; private string selectedName = "None"; private string notice = ""; private List<PlayerAvatar> players = new List<PlayerAvatar>(); private bool panelGrantRequested; private bool panelClearRequested; private object session; private static readonly string[] PanelRoles = new string[5] { "Medic", "Mechanic", "Tank", "Bird", "Brawler" }; private GUIStyle panelText; private GUIStyle panelTitle; private GUIStyle panelSmall; private GUIStyle panelButton; private GUIStyle panelRow; private Vector2 panelScroll; private int lastPanelRow = -1; private Texture2D panelPointer; private readonly List<RoleAnnouncement> roleAnnouncements = new List<RoleAnnouncement>(); private static readonly FieldInfo roleVoiceChat = AccessTools.Field(typeof(PlayerAvatar), "voiceChat"); private readonly TankRules tankRules = new TankRules(); private readonly TankHitScope tankHitScope = new TankHitScope(); private readonly List<TankDamageContext> tankContexts = new List<TankDamageContext>(); private readonly Dictionary<PlayerAvatar, TankRevival> tankRevivals = new Dictionary<PlayerAvatar, TankRevival>(); private readonly Dictionary<PlayerAvatar, int> tankWrites = new Dictionary<PlayerAvatar, int>(); private Harmony tankPatches; private string tankError; private string tankName = "None"; private string tankLastResult = "No damage observed yet."; private float nextTankDiagnostic; private ConfigEntry<bool> tankLogging; private bool tankWriting; private object tankStage; private object tankRoom; private PlayerAvatar maxAvatar; private int maxOriginal; private int maxUpgrade; private int maxApplied; private object maxRoom; private static readonly MethodInfo tankRpc = typeof(PhotonView).GetMethod("RPC", new Type[3] { typeof(string), typeof(RpcTarget), typeof(object[]) }); private static readonly FieldInfo headTimer = AccessTools.Field(typeof(PlayerDeathHead), "triggeredTimer"); private static readonly FieldInfo healthReady = AccessTools.Field(typeof(PlayerHealth), "healthSet"); private static readonly FieldInfo detectionOnly = AccessTools.Field(typeof(HurtCollider), "detectionOnly"); private static readonly FieldInfo ignoreLocal = AccessTools.Field(typeof(HurtCollider), "ignoreLocalPlayer"); private bool EngineerActive { get { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Invalid comparison between Unknown and I4 if (((Behaviour)this).enabled && modEnabled != null && modEnabled.Value && Object.op_Implicit((Object)(object)GameManager.instance) && SemiFunc.IsMultiplayer() && SemiFunc.IsMasterClient() && native.Room != null && session == native.Room && Object.op_Implicit((Object)(object)RunManager.instance) && Object.op_Implicit((Object)(object)GameDirector.instance) && Object.op_Implicit((Object)(object)LevelGenerator.Instance) && LevelGenerator.Instance.Generated && SemiFunc.RunIsLevel()) { return (int)GameDirector.instance.currentState == 2; } return false; } } internal RoleShuffleBridge RepairBridge => roleBridge; private bool MeleeSpecialistAuthority { get { if (((Behaviour)this).enabled && hostReady && modEnabled.Value && meleeSpecialistError == null && Object.op_Implicit((Object)(object)GameManager.instance) && SemiFunc.IsMasterClient()) { return session == native.Room; } return false; } } private string MedicKeyLabel { get { if (medicHealingHotkey.Value != -1) { return medicHealingHotkey.Value + " / Expression" + medicExpression; } return "Disabled"; } } private string RoleName { get { if (selectedRole != 0) { if (selectedRole != 1) { if (selectedRole != 2) { if (selectedRole != 3) { return "Brawler"; } return "Bird"; } return "Tank"; } return "Mechanic"; } return "Medic"; } } private int ConfiguredReviveHealth => Math.Max(1, Math.Min(1000, reviveHealth.Value)); private string RoleDescription { get { if (selectedRole != 0) { if (selectedRole != 1) { if (selectedRole != 2) { if (selectedRole != 3) { return "Brawler: +1 Tumble Launch level; tumble attacks deal 30 damage to enemies; all melee weapon hits deal x1.1 damage to enemies (rounded)."; } return "Bird: +1 extra jump, Wings level " + birdWingsLevel.Value + ", no fall damage (pits excluded). Tumble in midair + forward: camera-directed flight, landing recharges."; } return "Tank: restore allies' enemy damage within 5m; take 100% yourself with 15% resistance, stopping at 1 HP. At 1 HP cover stops. Lethal hits use corrective revival."; } return "Mechanic: directly hold valuables + hold 7 / Expression3 to repair 2%/s (50%/stage). Engineer effect suppression stays automatic. Carts do not repair their contents."; } return $"Medic: auto revive nearest fallen ally within 3m at {ConfiguredReviveHealth} HP, {rules.RevivesRemaining}/{rules.ReviveLimit} revives left. Hold {MedicKeyLabel}: 5 HP per ally per 2s within 5m, {rules.HealingRemaining}/{rules.HealingLimit} HP left."; } } private string PanelError { get { object obj; if (selectedRole != 0) { if (selectedRole != 1) { if (selectedRole != 2) { if (selectedRole != 3) { return meleeSpecialistError; } return birdError; } return tankError; } obj = repairError; if (obj == null) { return engineerError; } } else { obj = null; } return (string)obj; } } private bool TankAuthority { get { if (((Behaviour)this).enabled && hostReady && Object.op_Implicit((Object)(object)GameManager.instance) && SemiFunc.IsMasterClient()) { return session == native.Room; } return false; } } private bool TankActive { get { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 if (TankAuthority && modEnabled.Value && tankError == null && mapReady && Object.op_Implicit((Object)(object)RunManager.instance) && SemiFunc.RunIsLevel() && Object.op_Implicit((Object)(object)GameDirector.instance)) { return (int)GameDirector.instance.currentState == 2; } return false; } } private bool TankResistanceActive { get { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Invalid comparison between Unknown and I4 if (TankAuthority && modEnabled.Value && tankError == null && Object.op_Implicit((Object)(object)GameDirector.instance)) { return (int)GameDirector.instance.currentState == 2; } return false; } } private void InitializeBird() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown birdWingsLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Bird", "TumbleWingsLevel", 10, new ConfigDescription("Minimum Wings level when granted. Native code has no fixed maximum; level 10 reaches zero-gravity flight. Later purchases are retained.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); birdAirTurn = ((BaseUnityPlugin)this).Config.Bind<float>("Bird", "AirTurnDegreesPerSecond", 180f, new ConfigDescription("Additional horizontal air steering. Preserves vertical velocity; 0 disables. Applied to the assigned player only.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 720f), Array.Empty<object>())); try { if (new FieldInfo[8] { birdStats, birdTumble, birdGrounded, birdTumbling, birdInput, birdVelocity, birdWingsActive, birdFallTimer }.Any((FieldInfo f) => f == null)) { throw new MissingMemberException("Bird game API changed."); } birdPatches = new Harmony("Guest-Role.Bird"); InitializeBirdFlight(); } catch (Exception ex) { Harmony obj = birdPatches; if (obj != null) { obj.UnpatchSelf(); } birdError = ex.GetBaseException().Message; ((BaseUnityPlugin)this).Logger.LogError((object)("Bird unavailable; other roles retained: " + ex)); } } private void TickBirdSafely() { if (birdError != null) { return; } try { TickBird(); } catch (Exception ex) { birdError = ex.GetBaseException().Message; ((BaseUnityPlugin)this).Logger.LogError((object)("Bird stopped: " + ex)); try { ReleaseBird(); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)ex2); } } } private void TickBird() { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Invalid comparison between Unknown and I4 if (birdRoom != session) { ReleaseBird(); birdSelected = null; birdRoom = session; } if (!hostReady || !modEnabled.Value || birdSelected == null) { ReleaseBird(); return; } if (!Object.op_Implicit((Object)(object)StatsManager.instance) || !Object.op_Implicit((Object)(object)PunManager.instance) || !Object.op_Implicit((Object)(object)GameDirector.instance) || (int)GameDirector.instance.currentState != 2) { StopBirdFlight(); return; } PlayerAvatar val = ((IEnumerable<PlayerAvatar>)SemiFunc.PlayerGetList()).FirstOrDefault((Func<PlayerAvatar, bool>)((PlayerAvatar p) => Object.op_Implicit((Object)(object)p) && native.Id(p) == birdSelected)); if (!native.CanUse(val) || !mapReady) { StopBirdFlight(); } if (native.CanUse(val)) { if (birdJump != null && (birdJump.Values != StatsManager.instance.playerUpgradeExtraJump || birdWings == null || birdWings.Values != StatsManager.instance.playerUpgradeTumbleWings)) { ReleaseBird(); } if (birdJump == null) { Dictionary<string, int> playerUpgradeExtraJump = StatsManager.instance.playerUpgradeExtraJump; Dictionary<string, int> playerUpgradeTumbleWings = StatsManager.instance.playerUpgradeTumbleWings; playerUpgradeTumbleWings.TryGetValue(birdSelected, out var value); birdJump = new BirdBonus(birdSelected, playerUpgradeExtraJump, 1); ChangeBirdUpgrade(birdJump, "ExtraJump", 1); birdWings = new BirdBonus(birdSelected, playerUpgradeTumbleWings, BirdBonus.WingsBonus(value, birdWingsLevel.Value)); ChangeBirdUpgrade(birdWings, "TumbleWings", birdWings.Amount); } val.FallDamageResetSet(Mathf.Max(0.25f, (float)birdFallTimer.GetValue(val))); if (!(Time.timeScale <= 0f) && mapReady && !(Time.time < birdNextTurn)) { birdNextTurn = Time.time + 0.1f; BirdAirControl(val); } } } private void BirdAirControl(PlayerAvatar player) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_0073: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) if (birdAirTurn.Value <= 0f || (bool)birdGrounded.GetValue(player)) { return; } Vector3 val = (Vector3)birdInput.GetValue(player); if (!Finite(val) || ((Vector3)(ref val)).sqrMagnitude < 0.01f || (bool)birdTumbling.GetValue(player)) { return; } Vector3 val2 = (Vector3)birdVelocity.GetValue(player); Vector3 val3 = default(Vector3); ((Vector3)(ref val3))..ctor(val2.x, 0f, val2.z); if (!Finite(val3) || ((Vector3)(ref val3)).magnitude < 0.2f || ((Vector3)(ref val3)).magnitude > 12f) { return; } Vector3 val4 = Vector3.ProjectOnPlane((Object.op_Implicit((Object)(object)player.localCamera) ? player.localCamera.GetOverrideTransform() : ((Component)player).transform).forward, Vector3.up); Vector3 val5 = ((Vector3)(ref val4)).normalized; if (((Vector3)(ref val5)).sqrMagnitude < 0.1f) { val5 = ((Component)player).transform.forward; } Vector3 val6 = val5 * val.z + Vector3.Cross(Vector3.up, val5) * val.x; if (!(((Vector3)(ref val6)).sqrMagnitude < 0.01f)) { Vector3 val7 = Vector3.ClampMagnitude(Vector3.RotateTowards(val3, ((Vector3)(ref val6)).normalized * ((Vector3)(ref val3)).magnitude, birdAirTurn.Value * (MathF.PI / 180f) * 0.1f, 0f) - val3, 1.5f); val7.y = 0f; if (!(((Vector3)(ref val7)).sqrMagnitude < 0.0001f)) { player.ForceImpulse(val7); } } } private void ChangeBirdUpgrade(BirdBonus bonus, string upgrade, int delta) { if (delta != 0) { if (upgrade == "ExtraJump") { PunManager.instance.UpgradePlayerExtraJump(bonus.Player, delta); } else { PunManager.instance.UpgradePlayerTumbleWings(bonus.Player, delta); } PhotonView component = ((Component)PunManager.instance).GetComponent<PhotonView>(); if (!Object.op_Implicit((Object)(object)component)) { throw new InvalidOperationException("PunManager PhotonView is unavailable."); } tankRpc.Invoke(component, new object[3] { "TesterUpgradeCommandRPC", (object)(RpcTarget)1, new object[3] { bonus.Player, upgrade, delta } }); tankRpc.Invoke(component, new object[3] { "UpdateStatRPC", (object)(RpcTarget)1, new object[3] { "playerUpgrade" + upgrade, bonus.Player, bonus.Current } }); } } private void SelectBird(string id) { if (!(birdSelected == id)) { ReleaseBird(); birdSelected = id; } } private void ReleaseBird() { StopBirdFlight(); birdFlight.Reset(); birdFlightPlayer = null; birdFlightStage = null; BirdBonus item = birdJump; BirdBonus item2 = birdWings; birdJump = (birdWings = null); Exception ex = null; (BirdBonus, string)[] array = new(BirdBonus, string)[2] { (item, "ExtraJump"), (item2, "TumbleWings") }; for (int i = 0; i < array.Length; i++) { (BirdBonus, string) tuple = array[i]; var (birdBonus, _) = tuple; if (birdBonus == null) { continue; } int withoutBonus = birdBonus.WithoutBonus; bool flag = Object.op_Implicit((Object)(object)StatsManager.instance) && birdBonus.Values == ((tuple.Item2 == "ExtraJump") ? StatsManager.instance.playerUpgradeExtraJump : StatsManager.instance.playerUpgradeTumbleWings); try { if (flag && Object.op_Implicit((Object)(object)PunManager.instance) && SemiFunc.IsMasterClient() && birdRoom == native.Room) { ChangeBirdUpgrade(birdBonus, tuple.Item2, withoutBonus - birdBonus.Current); } } catch (Exception ex2) { ex = ex ?? ex2; } finally { if (birdBonus.Values.ContainsKey(birdBonus.Player)) { birdBonus.Values[birdBonus.Player] = withoutBonus; } } } if (ex != null) { throw ex; } } private void InitializeRoleUpgradePersistence() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0054: 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_0076: Expected O, but got Unknown //IL_0076: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown if (birdStats == null) { throw new MissingMemberException("Temporary upgrade save API changed."); } roleUpgradePatches = new Harmony("Guest-Role.TemporaryUpgrades"); roleUpgradePatches.Patch((MethodBase)AccessTools.Method(typeof(StatsManager), "SaveGame", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BirdSavePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "BirdSaveFinalizer", (Type[])null), (HarmonyMethod)null); string[] array = new string[2] { "LoadGame", "ResetAllStats" }; foreach (string text in array) { roleUpgradePatches.Patch((MethodBase)AccessTools.Method(typeof(StatsManager), text, (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BirdResetPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private static void BirdSavePrefix(StatsManager __instance, out BirdSaveState __state) { __state = null; Plugin current = Current; if (!Object.op_Implicit((Object)(object)current)) { return; } bool flag = current.birdJump != null && current.birdJump.Values == __instance.playerUpgradeExtraJump; bool flag2 = current.meleeSpecialistLaunch != null && current.meleeSpecialistLaunch.Values == __instance.playerUpgradeLaunch; if (!flag && !flag2) { return; } SortedDictionary<string, Dictionary<string, int>> sortedDictionary = (SortedDictionary<string, Dictionary<string, int>>)birdStats.GetValue(__instance); SortedDictionary<string, Dictionary<string, int>> sortedDictionary2 = new SortedDictionary<string, Dictionary<string, int>>(sortedDictionary, sortedDictionary.Comparer); if (flag) { sortedDictionary2["playerUpgradeExtraJump"] = current.birdJump.ForSave(); if (current.birdWings != null) { sortedDictionary2["playerUpgradeTumbleWings"] = current.birdWings.ForSave(); } } if (flag2) { sortedDictionary2["playerUpgradeLaunch"] = current.meleeSpecialistLaunch.ForSave(); } __state = new BirdSaveState { Original = sortedDictionary, Copy = sortedDictionary2 }; birdStats.SetValue(__instance, sortedDictionary2); } private static Exception BirdSaveFinalizer(StatsManager __instance, BirdSaveState __state, Exception __exception) { if (__state != null && birdStats.GetValue(__instance) == __state.Copy) { birdStats.SetValue(__instance, __state.Original); } return __exception; } private static void BirdResetPrefix() { Plugin current = Current; if (!Object.op_Implicit((Object)(object)current) || !SemiFunc.IsMasterClientOrSingleplayer()) { return; } try { current.ReleaseBird(); current.birdSelected = null; } finally { current.ReleaseMeleeSpecialist(); current.meleeSpecialistSelected = null; } } private void InitializeBirdFlight() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Expected O, but got Unknown birdFlightSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Bird", "FlightDurationSeconds", 3.5f, new ConfigDescription("Flight window per takeoff. Starts on forward input while tumbling in midair; only landing refills it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), Array.Empty<object>())); birdFlightSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Bird", "FlightSpeed", 4f, new ConfigDescription("Flight speed in metres per second along the camera direction, including pitch.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 15f), Array.Empty<object>())); birdFlightUpwardAngle = ((BaseUnityPlugin)this).Config.Bind<float>("Bird", "FlightUpwardAngleDegrees", 30f, new ConfigDescription("Aim flight this many degrees above the camera direction, capped at straight up. 0 restores exact camera-direction flight. Does not increase total speed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>())); FieldInfo[] array = new FieldInfo[6] { wingsFetched, wingsReady, wingsTimer, wingsPinkTimer, tumbleTriggered, tumbleForced }; for (int i = 0; i < array.Length; i++) { if (array[i] == null) { throw new MissingMemberException("Bird flight game API changed."); } } birdPatches.Patch((MethodBase)AccessTools.Method(typeof(ItemUpgradePlayerTumbleWingsLogic), "FixedUpdate", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BirdWingsFixedPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static bool BirdWingsFixedPrefix(ItemUpgradePlayerTumbleWingsLogic __instance) { Plugin current = Current; if (!Object.op_Implicit((Object)(object)current) || !((Behaviour)current).enabled || !current.hostReady || !current.modEnabled.Value || current.birdError != null || !SemiFunc.IsMasterClient() || current.session != current.native.Room) { return true; } PlayerAvatar playerAvatar = __instance.playerAvatar; if (!Object.op_Implicit((Object)(object)playerAvatar) || current.birdSelected == null || current.native.Id(playerAvatar) != current.birdSelected) { return true; } try { return current.BirdFlightFixed(__instance, playerAvatar); } catch (Exception ex) { current.birdError = ex.GetBaseException().Message; ((BaseUnityPlugin)current).Logger.LogError((object)("Bird flight stopped: " + ex)); try { current.ReleaseBird(); } catch (Exception ex2) { ((BaseUnityPlugin)current).Logger.LogError((object)ex2); } return true; } } private bool BirdFlightFixed(ItemUpgradePlayerTumbleWingsLogic logic, PlayerAvatar player) { //IL_00ee: 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_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) if (!mapReady || !native.CanUse(player) || birdJump == null || !(bool)wingsFetched.GetValue(logic) || !(bool)wingsReady.GetValue(logic)) { StopBirdFlight(); return true; } if (birdFlightPlayer != player || birdFlightStage != LevelGenerator.Instance) { StopBirdFlight(); birdFlight.Reset(); birdFlightPlayer = player; birdFlightStage = LevelGenerator.Instance; } object? value = birdTumble.GetValue(player); PlayerTumble val = (PlayerTumble)((value is PlayerTumble) ? value : null); PhysGrabObject val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<PhysGrabObject>() : null); if (!Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)val2.rb)) { StopBirdFlight(); return true; } bool num = (bool)birdTumbling.GetValue(player); bool grounded = (num ? SemiFunc.OnGroundCheck(((Component)val).transform.position, 1f, val2) : ((bool)birdGrounded.GetValue(player))); Vector3 val3 = (Vector3)birdInput.GetValue(player); bool eligible = num && val2.playerGrabbing.Count == 0 && !val2.rb.isKinematic && ((bool)tumbleTriggered.GetValue(val) || !(bool)tumbleForced.GetValue(val)) && (float)wingsPinkTimer.GetValue(logic) <= 0f && !GameDirector.instance.DisableInput; if (!birdFlight.Step(Time.timeAsDouble, grounded, eligible, Finite(val3) && val3.z > 0.1f, birdFlightSeconds.Value)) { StopBirdFlight(); if ((bool)birdWingsActive.GetValue(player) && (float)wingsPinkTimer.GetValue(logic) <= 0f) { player.UpgradeTumbleWingsVisualsActive(false, false); } return false; } Transform val4 = (Object.op_Implicit((Object)(object)player.localCamera) ? player.localCamera.GetOverrideTransform() : null); if (!Object.op_Implicit((Object)(object)val4) || !Finite(val4.forward)) { StopBirdFlight(); return false; } birdFlyingLogic = logic; if (!(bool)birdWingsActive.GetValue(player)) { player.UpgradeTumbleWingsVisualsActive(true, false); } wingsTimer.SetValue(logic, (float)(birdFlight.Remaining(Time.timeAsDouble) / (double)birdFlightSeconds.Value)); val2.OverrideZeroGravity(0.1f); val2.rb.useGravity = false; val.DisableCustomGravity(0.1f); Vector3 val5 = val4.forward; val5 = Vector3.RotateTowards(((Vector3)(ref val5)).normalized, Vector3.up, birdFlightUpwardAngle.Value * (MathF.PI / 180f), 0f); Vector3 normalized = ((Vector3)(ref val5)).normalized; val2.rb.velocity = normalized * birdFlightSpeed.Value; Vector3 val6 = SemiFunc.PhysFollowRotation(((Component)val2).transform, Quaternion.LookRotation(normalized, val4.up), val2.rb, 20f); val2.rb.AddTorque(val6, (ForceMode)1); return false; } private void StopBirdFlight() { ItemUpgradePlayerTumbleWingsLogic val = birdFlyingLogic; birdFlyingLogic = null; if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.playerAvatar) && SemiFunc.IsMasterClient() && birdRoom == native.Room && (bool)birdWingsActive.GetValue(val.playerAvatar)) { val.playerAvatar.UpgradeTumbleWingsVisualsActive(false, false); } } private bool IsEngineer(PlayerAvatar player) { if (Object.op_Implicit((Object)(object)player) && repairRules.Selected != null && native.Id(player) == repairRules.Selected) { return native.CanUse(player); } return false; } private void InitializeEngineer() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Expected O, but got Unknown try { engineer = new NativeEngineer(() => EngineerActive, IsEngineer, (PlayerAvatar p) => roleBridge != null && roleBridge.NativeEngineer(p)); engineerPatches = new Harmony("Guest-Role.Engineer"); HarmonyMethod val = new HarmonyMethod(typeof(Plugin), "EngineerEffectPrefix", (Type[])null); foreach (MethodBase item in NativeEngineer.EffectMethods()) { engineerPatches.Patch(item, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } HarmonyMethod val2 = new HarmonyMethod(typeof(Plugin), "EngineerGrabStartPrefix", (Type[])null); val2.priority = 0; val2.after = new string[1] { "REPOJP.RoleShuffle" }; HarmonyMethod val3 = val2; engineerPatches.Patch((MethodBase)AccessTools.Method(typeof(PhysGrabObject), "GrabStartedRPC", (Type[])null, (Type[])null), val3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); engineerPatches.Patch((MethodBase)AccessTools.Method(typeof(PhysGrabObject), "GrabEndedRPC", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "EngineerGrabEndPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { Harmony obj = engineerPatches; if (obj != null) { obj.UnpatchSelf(); } engineer = null; engineerError = ex.GetBaseException().Message; ((BaseUnityPlugin)this).Logger.LogError((object)("Engineer addition unavailable; existing Medic and repair remain active: " + ex)); } } private static bool EngineerEffectPrefix(Component __instance) { if (Object.op_Implicit((Object)(object)Current)) { return !Current.RunEngineer((NativeEngineer e) => e.Suppress(__instance)); } return true; } private static bool EngineerGrabStartPrefix(PhysGrabObject __instance, int playerPhotonID, PhotonMessageInfo _info) { //IL_0015: 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) if (Object.op_Implicit((Object)(object)Current)) { return !Current.RunEngineer((NativeEngineer e) => e.Register(__instance, playerPhotonID, _info)); } return true; } private static void EngineerGrabEndPrefix(PhysGrabObject __instance, int playerPhotonID, PhotonMessageInfo _info) { //IL_0015: 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) if (Object.op_Implicit((Object)(object)Current)) { Current.RunEngineer(delegate(NativeEngineer e) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) e.Release(__instance, playerPhotonID, _info); return true; }); } } private bool RunEngineer(Func<NativeEngineer, bool> action) { if (engineer == null || engineerError != null) { return false; } try { return action(engineer); } catch (Exception ex) { engineerError = ex.GetBaseException().Message; ((BaseUnityPlugin)this).Logger.LogError((object)("Engineer addition stopped; Medic and repair retained: " + ex)); try { engineer.RestoreAll(); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)ex2); } return false; } } private void InitializeRepair() { try { repair = new NativeRepair(); roleBridge = new RoleShuffleBridge(); } catch (Exception ex) { repairError = ex.GetBaseException().Message; ((BaseUnityPlugin)this).Logger.LogError((object)("Mechanic grant disabled; Medic remains available: " + ex)); } } private void TickMechanicSafely() { try { TickMechanic(); } catch (Exception ex) { repairError = ex.GetBaseException().Message; repairRules.ResetHold(); SetNotice("Mechanic stopped after an error; Medic is still available. See LogOutput.log."); ((BaseUnityPlugin)this).Logger.LogError((object)("Mechanic stopped without retrying an uncertain repair: " + ex)); } } private void TickMechanic() { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) if (repairError != null || !mapReady || !modEnabled.Value || Time.timeScale <= 0f || repairRules.Selected == null) { repairRules.ResetHold(); return; } PlayerAvatar val = ((IEnumerable<PlayerAvatar>)SemiFunc.PlayerGetList()).FirstOrDefault((Func<PlayerAvatar, bool>)((PlayerAvatar p) => Object.op_Implicit((Object)(object)p) && native.Id(p) == repairRules.Selected)); if (!native.CanUse(val)) { repairRules.ResetHold(); return; } mechanicName = PlayerLabel(val); if (roleBridge.NativeMechanic(val, out var used)) { repairRules.ObserveExternalUsage(used); repairRules.ResetHold(); return; } PhysGrabObject val2 = repair.DirectHeld(val); PhysGrabObjectImpactDetector detector; ValuableObject val3 = repair.Valuable(val2, out detector); if (!Object.op_Implicit((Object)(object)val3) || !repair.NetworkReady(detector) || !Finite(val2.centerPoint)) { repairRules.ResetHold(); return; } float num = repair.Original(val3); float num2 = repair.Current(val3); float num3 = repairRules.Tick(native.Id(val), val, val3, num, num2, Time.timeAsDouble, native.ExpressionActive(val)); if (!(num3 < 1f)) { try { repair.Apply(detector, num3, val2.centerPoint); } finally { repairRules.Complete(num, num2, repair.Current(val3)); } if (repairRules.Remaining <= 1E-06) { SetNotice("Mechanic repair exhausted for this stage: 50% used."); } } } internal bool AllowNativeMechanicRepair(PlayerAvatar player) { if (!((Behaviour)this).enabled || !hostReady || !modEnabled.Value || !Object.op_Implicit((Object)(object)player) || repairRules.Selected == null || native.Id(player) != repairRules.Selected) { return true; } if (repairError == null && mapReady && !menuOpen && Time.timeScale > 0f && native.CanUse(player)) { return native.ExpressionActive(player); } return false; } private string GrantedRoles(PlayerAvatar player) { string text = native.Id(player); return ((text == rules.Selected) ? " [Medic]" : "") + ((text == repairRules.Selected) ? " [Mechanic]" : "") + ((text == tankRules.Selected) ? " [Tank]" : "") + ((text == birdSelected) ? " [Bird]" : "") + ((text == meleeSpecialistSelected) ? " [Brawler]" : ""); } private bool IsMeleeSpecialist(PlayerAvatar player) { if (MeleeSpecialistAuthority && meleeSpecialistSelected != null && Object.op_Implicit((Object)(object)player) && native.Id(player) == meleeSpecialistSelected) { return native.CanUse(player); } return false; } private void InitializeMeleeSpecialist() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_0090: Expected O, but got Unknown //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown //IL_00df: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Expected O, but got Unknown try { if (meleeSpecialistLastHolder == null || meleeSpecialistGrabTimer == null || meleeSpecialistTumbleSetup == null) { throw new MissingMemberException("MeleeSpecialist game API changed."); } meleeSpecialistPatches = new Harmony("Guest-Role.MeleeSpecialist"); meleeSpecialistPatches.Patch((MethodBase)AccessTools.Method(typeof(HurtCollider), "EnemyHurt", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "MeleeSpecialistHitPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "MeleeSpecialistHitFinalizer", (Type[])null), (HarmonyMethod)null); meleeSpecialistPatches.Patch((MethodBase)AccessTools.Method(typeof(ItemMeleeInflatableHammer), "ExplosionRPC", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "MeleeSpecialistExplosionPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "MeleeSpecialistExplosionFinalizer", (Type[])null), (HarmonyMethod)null); meleeSpecialistPatches.Patch((MethodBase)AccessTools.Method(typeof(ParticleScriptExplosion), "Spawn", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "MeleeSpecialistExplosionSpawned", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { Harmony obj = meleeSpecialistPatches; if (obj != null) { obj.UnpatchSelf(); } meleeSpecialistError = ex.GetBaseException().Message; ((BaseUnityPlugin)this).Logger.LogError((object)("MeleeSpecialist unavailable; other roles retained: " + ex)); } } private void TickMeleeSpecialistSafely() { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Invalid comparison between Unknown and I4 if (meleeSpecialistError != null) { return; } try { if (meleeSpecialistRoom != session) { ReleaseMeleeSpecialist(); meleeSpecialistSelected = null; meleeSpecialistRoom = session; } if (!MeleeSpecialistAuthority || meleeSpecialistSelected == null) { ReleaseMeleeSpecialist(); } else { if (!Object.op_Implicit((Object)(object)StatsManager.instance) || !Object.op_Implicit((Object)(object)PunManager.instance) || !Object.op_Implicit((Object)(object)GameDirector.instance) || (int)GameDirector.instance.currentState != 2) { return; } PlayerAvatar val = ((IEnumerable<PlayerAvatar>)SemiFunc.PlayerGetList()).FirstOrDefault((Func<PlayerAvatar, bool>)((PlayerAvatar p) => Object.op_Implicit((Object)(object)p) && native.Id(p) == meleeSpecialistSelected)); if (!native.CanUse(val)) { return; } object? value = birdTumble.GetValue(val); PlayerTumble val2 = (PlayerTumble)((value is PlayerTumble) ? value : null); if (Object.op_Implicit((Object)(object)val2) && (bool)meleeSpecialistTumbleSetup.GetValue(val2)) { if (meleeSpecialistLaunch != null && meleeSpecialistLaunch.Values != StatsManager.instance.playerUpgradeLaunch) { ReleaseMeleeSpecialist(); } if (meleeSpecialistLaunch == null) { meleeSpecialistLaunch = new BirdBonus(meleeSpecialistSelected, StatsManager.instance.playerUpgradeLaunch, 1); ChangeMeleeSpecialistLaunch(meleeSpecialistLaunch, 1); } } } } catch (Exception ex) { meleeSpecialistError = ex.GetBaseException().Message; ((BaseUnityPlugin)this).Logger.LogError((object)("MeleeSpecialist stopped: " + ex)); try { ReleaseMeleeSpecialist(); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)ex2); } } } private void ChangeMeleeSpecialistLaunch(BirdBonus bonus, int delta) { if (delta != 0) { PunManager.instance.UpgradePlayerTumbleLaunch(bonus.Player, delta); PhotonView component = ((Component)PunManager.instance).GetComponent<PhotonView>(); if (!Object.op_Implicit((Object)(object)component)) { throw new InvalidOperationException("PunManager PhotonView is unavailable."); } tankRpc.Invoke(component, new object[3] { "TesterUpgradeCommandRPC", (object)(RpcTarget)1, new object[3] { bonus.Player, "Launch", delta } }); tankRpc.Invoke(component, new object[3] { "UpdateStatRPC", (object)(RpcTarget)1, new object[3] { "playerUpgradeLaunch", bonus.Player, bonus.Current } }); } } private void SelectMeleeSpecialist(string id) { if (!(meleeSpecialistSelected == id)) { ReleaseMeleeSpecialist(); meleeSpecialistSelected = id; } } private void ReleaseMeleeSpecialist() { BirdBonus birdBonus = meleeSpecialistLaunch; meleeSpecialistLaunch = null; if (birdBonus == null) { return; } int withoutBonus = birdBonus.WithoutBonus; try { if (Object.op_Implicit((Object)(object)StatsManager.instance) && birdBonus.Values == StatsManager.instance.playerUpgradeLaunch && Object.op_Implicit((Object)(object)PunManager.instance) && SemiFunc.IsMasterClient() && meleeSpecialistRoom == native.Room) { ChangeMeleeSpecialistLaunch(birdBonus, withoutBonus - birdBonus.Current); } } finally { if (birdBonus.Values.ContainsKey(birdBonus.Player)) { birdBonus.Values[birdBonus.Player] = withoutBonus; } } } private PlayerAvatar MeleeSpecialistMeleeUser(Component weapon) { ItemMelee val = (Object.op_Implicit((Object)(object)weapon) ? weapon.GetComponentInParent<ItemMelee>() : null); if (!Object.op_Implicit((Object)(object)val) || val.swingLogicOnly) { return null; } PhysGrabObject component = ((Component)val).GetComponent<PhysGrabObject>(); if (!Object.op_Implicit((Object)(object)component)) { return null; } if (component.playerGrabbing.Count > 0) { if (!Object.op_Implicit((Object)(object)component.playerGrabbing[0])) { return null; } return component.playerGrabbing[0].playerAvatar; } if (!((float)meleeSpecialistGrabTimer.GetValue(component) > 0f)) { return null; } object? value = meleeSpecialistLastHolder.GetValue(component); return (PlayerAvatar)((value is PlayerAvatar) ? value : null); } private static void MeleeSpecialistHitPrefix(HurtCollider __instance, out MeleeSpecialistHitState __state) { __state = null; Plugin current = Current; if (!Object.op_Implicit((Object)(object)current) || !current.MeleeSpecialistAuthority || !current.mapReady || current.meleeSpecialistHits.Contains(__instance) || __instance.deathPit || __instance.enemyKill || !__instance.enemyLogic) { return; } PlayerTumble componentInParent = ((Component)__instance).GetComponentInParent<PlayerTumble>(); int enemyDamage; if (Object.op_Implicit((Object)(object)componentInParent) && (Object)(object)componentInParent.hurtCollider == (Object)(object)__instance && current.IsMeleeSpecialist(componentInParent.playerAvatar)) { enemyDamage = 30; } else { MeleeSpecialistWeaponSource component = ((Component)__instance).GetComponent<MeleeSpecialistWeaponSource>(); PlayerAvatar player = (Object.op_Implicit((Object)(object)component) ? component.Player : current.MeleeSpecialistMeleeUser((Component)(object)__instance)); if (!current.IsMeleeSpecialist(player) || __instance.enemyDamage <= 0) { return; } enemyDamage = MeleeSpecialistRules.MeleeDamage(__instance.enemyDamage); } __state = new MeleeSpecialistHitState { Owner = current, Original = __instance.enemyDamage }; current.meleeSpecialistHits.Add(__instance); __instance.enemyDamage = enemyDamage; } private static Exception MeleeSpecialistHitFinalizer(HurtCollider __instance, MeleeSpecialistHitState __state, Exception __exception) { if (__state != null) { if (Object.op_Implicit((Object)(object)__instance)) { __instance.enemyDamage = __state.Original; } __state.Owner.meleeSpecialistHits.Remove(__instance); } return __exception; } private static void MeleeSpecialistExplosionPrefix(ItemMeleeInflatableHammer __instance, out bool __state) { __state = false; Plugin current = Current; if (Object.op_Implicit((Object)(object)current) && current.MeleeSpecialistAuthority && current.mapReady) { current.meleeSpecialistExplosions.Push(current.MeleeSpecialistMeleeUser((Component)(object)__instance)); __state = true; } } private static Exception MeleeSpecialistExplosionFinalizer(bool __state, Exception __exception) { if (__state && Object.op_Implicit((Object)(object)Current) && Current.meleeSpecialistExplosions.Count > 0) { Current.meleeSpecialistExplosions.Pop(); } return __exception; } private static void MeleeSpecialistExplosionSpawned(ParticlePrefabExplosion __result) { Plugin current = Current; if (Object.op_Implicit((Object)(object)current) && Object.op_Implicit((Object)(object)__result) && current.meleeSpecialistExplosions.Count != 0 && Object.op_Implicit((Object)(object)__result.HurtCollider)) { PlayerAvatar player = current.meleeSpecialistExplosions.Peek(); if (current.IsMeleeSpecialist(player)) { (((Component)__result.HurtCollider).GetComponent<MeleeSpecialistWeaponSource>() ?? ((Component)__result.HurtCollider).gameObject.AddComponent<MeleeSpecialistWeaponSource>()).Player = player; } } } private void Awake() { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected O, but got Unknown try { if (ConfigRename.MoveLegacy(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath))) { ((BaseUnityPlugin)this).Config.Reload(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Settings moved to Guest-Role.cfg; existing values preserved."); } modEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable Medic, Mechanic, Tank, Bird and Brawler grants. Host only."); reviveHealth = ((BaseUnityPlugin)this).Config.Bind<int>("Revival", "HealthAfterRevive", 25, new ConfigDescription("Total HP after automatic revival, capped at the target maximum. Replaces ReviveHealth from 1.x.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>())); menuKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "HostMenuKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Host-only role panel. Up/Down: player, Left/Right: Medic, Mechanic, Tank, Bird or Brawler, Enter: grant, Backspace: clear the displayed role."); medicHealingHotkey = ((BaseUnityPlugin)this).Config.Bind<int>("Medic", "HealingHotkey", 7, new ConfigDescription("Vanilla expression key for healing: 5/6/7/8/9/0, or -1 to disable healing. Unmodded guests use their binding for the corresponding Expression1-6. Hold mode is recommended.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[7] { 5, 6, 7, 8, 9, 0, -1 }), Array.Empty<object>())); medicHealingLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Medic", "HealingLimitPerStage", 100, new ConfigDescription("Total ally HP healed per stage, shared across all recipients. 0 disables healing. Changing settings does not reset HP already spent.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100000), Array.Empty<object>())); medicReviveLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Medic", "RevivesPerStage", 1, new ConfigDescription("Automatic revivals per stage. 0 disables revival. Changing settings does not reset uses already spent.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>())); ConfigureMedic(); native = new NativeContext(); InitializeRepair(); Current = this; InitializeEngineer(); InitializeTank(); InitializeRoleUpgradePersistence(); InitializeBird(); InitializeMeleeSpecialist(); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Guest-Role 2.7.4 loaded: Tank (200 base max HP, 5m enemy damage cover, 100% transfer, 15% damage resistance, nonlethal transfer cost); F8 Mechanic includes Expression3 repair (top-row 7, 2%/s, 50%/stage) plus Engineer effect suppression; nearest death head within 3m revives at {ConfiguredReviveHealth} HP up to {rules.ReviveLimit} times per stage; {MedicKeyLabel} heals allies within 5m by 5 HP every 2s, {rules.HealingLimit} HP shared cap. No health pack spawning."); } catch (Exception ex) { ((Behaviour)this).enabled = false; ((BaseUnityPlugin)this).Logger.LogError((object)("Guest-Role disabled: " + ex)); } } private bool SyncContext() { //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Invalid comparison between Unknown and I4 hostReady = ((Behaviour)this).enabled && Object.op_Implicit((Object)(object)GameManager.instance) && SemiFunc.IsMultiplayer() && SemiFunc.IsMasterClient() && native.Room != null; if (!hostReady) { rules.SetSession(null); repairRules.SetSession(null); session = null; menuOpen = false; mapReady = false; selectedName = "None"; return false; } object obj = session; session = native.Room; rules.SetSession(session); repairRules.SetSession(session); if (obj != session) { selectedName = (mechanicName = "None"); players.Clear(); } mapReady = Object.op_Implicit((Object)(object)RunManager.instance) && Object.op_Implicit((Object)(object)GameDirector.instance) && LevelGenerator.Instance != null && LevelGenerator.Instance.Generated && SemiFunc.RunIsLevel() && (int)GameDirector.instance.currentState == 2; if (mapReady) { repairRules.SetMap(LevelGenerator.Instance); if (rules.SetMap(LevelGenerator.Instance)) { SetNotice($"New stage: Medic {rules.ReviveLimit} revives + {rules.HealingLimit} HP healing; Mechanic 50% repair available."); } } return mapReady; } private void Update() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) try { ConfigureMedic(); SyncContext(); RunEngineer(delegate(NativeEngineer nativeEngineer) { nativeEngineer.Reconcile(); return true; }); TankSafely(TickTank); TickBirdSafely(); TickMeleeSpecialistSafely(); TickRoleSpeech(); if (!hostReady) { return; } KeyboardShortcut value = menuKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { menuOpen = !menuOpen; if (menuOpen) { RefreshPlayers(); selectedRow = 0; } } if (!menuOpen) { panelGrantRequested = (panelClearRequested = false); TickMedic(); TickMechanicSafely(); return; } if (Input.GetKeyDown((KeyCode)27)) { menuOpen = false; panelGrantRequested = (panelClearRequested = false); return; } if (Object.op_Implicit((Object)(object)CursorManager.instance)) { SemiFunc.CursorUnlock(0.1f); } if (Object.op_Implicit((Object)(object)CameraAim.Instance)) { CameraAim.Instance.OverrideAimStop(); } rules.ResetChannel(); repairRules.ResetHold(); if (Object.op_Implicit((Object)(object)InputManager.instance)) { InputManager.instance.DisableControlsExcept(0.1f, blockHostInput); } if (Time.unscaledTime >= nextRosterRefresh) { RefreshPlayers(); } if (Input.GetKeyDown((KeyCode)273)) { selectedRow = Mathf.Max(0, selectedRow - 1); } if (Input.GetKeyDown((KeyCode)274)) { selectedRow = Mathf.Min(Mathf.Max(0, players.Count - 1), selectedRow + 1); } if (Input.GetKeyDown((KeyCode)276)) { selectedRole = (selectedRole + 4) % 5; } if (Input.GetKeyDown((KeyCode)275)) { selectedRole = (selectedRole + 1) % 5; } if (ConsumePanelRequest(ref panelClearRequested) || Input.GetKeyDown((KeyCode)8)) { if (selectedRole == 0) { rules.Select(null); selectedName = "None"; } else if (selectedRole == 1) { repairRules.Select(null); mechanicName = "None"; } else if (selectedRole == 2) { tankRules.Select(null); tankName = "None"; } else if (selectedRole == 3) { SelectBird(null); } else { SelectMeleeSpecialist(null); } SetNotice(RoleName + " cleared. Stage budgets were kept."); } if ((!ConsumePanelRequest(ref panelGrantRequested) && !Input.GetKeyDown((KeyCode)13)) || players.Count <= 0) { return; } PlayerAvatar val = players[Mathf.Clamp(selectedRow, 0, players.Count - 1)]; if (!Object.op_Implicit((Object)(object)val)) { return; } string text = native.Id(val); if (string.IsNullOrEmpty(text)) { SetNotice("Player information is still loading."); return; } if (selectedRole == 0) { ClearConflictingRoles(text, selectedRole); rules.Select(text); selectedName = PlayerLabel(val); SetNotice("Medic: " + selectedName + ". Approach death heads to revive; hold " + MedicKeyLabel + " to heal allies."); } else if (selectedRole == 1) { if (repairError != null) { SetNotice("Mechanic unavailable: " + repairError); return; } ClearConflictingRoles(text, selectedRole); repairRules.Select(text); mechanicName = PlayerLabel(val); SetNotice("Mechanic: " + mechanicName + ". Hold 7 / Expression3 while holding valuables: repair 2%/s, 50%/stage."); } else if (selectedRole == 2) { if (tankError != null) { SetNotice("Tank unavailable: " + tankError); return; } ClearConflictingRoles(text, selectedRole); tankRules.Select(text); tankName = PlayerLabel(val); SetNotice("Tank: " + tankName + ". Cover enemy damage within 5m; absorb all with 15% resistance, retain at least 1 HP."); } else if (selectedRole == 3) { if (birdError != null) { SetNotice("Bird unavailable: " + birdError); return; } ClearConflictingRoles(text, selectedRole); SelectBird(text); TickBirdSafely(); if (birdError != null) { birdSelected = null; SetNotice("Bird unavailable: " + birdError); return; } SetNotice("Bird: " + PlayerLabel(val) + ". Extra jump, Wings, fall protection and air steering."); } else { if (meleeSpecialistError != null) { SetNotice("Brawler unavailable: " + meleeSpecialistError); return; } ClearConflictingRoles(text, selectedRole); SelectMeleeSpecialist(text); TickMeleeSpecialistSafely(); if (meleeSpecialistError != null) { meleeSpecialistSelected = null; SetNotice("Brawler unavailable: " + meleeSpecialistError); return; } SetNotice("Brawler: " + PlayerLabel(val) + ". Tumble Launch +1, tumble damage 30, melee damage x1.1."); } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Granted " + RoleName + " to " + PlayerLabel(val))); AnnounceRole(val, text, selectedRole, RoleName); menuOpen = false; } catch (Exception e) { DisableAfterError(e); } } private void RefreshPlayers() { string highlighted = ((players.Count > 0 && selectedRow < players.Count && Object.op_Implicit((Object)(object)players[selectedRow])) ? native.Id(players[selectedRow]) : null); nextRosterRefresh = Time.unscaledTime + 0.5f; players = (Object.op_Implicit((Object)(object)GameDirector.instance) ? (from p in SemiFunc.PlayerGetList() where Object.op_Implicit((Object)(object)p) && !string.IsNullOrEmpty(native.Id(p)) orderby (p != PlayerAvatar.instance) ? 1 : 0 select p).ThenBy<PlayerAvatar, string>((PlayerAvatar p) => native.Id(p), StringComparer.Ordinal).ToList() : new List<PlayerAvatar>()); int num = ((highlighted == null) ? (-1) : players.FindIndex((PlayerAvatar p) => native.Id(p) == highlighted)); if (num >= 0) { selectedRow = num; } selectedRow = Mathf.Clamp(selectedRow, 0, Mathf.Max(0, players.Count - 1)); } private void ClearConflictingRoles(string id, int role) { string[] array = RoleSelection.Assign(new string[5] { rules.Selected, repairRules.Selected, tankRules.Selected, birdSelected, meleeSpecialistSelected }, role, id); if (role != 0 && rules.Selected != array[0]) { rules.Select(null); selectedName = "None"; } if (role != 1 && repairRules.Selected != array[1]) { repairRules.Select(null); mechanicName = "None"; } if (role != 2 && tankRules.Selected != array[2]) { tankRules.Select(null); tankName = "None"; TankSafely(RestoreTankMaximum); } if (role != 3 && birdSelected != array[3]) { SelectBird(null); } if (role != 4 && meleeSpecialistSelected != array[4]) { SelectMeleeSpecialist(null); } RunEngineer(delegate(NativeEngineer e) { e.Reconcile(); return true; }); } private string PlayerLabel(PlayerAvatar player) { return native.Name(player) + ((player == PlayerAvatar.instance) ? " [Host / You]" : " [Guest]"); } private void TickMedic() { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) if (!mapReady || !modEnabled.Value || Time.timeScale <= 0f || rules.Selected == null) { rules.ResetChannel(); return; } PlayerAvatar[] array = (from p in SemiFunc.PlayerGetList() where Object.op_Implicit((Object)(object)p) select p).ToArray(); PlayerAvatar medic = ((IEnumerable<PlayerAvatar>)array).FirstOrDefault((Func<PlayerAvatar, bool>)((PlayerAvatar p) => native.Id(p) == rules.Selected)); if (!native.CanUse(medic) || !Finite(((Component)medic).transform.position)) { rules.ResetChannel(); return; } selectedName = PlayerLabel(medic); PlayerAvatar val = null; if (rules.RevivesRemaining > 0) { PlayerDeathHead nearest = (from p in array select native.Head(p) into h where !TankRevivalPending(Object.op_Implicit((Object)(object)h) ? h.playerAvatar : null) && native.CanRevive(medic, h) && Within(((Component)medic).transform.position, HeadPosition(h), 3f) select h).OrderBy(delegate(PlayerDeathHead h) { //IL_0001: 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_001b: Unknown result type (might be due to invalid IL or missing references) Vector3 val2 = HeadPosition(h) - ((Component)medic).transform.position; return ((Vector3)(ref val2)).sqrMagnitude; }).ThenBy<PlayerDeathHead, string>((PlayerDeathHead h) => native.Id(h.playerAvatar), StringComparer.Ordinal).FirstOrDefault(); if (Object.op_Implicit((Object)(object)nearest) && rules.TryRevive(rules.Selected, () => ReviveTeammate(medic, nearest))) { val = nearest.playerAvatar; SetNotice(native.Name(medic) + " revived " + native.Name(val) + $". Revives remaining: {rules.RevivesRemaining}/{rules.ReviveLimit}."); } } bool eligible = medicExpression >= 1 && native.ExpressionActive(medic, medicExpression) && !HoldingBoombox(medic); if (rules.HealingTick(native.Id(medic), medic, eligible, Time.timeAsDouble)) { HealNearby(medic, array, val); } } private static bool HoldingBoombox(PlayerAvatar avatar) { Transform val = (Object.op_Implicit((Object)(object)avatar.physGrabber) ? avatar.physGrabber.grabbedObjectTransform : null); if (Object.op_Implicit((Object)(object)val)) { if (!Object.op_Implicit((Object)(object)((Component)val).GetComponentInParent<ValuableBoombox>())) { return Object.op_Implicit((Object)(object)((Component)val).GetComponentInChildren<ValuableBoombox>()); } return true; } return false; } private static Vector3 HeadPosition(PlayerDeathHead head) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) PhysGrabObject component = ((Component)head).GetComponent<PhysGrabObject>(); if (!Object.op_Implicit((Object)(object)component)) { return ((Component)head).transform.position; } return component.centerPoint; } private static bool Within(Vector3 a, Vector3 b, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (Finite(a) && Finite(b)) { Vector3 val = a - b; return GrantRules.WithinRadius(((Vector3)(ref val)).sqrMagnitude, radius); } return false; } private bool ReviveTeammate(PlayerAvatar medic, PlayerDeathHead head) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (!modEnabled.Value || rules.Selected != native.Id(medic) || !native.CanRevive(medic, head) || !Within(((Component)medic).transform.position, HeadPosition(head), 3f)) { return false; } PlayerAvatar playerAvatar = head.playerAvatar; int num = GrantRules.RevivalHealth(ConfiguredReviveHealth, native.MaxHealth(playerAvatar)); playerAvatar.Revive(false); if (native.IsDead(playerAvatar)) { return false; } int num2 = num - 1; if (num2 > 0) { try { playerAvatar.playerHealth.HealOther(num2, false); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Revive succeeded but its HP top-up failed; use consumed, no resend: " + ex)); } } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Native revive issued: " + native.Name(medic) + " -> " + native.Name(playerAvatar) + "; requestedHP=" + num + "; additionalHealing=" + num2)); return true; } private void ConfigureMedic() { rules.Configure(medicHealingLimit.Value, medicReviveLimit.Value); int num = ((medicHealingHotkey.Value == -1) ? (-1) : GrantRules.ExpressionForDigit(medicHealingHotkey.Value)); if (num != medicExpression) { rules.ResetChannel(); medicExpression = num; } } private void HealNearby(PlayerAvatar medic, PlayerAvatar[] roster, PlayerAvatar justRevived) { int num = 0; foreach (PlayerAvatar item in roster.Where((PlayerAvatar p) => (Object)(object)p != (Object)(object)medic && (Object)(object)p != (Object)(object)justRevived && native.CanUse(p) && Within(((Component)medic).transform.position, ((Component)p).transform.position, 5f)).OrderBy(delegate(PlayerAvatar p) { //IL_0006: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)p).transform.position - ((Component)medic).transform.position; return ((Vector3)(ref val)).sqrMagnitude; }).ThenBy<PlayerAvatar, string>((PlayerAvatar p) => native.Id(p), StringComparer.Ordinal)) { int num2 = rules.ReserveHealing(native.Id(medic), native.Health(item), native.MaxHealth(item)); if (num2 > 0) { item.playerHealth.HealOther(num2, true); num += num2; } } if (num > 0 && rules.HealingRemaining == 0) { SetNotice($"Ally healing exhausted for this stage: {rules.HealingUsed}/{rules.HealingLimit} HP used."); } } private static bool Finite(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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_0027: 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_0041: Unknown result type (might be due to invalid IL or missing references) if (!float.IsNaN(v.x) && !float.IsNaN(v.y) && !float.IsNaN(v.z) && !float.IsInfinity(v.x) && !float.IsInfinity(v.y)) { return !float.IsInfinity(v.z); } return false; } private void SetNotice(string text) { notice = text; noticeUntil = Time.unscaledTime + 8f; } private void DisableAfterError(Exception e) { ((Behaviour)this).enabled = false; hostReady = false; menuOpen = false; rules.SetSession(null); repairRules.SetSession(null); ((BaseUnityPlugin)this).Logger.LogError((object)("Guest-Role disabled after an error: " + e)); } private void OnDisable() { roleAnnouncements.Clear(); try { ReleaseMeleeSpecialist(); meleeSpecialistSelected = null; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)ex); } try { ReleaseBird(); birdSelected = null; } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)ex2); } RunEngineer(delegate(NativeEngineer e) { e.RestoreAll(); return true; }); try { RestoreTankMaximum(); } catch (Exception ex3) { ((BaseUnityPlugin)this).Logger.LogError((object)ex3); } tankRules.SetSession(null); tankRevivals.Clear(); tankContexts.Clear(); tankWrites.Clear(); tankHitScope.Clear(); hostReady = false; menuOpen = false; rules.SetSession(null); repairRules.SetSession(null); } private void OnDestroy() { if (Object.op_Implicit((Object)(object)panelPointer)) { Object.Destroy((Object)(object)panelPointer); } roleBridge?.Dispose(); Harmony obj = engineerPatches; if (obj != null) { obj.UnpatchSelf(); } Harmony obj2 = tankPatches; if (obj2 != null) { obj2.UnpatchSelf(); } Harmony obj3 = birdPatches; if (obj3 != null) { obj3.UnpatchSelf(); } Harmony obj4 = meleeSpecialistPatches; if (obj4 != null) { obj4.UnpatchSelf(); } Harmony obj5 = roleUpgradePatches; if (obj5 != null) { obj5.UnpatchSelf(); } if (Current == this) { Current = null; } } private static bool ConsumePanelRequest(ref bool request) { bool result = request; request = false; return result; } private void PreparePanelStyles() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //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_0082: Expected O, but got Unknown //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: 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_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_0103: Expected O, but got Unknown if (panelText == null) { panelText = new GUIStyle(GUI.skin.label) { fontSize = 17, wordWrap = true, richText = false }; panelText.normal.textColor = Color.white; panelTitle = new GUIStyle(panelText) { fontSize = 27, fontStyle = (FontStyle)1 }; panelSmall = new GUIStyle(panelText) { fontSize = 14 }; panelSmall.normal.textColor = new Color(0.79f, 0.8f, 0.82f); panelButton = new GUIStyle(panelText) { alignment = (TextAnchor)4, fontStyle = (FontStyle)1, clipping = (TextClipping)1 }; panelRow = new GUIStyle(panelText) { alignment = (TextAnchor)3, clipping = (TextClipping)1, wordWrap = false, padding = new RectOffset(16, 16, 0, 0) }; } } private static void PanelFill(Rect rect, Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) Color color2 = GUI.color; GUI.color = color; GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture); GUI.color = color2; } private bool PanelButton(Rect rect, string text, bool selected = false, bool row = false) { //IL_0007: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Invalid comparison between Unknown and I4 //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Expected O, but got Unknown //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) bool num = ((Rect)(ref rect)).Contains(Event.current.mousePosition); Color val = (selected ? new Color(0.78f, 0.23f, 0.25f) : new Color(0.22f, 0.2f, 0.23f)); if (num && GUI.enabled) { val = Color.Lerp(val, Color.white, 0.12f); } if (!GUI.enabled) { val *= 0.6f; } PanelFill(rect, val); if (selected) { PanelFill(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 4f, ((Rect)(ref rect)).height), new Color(1f, 0.55f, 0.5f)); } if (text == "Tank") { bool result = GUI.Button(rect, GUIContent.none, panelButton); if ((int)Event.current.type == 7) { GUIStyle val2 = new GUIStyle(panelButton) { alignment = (TextAnchor)3 }; float num2 = 0f; string text2 = text; for (int i = 0; i < text2.Length; i++) { num2 += val2.CalcSize(new GUIContent(text2[i].ToString())).x; } float num3 = ((Rect)(ref rect)).x + (((Rect)(ref rect)).width - num2) / 2f; text2 = text; for (int i = 0; i < text2.Length; i++) { GUIContent val3 = new GUIContent(text2[i].ToString()); float x = val2.CalcSize(val3).x; val2.Draw(new Rect(num3, ((Rect)(ref rect)).y, x + 2f, ((Rect)(ref rect)).height), val3, false, false, false, false); num3 += x; } } return result; } return GUI.Button(rect, text, row ? panelRow : panelButton); } private void OnGUI() { //IL_06bd: Unknown result type (might be due to invalid IL or missing references) //IL_06c3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: 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_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_020d: 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_006a: 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_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03bb: Unknown result type (might be due to invalid IL or missing references) //IL_03e8: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Unknown result type (might be due to invalid IL or missing references) //IL_03f2: Unknown result type (might be due to invalid IL or missing references) //IL_0519: Unknown result type (might be due to invalid IL or missing references) //IL_052d: Unknown result type (might be due to invalid IL or missing references) //IL_0551: Unknown result type (might be due to invalid IL or missing references) //IL_04e5: Unknown result type (might be due to invalid IL or missing references) //IL_048b: Unknown result type (might be due to invalid IL or missing references) //IL_05b7: Unknown result type (might be due to invalid IL or missing references) //IL_05fd: Unknown result type (might be due to invalid IL or missing references) //IL_0643: Unknown result type (might be due to invalid IL or missing references) //IL_0682: Unknown result type (might be due to invalid IL or missing references) //IL_0692: Unknown result type (might be due to invalid IL or missing references) //IL_0697: Unknown result type (might be due to invalid IL or missing references) //IL_06dd: Unknown result type (might be due to invalid IL or missing references) //IL_06e3: Invalid comparison between Unknown and I4 if (!hostReady || !((Behaviour)this).enabled) { return; } PreparePanelStyles(); if (!menuOpen) { if (Time.unscaledTime < noticeUntil) { PanelFill(new Rect(20f, 20f, (float)Mathf.Min(680, Screen.width - 40), 85f), new Color(0.12f, 0.12f, 0.14f, 0.94f)); GUI.Label(new Rect(32f, 30f, (float)Mathf.Min(656, Screen.width - 64), 65f), notice, panelText); } return; } Matrix4x4 matrix = GUI.matrix; Color color = GUI.color; bool enabled = GUI.enabled; try { PanelFill(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), new Color(0f, 0f, 0f, 0.5f)); float num = 0.5f * Mathf.Min((float)Screen.width / 1000f, (float)Screen.height / 820f); float num2 = (float)Screen.width / num; float num3 = (float)Screen.height / num; GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, Vector3.one * num); float num4 = (num2 - 920f) / 2f; float num5 = (num3 - 748f) / 2f; PanelFill(new Rect(num4, num5, 920f, 748f), new Color(0.29f, 0.29f, 0.3f)); PanelFill(new Rect(num4, num5, 920f, 66f), new Color(0.55f, 0.13f, 0.14f)); GUI.Label(new Rect(num4 + 26f, num5 + 17f, 760f, 40f), "Guest-Role / ROLE ASSIGNMENT", panelTitle); if (PanelButton(new Rect(num4 + 858f, num5 + 14f, 44f, 38f), "X")) { menuOpen = false; } GUI.Label(new Rect(num4 + 26f, num5 + 82f, 840f, 25f), "01 SELECT ROLE", panelSmall); for (int i = 0; i < PanelRoles.Length; i++) { if (PanelButton(new Rect(num4 + 26f + (float)(i * 175), num5 + 112f, 166f, 48f), PanelRoles[i], selectedRole == i)) { selectedRole = i; } } GUI.Label(new Rect(num4 + 26f, num5 + 179f, 840f, 26f), "02 SELECT PLAYER / " + players.Count + " connected", panelSmall); Rect val = default(Rect); ((Rect)(ref val))..ctor(num4 + 26f, num5 + 212f, 868f, 214f); PanelFill(val, new Color(0.16f, 0.16f, 0.18f)); if (lastPanelRow != selectedRow) { float num6 = selectedRow * 52; if (num6 < panelScroll.y) { panelScroll.y = num6; } if (num6 + 50f > panelScroll.y + 214f) { panelScroll.y = num6 + 50f - 214f; } lastPanelRow = selectedRow; } panelScroll = GUI.BeginScrollView(val, panelScroll, new Rect(0f, 0f, 842f, (float)Mathf.Max(214, players.Count * 52))); for (int j = 0; j < players.Count; j++) { if (Object.op_Implicit((Object)(object)players[j])) { string text = GrantedRoles(players[j]); string text2 = j + 1 + ". " + PlayerLabel(players[j]) + (string.IsNullOrEmpty(text) ? " [No role]" : (" " + text)); if (PanelButton(new Rect(4f, (float)(j * 52 + 3), 834f, 46f), text2, selectedRow == j, row: true)) { selectedRow = j; } } } if (players.Count == 0) { GUI.Label(new Rect(16f, 18f, 810f, 80f), "Waiting for players...\nOpen this panel after your character appears.", panelText); } GUI.EndScrollView(); PanelFill(new Rect(num4 + 26f, num5 + 443f, 868f, 112f), new Color(0.23f, 0.23f, 0.25f)); GUI.Label(new Rect(num4 + 42f, num5 + 454f, 836f, 94f), RoleDescription, panelText); string text3 = ((PanelError != null) ? ("Unavailable: " + PanelError) : ((Time.unscaledTime < noticeUntil) ? notice : "One role per player. Assigning a role replaces their previous role.")); GUI.Label(new Rect(num4 + 26f, num5 + 570f, 868f, 54f), text3, panelSmall); GUI.enabled = enabled && players.Count > 0; if (PanelButton(new Rect(num4 + 26f, num5 + 638f, 560f, 56f), "ASSIGN " + RoleName, selected: true)) { panelGrantRequested = true; } GUI.enabled = enabled; if (PanelButton(new Rect(num4 + 602f, num5 + 638f, 292f, 56f), "CLEAR " + RoleName)) { panelClearRequested = true; } GUI.Label(new Rect(num4 + 26f, num5 + 712f, 868f, 24f), "Mouse: select Arrows: choose Enter: assign Backspace: clear role " + ((object)menuKey.Value/*cast due to .constrained prefix*/).ToString() + " / Esc: close", panelSmall); } finally { GUI.matrix = matrix; GUI.color = color; GUI.enabled = enabled; } if (menuOpen && (int)Event.current.type == 7) { DrawPanelPointer(); } } private void DrawPanelPointer() { //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0148: 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_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)panelPointer)) { panelPointer = new Texture2D(24, 32, (TextureFormat)4, false); ((Texture)panelPointer).filterMode = (FilterMode)0; Color[] array = (Color[])(object)new Color[768]; for (int i = 0; i < 32; i++) { for (int j = 0; j < 24; j++) { bool flag = i < 25 && (float)j <= (float)i * 0.64f; bool flag2 = i >= 17 && i < 31 && j >= 7 && j <= 12; bool flag3 = i >= 4 && i < 22 && j >= 2 && (float)j <= (float)i * 0.64f - 2f; bool flag4 = i >= 18 && i < 29 && j >= 9 && j <= 10; array[(31 - i) * 24 + j] = ((flag || flag2) ? ((flag3 || flag4) ? Color.white : Color.black) : Color.clear); } } panelPointer.SetPixels(array); panelPointer.Apply(); } Matrix4x4 matrix = GUI.matrix; Color color = GUI.color; try { GUI.matrix = Matrix4x4.identity; GUI.color = Color.white; Vector3 mousePosition = Input.mousePosition; float num = Mathf.Max(1f, (float)Screen.height / 1080f); GUI.DrawTexture(new Rect(mousePosition.x, (float)Screen.height - mousePosition.y, 24f * num, 32f * num), (Texture)(object)panelPointer); } finally { GUI.matrix = matrix; GUI.color = color; } } private void AnnounceRole(PlayerAvatar player, string id, int role, string text) { roleAnnouncements.RemoveAll((RoleAnnouncement a) => a.Id == id); roleAnnouncements.Add(new RoleAnnouncement { Player = player, Id = id, Role = role, Text = text, Room = session, Deadline = Time.unscaledTime + 15f }); TickRoleSpeech(); } private void TickRoleSpeech() { //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Invalid comparison between Unknown and I4 for (int num = roleAnnouncements.Count - 1; num >= 0; num--) { RoleAnnouncement roleAnnouncement = roleAnnouncements[num]; string text = ((roleAnnouncement.Role == 0) ? rules.Selected : ((roleAnnouncement.Role == 1) ? repairRules.Selected : ((roleAnnouncement.Role == 2) ? tankRules.Selected : ((roleAnnouncement.Role == 3) ? birdSelected : meleeSpecialistSelected)))); if (!hostReady || !modEnabled.Value || roleAnnouncement.Room != session || !Object.op_Implicit((Object)(object)roleAnnouncement.Player) || text != roleAnnouncement.Id || Time.unscaledTime > roleAnnouncement.Deadline) { roleAnnouncements.RemoveAt(num); } else { object? obj = roleVoiceChat?.GetValue(roleAnnouncement.Player); PlayerVoiceChat val = (PlayerVoiceChat)((obj is PlayerVoiceChat) ? obj : null); if (Object.op_Implicit((Object)(object)GameDirector.instance) && (int)GameDirector.instance.currentState == 2 && Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.ttsVoice)) { roleAnnouncements.RemoveAt(num); try { roleAnnouncement.Player.ChatMessageSend(roleAnnouncement.Text); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Role TTS could not be sent; role assignment retained: " + ex.GetBaseException().Message)); } } } } } private bool IsTank(PlayerAvatar player) { if (Object.op_Implicit((Object)(object)player) && tankRules.Selected != null) { return native.Id(player) == tankRules.Selected; } return false; } private void InitializeTank() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Expected O, but got Unknown try { tankLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "TankLogging", false, "Log Tank damage observations and cover decisions (rate-limited). F8 also shows the latest result."); if (headTimer == null || healthReady == null || detectionOnly == null || ignoreLocal == null || tankRpc == null) { throw new MissingMemberException("Tank game API changed."); } tankPatches = new Harmony("Guest-Role.Tank"); Patch(typeof(HurtCollider), "PlayerHurt", "TankHitPrefix", null, "TankHitFinalizer"); Patch(typeof(PlayerHealth), "Hurt", "TankHurtPrefix", "TankHurtPostfix", "TankContextFinalizer"); Patch(typeof(PlayerHealth), "UpdateHealthRPC", "TankHealthPrefix", "TankHealthPostfix", "TankContextFinalizer"); Type type = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "RoleShuffle")?.GetType("REPOJP.StageRoles.EventRoleRuntime"); MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "ApplyBodyguardTransfer", (Type[])null, (Type[])null)); if (methodInfo != null) { tankPatches.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "TankOriginalBodyguardPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { Harmony obj = tankPatches; if (obj != null) { obj.UnpatchSelf(); } tankError = ex.GetBaseException().Message; ((BaseUnityPlugin)this).Logger.LogError((object)("Tank unavailable; Medic and Mechanic retained: " + ex)); } static HarmonyMethod Hook(string name) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (name != null) { return new HarmonyMethod(typeof(Plugin), name, (Type[])null); } return null; } void Patch(Type type2, string method, string prefix = null, string postfix = null, string finalizer = null) { tankPatches.Patch((MethodBase)AccessTools.Method(type2, method, (Type[])null, (Type[])null), Hook(prefix), Hook(postfix), (HarmonyMethod)null, Hook(finalizer), (HarmonyMethod)null); } } private void TankSafely(Action action) { if (tankError != null) { return; } try { action(); } catch (Exception ex) { tankError = ex.GetBaseException().Message; tankRules.ClearHits(); ((BaseUnityPlugin)this).Logger.LogError((object)("Tank stopped after an error; no uncertain transfer is retried: " + ex)); try { RestoreTankMaximum(); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)ex2); } } } private PlayerAvatar CurrentTank() { return ((IEnumerable<PlayerAvatar>)SemiFunc.PlayerGetList()).FirstOrDefault((Func<PlayerAvatar, bool>)((PlayerAvatar p) => Object.op_Implicit((Object)(object)p) && native.Id(p) == tankRules.Selected)); } private void TankDiagnostic(string result, PlayerAvatar victim, int before, int after) { //IL_003e: 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) if (tankRules.Selected != null && Object.op_Implicit((Object)(object)GameDirector.instance)) { PlayerAvatar val = CurrentTank(); string text = ((Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)victim)) ? (Vector3.Distance(((Component)val).transform.position, ((Component)victim).transform.position).ToString("0.00") + "m") : "unknown"); tankLastResult = result + " | " + (Object.op_Implicit((Object)(object)victim) ? PlayerLabel(victim) : "unknown") + " " + before + "->" + after + " | Tank HP=" + native.Health(val) + " | distance=" + text; if (tankLogging != null && tankLogging.Value && Time.unscaledTime >= nextTankDiagnostic) { nextTankDiagnostic = Time.unscaledTime + 0.5f; ((BaseUnityPlugin)this).Logger.LogInfo((object)("[Tank] " + tankLastResult + " | tank=" + (Object.op_Implicit((Object)(object)val) ? PlayerLabel(val) : "missing") + " | active=" + TankActive + " | living=" + native.CanUse(val))); } } } private PlayerAvatar CoveringTank(PlayerAvatar victim) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if (!TankActive || !Object.op_Implicit((Object)(object)victim) || tankRules.Selected == null) { return null; } PlayerAvatar val = CurrentTank(); if (Object.op_Implicit((Object)(object)val) && (Object)(object)val != (Object)(object)victim && native.CanUse(val) && native.Health(val) > 1 && Finite(((Component)val).transform.position) && Finite(((Component)victim).transform.position)) { Vector3 val2 = ((Component)val).transform.position - ((Component)victim).transform.position; if (TankRules.InRange(((Vector3)(ref val2)).sqrMagnitude)) { return val; } } return null; } private void TickTank() { if (tankRoom != session) { tankRoom = session; tankStage = null; tankName = "None"; tankRevivals.Clear(); tankWrites.Clear(); tankRules.SetSession(session); tankHitScope.Clear(); maxAvatar = null; maxRoom = null; } if (mapReady && tankStage != LevelGenerator.Instance) { tankStage = LevelGenerator.Instance; tankRules.SetMap(tankStage); tankRevivals.Clear(); } if (!TankAuthority || !Object.op_Implicit((Object)(object)GameDirector.instance)) { tankRules.ClearHits(); return; } PlayerAvatar val = ((tankRules.Selected == null) ? null : CurrentTank()); if (Object.op_Implicit((Object)(object)maxAvatar) && (!modEnabled.Value || (Object)(object)maxAvatar != (Object)(object)val)) { RestoreTankMaximum(); } if (modEnabled.Value && Object.op_Implicit((Object)(object)val) && native.CanUse(val) && Object.op_Implicit((Object)(object)StatsManager.instance) && (bool)healthReady.GetValue(val.playerHealth)) { tankName = PlayerLabel(val); int playerMaxHealth = StatsManager.instance.GetPlayerMaxHealth(native.Id(val)); if ((Object)(object)maxAvatar != (Object)(object)val) { maxAvatar = val; maxRoom = session; maxOriginal = native.MaxHealth(val); maxUpgrade = playerMaxHealth; } maxApplied = TankRules.Maximum(playerMaxHealth); bool flag = tankRules.InitializeHealth(native.Id(val)); if (flag || native.MaxHealth(val) != maxApplied) { SetTankHealth(val, flag ? maxApplied : Math.Min(native.Health(val), maxApplied), maxApplied, effect: false); } } if (!TankActive) { tankRules.ClearHits(); } if (mapReady) { FinishTankRevivals(); } } private void RestoreTankMaximum() { if (Object.op_Implicit((Object)(object)maxAvatar) && Object.op_Implicit((Object)(object)GameManager.instance) && SemiFunc.IsMasterClient() && maxRoom == native.Room && Object.op_Implicit((Object)(object)StatsManager.instance) && native.MaxHealth(maxAvatar) == maxApplied) { int playerMaxHealth = StatsManager.instance.GetPlayerMaxHealth(native.Id(maxAvatar)); int num = (int)Math.Max(1L, Math.Min(2147483647L, (long)maxOriginal + (long)playerMaxHealth - maxUpgrade)); SetTankHealth(maxAvatar, Math.Min(native.Health(maxAvatar), num), num, effect: false); } maxAvatar = null; maxRoom = null; } private void SetTankHealth(PlayerAvatar player, int hp, int maximum, bool effect) { if (!Object.op_Implicit((Object)(object)GameManager.instance) || !SemiFunc.IsMasterClient() || !Object.op_Implicit((Object)(object)player) || !Ob