using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ComputerysModdingUtilities; using FishNet; using FishNet.Connection; using FishNet.Managing; using FishNet.Object; using HarmonyLib; using Microsoft.CodeAnalysis; using MyceliumNetworking; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: StraftatMod(false)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("StraftatEightsPlugin")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+5c365d7c650735dc75dbce0a28beb5d122d58f51")] [assembly: AssemblyProduct("Straftat Eights")] [assembly: AssemblyTitle("StraftatEightsPlugin")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace StraftatEightsPlugin { [BepInPlugin("StraftatEightsPlugin", "Straftat Eights", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInProcess("STRAFTAT.exe")] public class Plugin : BaseUnityPlugin { internal static ConfigEntry DefaultGameModeEnabled; internal const uint FFAModId = 2718281828u; internal static ConfigEntry FFAEnabled; internal const uint GlobalModifiersModId = 1892737465u; internal static ConfigEntry MovementTweaksEnabled; internal static ConfigEntry WallJumpEnabled; internal static ConfigEntry WallJumpBoostEnabled; internal static ConfigEntry SlidingEnabled; internal static ConfigEntry SlideBoostEnabled; internal static ConfigEntry MoveSpeedPercent; internal static ConfigEntry AdsSpeedPercent; internal static ConfigEntry GravityPercent; internal static ConfigEntry MomentumPercent; internal static ConfigEntry AirSpeedRatioPercent; internal const uint GlobalWeaponsModId = 1618033991u; internal static ConfigEntry WeaponTweaksEnabled; internal static ConfigEntry AllowedWeapons; internal static ConfigEntry SpareMagazines; internal static ConfigEntry CycleWeapons; internal const uint GunGameModId = 1618033990u; internal static ConfigEntry GunGameEnabled; internal static ConfigEntry GunGameWeaponOrder; internal const uint HealthSettingsModId = 1892737466u; internal static ConfigEntry MaxHealthPercent; internal static ConfigEntry HealthTweaksEnabled; internal static ConfigEntry HealthRegenEnabled; internal static ConfigEntry HealthRegenDelaySeconds; internal static ConfigEntry HealthRegenRate; internal const uint HotPotatoModId = 2718281830u; internal static ConfigEntry HotPotatoEnabled; internal const uint InfidelModId = 2718281831u; internal static ConfigEntry InfidelEnabled; internal const uint JuggernautModId = 3141592653u; internal static ConfigEntry JuggernautEnabled; internal const uint KillTheRatModId = 3141592655u; internal static ConfigEntry KillTheRatEnabled; internal const uint MichaelMeyersModId = 3141592654u; internal static ConfigEntry MichaelMeyersEnabled; internal const uint OneInTheChamberModId = 2718281829u; internal static ConfigEntry OneInTheChamberEnabled; internal static ManualLogSource Logger; internal static Plugin Instance; internal const uint SniperBattleModId = 1618033992u; internal static ConfigEntry SniperBattleEnabled; private void InitializeDefaultGameMode() { DefaultGameModeEnabled = ((BaseUnityPlugin)this).Config.Bind("Game Mode Settings", "Default Game Mode Enabled", false, "Host-controlled: uses normal Straftat one-life rules, map weapon spawners, and default health while keeping global movement settings active."); DefaultGameModeEnabled.SettingChanged += delegate { GameModeManager.OnSettingsChanged(); }; } private void InitializeFFA() { FFAEnabled = ((BaseUnityPlugin)this).Config.Bind("Game Mode Settings", "Free For All Enabled", false, "Host-controlled: players score ten points per kill; the first to 100 points wins."); FFAEnabled.SettingChanged += delegate { FFAState.PushSettingsIfHost(); GameModeManager.OnSettingsChanged(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 2718281828u, 0); MyceliumNetwork.LobbyCreated += FFAState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += FFAState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += FFAState.OnPlayerEntered; } [CustomRPC] public void SyncFFASettings(CSteamID hostId, int roundId, int revision, bool enabled) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (FFAState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { FFAState.ApplySettings(enabled); } } [CustomRPC] public void SyncFFALiveState(CSteamID hostId, string killsData, int winnerId, int roundId, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) FFAState.ApplyLiveState(hostId, killsData, winnerId, roundId, revision); } [CustomRPC] public void FFAAnnounce(string text) { if ((Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.WriteLog(ClientInstance.ReplaceAllPlayerNameTags(text)); } } private void InitializeGlobalModifiers() { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Expected O, but got Unknown //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Expected O, but got Unknown MovementTweaksEnabled = ((BaseUnityPlugin)this).Config.Bind("Movement Settings", "Movement Tweaks Enabled", true, "Host-controlled: master switch. Turn off for pure stock Straftat movement."); WallJumpEnabled = ((BaseUnityPlugin)this).Config.Bind("Movement Settings", "Enable Wall Jump", true, "Host-controlled: allows wall jumping for everyone in the lobby."); WallJumpBoostEnabled = ((BaseUnityPlugin)this).Config.Bind("Movement Settings", "Enable Wall Jump Speed Boost", true, "Host-controlled: whether wall jumping gives the usual Straftat horizontal speed kick (chaining wall jumps for extra speed)."); SlidingEnabled = ((BaseUnityPlugin)this).Config.Bind("Movement Settings", "Enable Sliding", true, "Host-controlled: allows crouch-sliding for everyone in the lobby."); SlideBoostEnabled = ((BaseUnityPlugin)this).Config.Bind("Movement Settings", "Enable Slide Speed Boost", true, "Host-controlled: whether sliding gives the usual Straftat speed boost. Disable to slide like most other games."); MoveSpeedPercent = ((BaseUnityPlugin)this).Config.Bind("Movement Settings", "Move Speed %", 100, new ConfigDescription("Host-controlled: overall movement speed as a percent of normal.", (AcceptableValueBase)(object)new AcceptableValueRange(50, 200), Array.Empty())); AdsSpeedPercent = ((BaseUnityPlugin)this).Config.Bind("Movement Settings", "ADS Speed %", 100, new ConfigDescription("Host-controlled: movement speed while aiming down sights, as a percent of normal.", (AcceptableValueBase)(object)new AcceptableValueRange(50, 100), Array.Empty())); GravityPercent = ((BaseUnityPlugin)this).Config.Bind("Movement Settings", "Gravity %", 100, new ConfigDescription("Host-controlled: gravity as a percent of normal. Lower = floatier jumps and slower falling.", (AcceptableValueBase)(object)new AcceptableValueRange(10, 100), Array.Empty())); MomentumPercent = ((BaseUnityPlugin)this).Config.Bind("Movement Settings", "Momentum %", 100, new ConfigDescription("Host-controlled: how much weight/inertia is behind movement. 100% = stock Straftat snappiness. Higher = heavier, slower to speed up/stop/turn. Lower = snappier, near-instant direction changes.", (AcceptableValueBase)(object)new AcceptableValueRange(10, 400), Array.Empty())); AirSpeedRatioPercent = ((BaseUnityPlugin)this).Config.Bind("Movement Settings", "Air Move Speed %", (int)MathF.Round(116.666664f), new ConfigDescription("Host-controlled: air move speed (not air control) as a percent of ground move speed. Stock Straftat is already faster sprinting in the air than on the ground (the default reflects that real ratio). Lower this to slow players down in the air.", (AcceptableValueBase)(object)new AcceptableValueRange(50, 200), Array.Empty())); WallJumpEnabled.SettingChanged += delegate { GlobalModifiersState.PushIfHost(); }; MovementTweaksEnabled.SettingChanged += delegate { GlobalModifiersState.PushIfHost(); }; SlidingEnabled.SettingChanged += delegate { GlobalModifiersState.PushIfHost(); }; SlideBoostEnabled.SettingChanged += delegate { GlobalModifiersState.PushIfHost(); }; WallJumpBoostEnabled.SettingChanged += delegate { GlobalModifiersState.PushIfHost(); }; MoveSpeedPercent.SettingChanged += delegate { GlobalModifiersState.PushIfHost(); }; AdsSpeedPercent.SettingChanged += delegate { GlobalModifiersState.PushIfHost(); }; GravityPercent.SettingChanged += delegate { GlobalModifiersState.PushIfHost(); }; MomentumPercent.SettingChanged += delegate { GlobalModifiersState.PushIfHost(); }; AirSpeedRatioPercent.SettingChanged += delegate { GlobalModifiersState.PushIfHost(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 1892737465u, 0); MyceliumNetwork.LobbyCreated += GlobalModifiersState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += GlobalModifiersState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += GlobalModifiersState.OnPlayerEntered; } [CustomRPC] public void SyncMovementSettings(CSteamID hostId, int roundId, int revision, bool enabled, bool wallJump, bool sliding, bool slideBoost, bool wallJumpBoost, int moveSpeedPercent, int adsSpeedPercent, int gravityPercent, int momentumPercent, int airSpeedRatioPercent) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (GlobalModifiersState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { Logger.LogInfo((object)$"[MovementSettings] Received sync: enabled={enabled} wallJump={wallJump} sliding={sliding} slideBoost={slideBoost} wallJumpBoost={wallJumpBoost} moveSpeed%={moveSpeedPercent} adsSpeed%={adsSpeedPercent} gravity%={gravityPercent} momentum%={momentumPercent} airSpeed%={airSpeedRatioPercent}"); GlobalModifiersState.Apply(enabled, wallJump, sliding, slideBoost, wallJumpBoost, moveSpeedPercent, adsSpeedPercent, gravityPercent, momentumPercent, airSpeedRatioPercent); } } private void InitializeGlobalWeapons() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown WeaponTweaksEnabled = ((BaseUnityPlugin)this).Config.Bind("Weapon Settings", "Weapon Tweaks Enabled", false, "Host-controlled: enables weapon override rules."); AllowedWeapons = ((BaseUnityPlugin)this).Config.Bind("Weapon Settings", "Allowed Weapons", "AK-K, AR15, Bukanee, Dispenser, HK_G11, Keso, Kusma, M2000, QCW05, Glock, Silenzzio, SMG, SmithCarbine, Warden, Yangtse", "Host-controlled: exact weapon IDs allowed on spawners and for cycling."); SpareMagazines = ((BaseUnityPlugin)this).Config.Bind("Weapon Settings", "Spare Magazines", 5, new ConfigDescription("Host-controlled: spare magazines granted with a weapon pickup.", (AcceptableValueBase)(object)new AcceptableValueRange(2, 10), Array.Empty())); CycleWeapons = ((BaseUnityPlugin)this).Config.Bind("Weapon Settings", "F8 Cycle Weapons", false, "Host-controlled: F8 cycles through allowed weapons when no active mode owns weapon loadouts, and disables weapon droppers."); WeaponTweaksEnabled.SettingChanged += delegate { WeaponSettingsState.PushIfHost(); }; AllowedWeapons.SettingChanged += delegate { WeaponSettingsState.PushIfHost(); }; SpareMagazines.SettingChanged += delegate { WeaponSettingsState.PushIfHost(); }; CycleWeapons.SettingChanged += delegate { WeaponSettingsState.PushIfHost(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 1618033991u, 0); MyceliumNetwork.LobbyCreated += WeaponSettingsState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += WeaponSettingsState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += WeaponSettingsState.OnPlayerEntered; } [CustomRPC] public void SyncWeaponSettings(CSteamID hostId, int roundId, int revision, bool enabled, string allowedWeapons, int spareMagazines, bool cycleWeapons) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (WeaponSettingsState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { WeaponSettingsState.Apply(enabled, allowedWeapons, spareMagazines, cycleWeapons); } } [CustomRPC] public void RequestWeaponCycle(int playerId) { if (MyceliumNetwork.IsHost && WeaponSettingsState.Enabled && WeaponSettingsState.Cycle) { WeaponSettingsState.GiveCycledWeapon(playerId); } } [CustomRPC] public void AttachServerGrantedWeapon(int playerId) { WeaponService.AttachGrantedWeaponForOwner(playerId); } private void InitializeGunGame() { GunGameEnabled = ((BaseUnityPlugin)this).Config.Bind("Game Mode Settings", "Gun Game Enabled", false, "Host-controlled: players advance through the weapon list with each kill."); GunGameWeaponOrder = ((BaseUnityPlugin)this).Config.Bind("Game Mode Settings", "Gun Game Weapon Order", "Glock, Webley, SMG, Bukanee, SawedOff, Shotgun, Yangtse, Kusma, AR15, AK-K, QCW05, FG42, HK_G11, SmithCarbine, M2000, BaseballBat", "Host-controlled: exact prefab IDs in progression order."); GunGameEnabled.SettingChanged += delegate { GunGameState.PushSettingsIfHost(); GameModeManager.OnSettingsChanged(); }; GunGameWeaponOrder.SettingChanged += delegate { GunGameState.PushSettingsIfHost(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 1618033990u, 0); MyceliumNetwork.LobbyCreated += GunGameState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += GunGameState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += GunGameState.OnPlayerEntered; } [CustomRPC] public void SyncGunGameSettings(CSteamID hostId, int roundId, int revision, bool enabled, string weaponOrder) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (GunGameState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { GunGameState.ApplySettings(enabled, weaponOrder); } } [CustomRPC] public void SyncGunGameLiveState(CSteamID hostId, string progressData, int roundId, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) GunGameState.ApplyLiveState(hostId, progressData, roundId, revision); } private void InitializeHealthSettings() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown HealthTweaksEnabled = ((BaseUnityPlugin)this).Config.Bind("Health Settings", "Health Tweaks Enabled", true, "Host-controlled: master switch. Turn off to restore normal health and disable regeneration."); MaxHealthPercent = ((BaseUnityPlugin)this).Config.Bind("Health Settings", "Max Health %", 100, new ConfigDescription("Host-controlled: max health as a percent of normal.", (AcceptableValueBase)(object)new AcceptableValueRange(10, 400), Array.Empty())); HealthRegenEnabled = ((BaseUnityPlugin)this).Config.Bind("Health Settings", "Enable Health Regen", true, "Host-controlled: enables health regeneration after taking damage."); HealthRegenDelaySeconds = ((BaseUnityPlugin)this).Config.Bind("Health Settings", "Regen Delay (seconds)", 5, new ConfigDescription("Host-controlled: delay after taking damage before health regeneration starts.", (AcceptableValueBase)(object)new AcceptableValueRange(2, 15), Array.Empty())); HealthRegenRate = ((BaseUnityPlugin)this).Config.Bind("Health Settings", "Regen Rate (health per second)", 150, new ConfigDescription("Host-controlled: displayed HUD health restored per second after the regen delay.", (AcceptableValueBase)(object)new AcceptableValueList(new int[6] { 50, 100, 150, 200, 300, 400 }), Array.Empty())); if (HealthRegenRate.Value != 50 && HealthRegenRate.Value != 100 && HealthRegenRate.Value != 150 && HealthRegenRate.Value != 200 && HealthRegenRate.Value != 300 && HealthRegenRate.Value != 400) { HealthRegenRate.Value = 150; } HealthTweaksEnabled.SettingChanged += delegate { HealthSettingsState.PushIfHost(); }; MaxHealthPercent.SettingChanged += delegate { HealthSettingsState.PushIfHost(); }; HealthRegenEnabled.SettingChanged += delegate { HealthSettingsState.PushIfHost(); }; HealthRegenDelaySeconds.SettingChanged += delegate { HealthSettingsState.PushIfHost(); }; HealthRegenRate.SettingChanged += delegate { HealthSettingsState.PushIfHost(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 1892737466u, 0); MyceliumNetwork.LobbyCreated += HealthSettingsState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += HealthSettingsState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += HealthSettingsState.OnPlayerEntered; } [CustomRPC] public void SyncHealthSettings(CSteamID hostId, int roundId, int revision, bool enabled, int maxHealthPercent, bool regenEnabled, int regenDelaySeconds, int regenRate) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (HealthSettingsState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { Logger.LogInfo((object)$"[HealthSettings] Received sync: tweaks={enabled} maxHealth%={maxHealthPercent} regen={regenEnabled} delay={regenDelaySeconds:0.##} rate={regenRate}"); HealthSettingsState.Apply(enabled, maxHealthPercent, regenEnabled, regenDelaySeconds, regenRate); } } private void InitializeHotPotato() { HotPotatoEnabled = ((BaseUnityPlugin)this).Config.Bind("Game Mode Settings", "Hot Potato Enabled", false, "Host-controlled: one player has a BaseballBat, all other players have Shotgun, and kills score points."); HotPotatoEnabled.SettingChanged += delegate { HotPotatoState.PushSettingsIfHost(); GameModeManager.OnSettingsChanged(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 2718281830u, 0); MyceliumNetwork.LobbyCreated += HotPotatoState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += HotPotatoState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += HotPotatoState.OnPlayerEntered; } [CustomRPC] public void SyncHotPotatoSettings(CSteamID hostId, int roundId, int revision, bool enabled) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (HotPotatoState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { HotPotatoState.ApplySettings(enabled); } } [CustomRPC] public void SyncHotPotatoLiveState(CSteamID hostId, string killsData, int potatoPlayerId, int winnerId, int roundId, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) HotPotatoState.ApplyLiveState(hostId, killsData, potatoPlayerId, winnerId, roundId, revision); } [CustomRPC] public void HotPotatoAnnounce(string text) { if ((Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.WriteLog(ClientInstance.ReplaceAllPlayerNameTags(text)); } } private void InitializeInfidel() { InfidelEnabled = ((BaseUnityPlugin)this).Config.Bind("Game Mode Settings", "Infidel Enabled", false, "Host-controlled: one private Infidel role, delayed AK loadouts, slow movement, and role-based scoring."); InfidelEnabled.SettingChanged += delegate { InfidelState.PushSettingsIfHost(); GameModeManager.OnSettingsChanged(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 2718281831u, 0); MyceliumNetwork.LobbyCreated += InfidelState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += InfidelState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += InfidelState.OnPlayerEntered; } [CustomRPC] public void SyncInfidelSettings(CSteamID hostId, int roundId, int revision, bool enabled) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (InfidelState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { InfidelState.ApplySettings(enabled); } } [CustomRPC] public void SyncInfidelLiveState(CSteamID hostId, string scoresData, int winnerId, int subRoundId, bool weaponsUnlocked, int roundId, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) InfidelState.ApplyLiveState(hostId, scoresData, winnerId, subRoundId, weaponsUnlocked, roundId, revision); } [CustomRPC] public void SyncInfidelRole(CSteamID hostId, int subRoundId, bool isInfidel, bool announce) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) InfidelState.ApplyLocalRole(hostId, subRoundId, isInfidel, announce); } [CustomRPC] public void InfidelAnnounce(string text) { if ((Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.WriteLog(ClientInstance.ReplaceAllPlayerNameTags(text)); } } private void InitializeJuggernaut() { JuggernautEnabled = ((BaseUnityPlugin)this).Config.Bind("Game Mode Settings", "Juggernaut Enabled", false, "Host-controlled: first blood becomes the Juggernaut; everyone else hunts them for the crown."); JuggernautEnabled.SettingChanged += delegate { JuggernautState.PushSettingsIfHost(); GameModeManager.OnSettingsChanged(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 3141592653u, 0); MyceliumNetwork.LobbyCreated += JuggernautState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += JuggernautState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += JuggernautState.OnPlayerEntered; } [CustomRPC] public void SyncJuggernautSettings(CSteamID hostId, int roundId, int revision, bool enabled) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (JuggernautState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { Logger.LogInfo((object)$"[Juggernaut] Received settings sync: enabled={enabled}"); JuggernautState.ApplySettings(enabled); } } [CustomRPC] public void SyncJuggernautLiveState(CSteamID hostId, int juggernautPlayerId, int juggernautKills, string pointsData, int roundId, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) JuggernautState.ApplyLiveState(hostId, juggernautPlayerId, juggernautKills, pointsData, roundId, revision); } [CustomRPC] public void JuggernautAnnounce(string text) { if ((Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.WriteLog(ClientInstance.ReplaceAllPlayerNameTags(text)); } } [CustomRPC] public void JuggernautAnnounceTarget(string text) { GameModeHud.AnnounceTarget(ClientInstance.ReplaceAllPlayerNameTags(text)); } private void InitializeKillTheRat() { KillTheRatEnabled = ((BaseUnityPlugin)this).Config.Bind("Game Mode Settings", "Kill the Rat Enabled", false, "Host-controlled: the Rat gains one point per second; players gain ten points for killing the Rat."); KillTheRatEnabled.SettingChanged += delegate { KillTheRatState.PushSettingsIfHost(); GameModeManager.OnSettingsChanged(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 3141592655u, 0); MyceliumNetwork.LobbyCreated += KillTheRatState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += KillTheRatState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += KillTheRatState.OnPlayerEntered; } [CustomRPC] public void SyncKillTheRatSettings(CSteamID hostId, int roundId, int revision, bool enabled) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (KillTheRatState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { KillTheRatState.ApplySettings(enabled); } } [CustomRPC] public void SyncKillTheRatLiveState(CSteamID hostId, int ratPlayerId, string pointsData, int winnerId, int roundId, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) KillTheRatState.ApplyLiveState(hostId, ratPlayerId, pointsData, winnerId, roundId, revision); } [CustomRPC] public void KillTheRatAnnounce(string text) { if ((Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.WriteLog(ClientInstance.ReplaceAllPlayerNameTags(text)); } } private void InitializeMichaelMeyers() { MichaelMeyersEnabled = ((BaseUnityPlugin)this).Config.Bind("Game Mode Settings", "Michael Meyers Enabled", false, "Host-controlled: one player hunts the other players with a Couperet; the last player alive wins."); MichaelMeyersEnabled.SettingChanged += delegate { MichaelMeyersState.PushSettingsIfHost(); GameModeManager.OnSettingsChanged(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 3141592654u, 0); MyceliumNetwork.LobbyCreated += MichaelMeyersState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += MichaelMeyersState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += MichaelMeyersState.OnPlayerEntered; } [CustomRPC] public void SyncMichaelMeyersSettings(CSteamID hostId, int roundId, int revision, bool enabled) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (MichaelMeyersState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { MichaelMeyersState.ApplySettings(enabled); } } [CustomRPC] public void SyncMichaelMeyersLiveState(CSteamID hostId, int michaelPlayerId, int survivorCount, bool oneVsOne, int roundId, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) MichaelMeyersState.ApplyLiveState(hostId, michaelPlayerId, survivorCount, oneVsOne, roundId, revision); } [CustomRPC] public void MichaelMeyersAnnounce(string text) { if ((Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.WriteLog(ClientInstance.ReplaceAllPlayerNameTags(text)); } } private void InitializeOneInTheChamber() { OneInTheChamberEnabled = ((BaseUnityPlugin)this).Config.Bind("Game Mode Settings", "One in the Chamber Enabled", false, "Host-controlled: players get one Pistol shot, a Couperet, and no respawns; the last player alive wins."); OneInTheChamberEnabled.SettingChanged += delegate { OneInTheChamberState.PushSettingsIfHost(); GameModeManager.OnSettingsChanged(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 2718281829u, 0); MyceliumNetwork.LobbyCreated += OneInTheChamberState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += OneInTheChamberState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += OneInTheChamberState.OnPlayerEntered; } [CustomRPC] public void SyncOneInTheChamberSettings(CSteamID hostId, int roundId, int revision, bool enabled) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (OneInTheChamberState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { OneInTheChamberState.ApplySettings(enabled); } } [CustomRPC] public void SyncOneInTheChamberLiveState(CSteamID hostId, string aliveData, string bulletsData, int roundId, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) OneInTheChamberState.ApplyLiveState(hostId, aliveData, bulletsData, roundId, revision); } [CustomRPC] public void OneInTheChamberAnnounce(string text) { if ((Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.WriteLog(ClientInstance.ReplaceAllPlayerNameTags(text)); } } private void Awake() { //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Expected O, but got Unknown Instance = this; Logger = ((BaseUnityPlugin)this).Logger; InitializeSafely("compatibility checks", FishNetCompatibility.LogPreflight); InitializeSafely("weapon service", WeaponService.Initialize); InitializeSafely("game mode manager", GameModeManager.Initialize); InitializeSafely("global modifiers", InitializeGlobalModifiers); InitializeSafely("health settings", InitializeHealthSettings); InitializeSafely("global weapons", InitializeGlobalWeapons); InitializeSafely("default game mode", InitializeDefaultGameMode); InitializeSafely("Michael Meyers", InitializeMichaelMeyers); InitializeSafely("Kill the Rat", InitializeKillTheRat); InitializeSafely("One in the Chamber", InitializeOneInTheChamber); InitializeSafely("Hot Potato", InitializeHotPotato); InitializeSafely("Infidel", InitializeInfidel); InitializeSafely("Free For All", InitializeFFA); InitializeSafely("Juggernaut", InitializeJuggernaut); InitializeSafely("Gun Game", InitializeGunGame); InitializeSafely("Sniper Battle", InitializeSniperBattle); try { ((Component)this).gameObject.AddComponent(); } catch (Exception ex) { Logger.LogError((object)("[GameModeHud] Startup failed: " + ex.GetBaseException().Message)); } PatchAllSafely(new Harmony("StraftatEightsPlugin")); Logger.LogInfo((object)"Plugin StraftatEightsPlugin is loaded!"); } private static void InitializeSafely(string featureName, Action initialize) { try { initialize(); } catch (Exception ex) { Logger.LogError((object)("[Startup] Failed to initialize " + featureName + ": " + ex.GetBaseException().Message)); } } private static void PatchAllSafely(Harmony harmony) { Type[] array; try { array = (from type2 in AccessTools.GetTypesFromAssembly(typeof(Plugin).Assembly) where type2.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0 select type2).OrderBy((Type type2) => type2.FullName, StringComparer.Ordinal).ToArray(); } catch (Exception ex) { Logger.LogError((object)("[Harmony] Could not discover patch classes: " + ex.GetBaseException().Message)); return; } Type[] array2 = array; foreach (Type type in array2) { try { int num2 = harmony.CreateClassProcessor(type).Patch().Count(); if (num2 == 0) { HarmonyPatchStatus.RecordFailure(type, "no patch target was installed"); Logger.LogError((object)("[Harmony] Disabled patch class " + type.FullName + ": no patch target was installed.")); } else { Logger.LogInfo((object)$"[Harmony] Patched {type.FullName} ({num2} method(s))."); } } catch (Exception ex2) { HarmonyPatchStatus.RecordFailure(type, ex2.GetBaseException().Message); Logger.LogError((object)("[Harmony] Disabled patch class " + type.FullName + ": " + ex2.GetBaseException().Message)); } } } private void Update() { GameModeManager.PeriodicPushIfHost(); GameModeManager.PeriodicActiveModePushIfHost(); GlobalModifiersState.PeriodicPushIfHost(); HealthSettingsState.PeriodicPushIfHost(); HealthSettingsState.ServerTick(); WeaponSettingsState.UpdateLocalCycle(); WeaponSettingsState.PeriodicPushIfHost(); WeaponSettingsState.EnsureCycleLoadouts(); GameModeManager.EnsureActiveModeLoadouts(); JuggernautOutline.EnforceOutline(); MichaelMeyersOutline.EnforceOutline(); KillTheRatOutline.EnforceOutline(); } [CustomRPC] public void SyncScorePopup(int amount) { if (amount > 0 && amount <= 100) { GameModeHud.ShowScorePopup(amount); } } [CustomRPC] public void SyncGlobalSettings(CSteamID hostId, int roundId, int revision, float respawnDelaySeconds, int pointsToWin) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (GameModeManager.TryAcceptGlobalSettingsSnapshot(hostId, roundId, revision)) { GameModeManager.ApplyGlobalSettings(respawnDelaySeconds, pointsToWin); } } [CustomRPC] public void SyncActiveGameMode(CSteamID hostId, int mode, int roundId, int phase, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (GameModeManager.TryAcceptActiveModeSnapshot(hostId, roundId, revision)) { GameModeManager.ApplyActiveMode(mode, roundId, phase); } } private void InitializeSniperBattle() { SniperBattleEnabled = ((BaseUnityPlugin)this).Config.Bind("Game Mode Settings", "Sniper Battle Enabled", false, "Host-controlled: players respawn with only the M2000, which has unlimited ammo, and score ten points per kill."); SniperBattleEnabled.SettingChanged += delegate { SniperBattleState.PushSettingsIfHost(); GameModeManager.OnSettingsChanged(); }; MyceliumNetwork.RegisterNetworkObject((object)this, 1618033992u, 0); MyceliumNetwork.LobbyCreated += SniperBattleState.OnLobbyEntered; MyceliumNetwork.LobbyEntered += SniperBattleState.OnLobbyEntered; MyceliumNetwork.PlayerEntered += SniperBattleState.OnPlayerEntered; } [CustomRPC] public void SyncSniperBattleSettings(CSteamID hostId, int roundId, int revision, bool enabled) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (SniperBattleState.TryAcceptSettingsSnapshot(hostId, roundId, revision)) { SniperBattleState.ApplySettings(enabled); } } [CustomRPC] public void SyncSniperBattleLiveState(CSteamID hostId, string pointsData, int winnerId, int roundId, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) SniperBattleState.ApplyLiveState(hostId, pointsData, winnerId, roundId, revision); } [CustomRPC] public void SniperBattleAnnounce(string text) { if ((Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.WriteLog(ClientInstance.ReplaceAllPlayerNameTags(text)); } } } internal static class FFAState { internal static bool Enabled; internal static int WinnerId = -1; internal static readonly Dictionary Kills = new Dictionary(); private static readonly ModeSyncState Sync = new ModeSyncState(); internal static int KillsToWin => GameModeManager.EffectivePointsToWin; internal static void ApplySettings(bool enabled) { bool num = Enabled != enabled; Enabled = enabled; if (num) { ResetMatchState(); } } private static void ApplySettingsFromHostConfig() { ApplySettings(Plugin.FFAEnabled.Value); } internal static void PushSettingsIfHost() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); MyceliumNetwork.RPC(2718281828u, "SyncFFASettings", (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.NextSettingsRevision(), Plugin.FFAEnabled.Value }); } } internal static void PeriodicPushSettingsIfHost() { if (Sync.IsSettingsPushDue()) { PushSettingsIfHost(); } } internal static void PeriodicPushIfHost() { PeriodicPushSettingsIfHost(); if (Sync.IsLivePushDue()) { BroadcastLiveState(); } } internal static void OnLobbyEntered() { Sync.ResetForLobby(); if (MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); ResetMatchState(); } } internal static void OnPlayerEntered(CSteamID player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { MyceliumNetwork.RPCTarget(2718281828u, "SyncFFASettings", player, (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.SettingsRevision, Plugin.FFAEnabled.Value }); MyceliumNetwork.RPCTarget(2718281828u, "SyncFFALiveState", player, (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, SerializeKills(), WinnerId, GameModeManager.RoundId, Sync.LiveRevision }); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } internal static void ResetMatchState() { Sync.ResetLiveState(); WinnerId = -1; Kills.Clear(); } internal static void ApplyLiveState(CSteamID hostId, string killsData, int winnerId, int roundId, int revision) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (winnerId < -1 || !Sync.TryAcceptLiveSnapshot(hostId, roundId, revision)) { return; } WinnerId = winnerId; Kills.Clear(); foreach (KeyValuePair item in ScoreCodec.Parse(killsData, KillsToWin)) { Kills[item.Key] = item.Value; } } internal static void OnServerKill(int deadPlayerId, int killerId) { if (Enabled && WinnerId < 0 && killerId >= 0 && killerId != deadPlayerId) { Kills.TryGetValue(killerId, out var value); int num = value + 10; Kills[killerId] = num; GameModeHud.ShowScorePopupForPlayer(killerId, 10); if (num >= KillsToWin) { WinnerId = killerId; Announce(PlayerLookup.GetPlayerNameTag(killerId) + " reached " + KillsToWin + " points and won the round!"); GameModeManager.CompleteCustomRound(ScoreManager.Instance.GetTeamId(killerId)); } BroadcastLiveState(); } } internal static string SerializeKills() { return ScoreCodec.Serialize(Kills); } private static void BroadcastLiveState() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(2718281828u, "SyncFFALiveState", (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, SerializeKills(), WinnerId, GameModeManager.RoundId, Sync.NextLiveRevision() }); } } private static void Announce(string text) { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(2718281828u, "FFAAnnounce", (ReliableType)1, new object[1] { text }); } } } [HarmonyPatch(typeof(FirstPersonController), "HandleAddingForce")] internal static class FirstPersonController_SlideBoost_Patch { private static void Postfix(FirstPersonController __instance) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (!GlobalModifiersState.SlideBoostEnabled && __instance.isSliding) { Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(__instance.moveDirection.x, 0f, __instance.moveDirection.z); Vector3 val2 = val + __instance.forceAdded; float effectiveSprintSpeed = MovementTuning.GetEffectiveSprintSpeed(__instance); float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude > effectiveSprintSpeed && magnitude > 0.0001f) { __instance.forceAdded = val2 * (effectiveSprintSpeed / magnitude) - val; } } } } [HarmonyPatch(typeof(FirstPersonController), "Jump")] internal static class FirstPersonController_SlideJumpForce_Patch { private static void Prefix(FirstPersonController __instance) { //IL_001c: 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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (!GlobalModifiersState.SlideBoostEnabled && __instance.isSliding) { __instance.forceFactor = 0f; __instance.forceAdded = Vector3.zero; __instance.force = Vector3.zero; __instance.isCrouching = false; } } } [HarmonyPatch(typeof(FirstPersonController), "BForce")] internal static class FirstPersonController_WallJumpBoostTrack_Patch { private static void Prefix(FirstPersonController __instance) { MovementTuning.MarkWallJumpBForce(__instance, __instance.CanWallJump); } } [HarmonyPatch(typeof(FirstPersonController), "BForce")] internal static class FirstPersonController_SlideJumpBoostTrack_Patch { private static void Prefix(FirstPersonController __instance) { MovementTuning.MarkSlideBForce(__instance, __instance.IsSliding); } private static void Postfix(FirstPersonController __instance) { if (!GlobalModifiersState.SlideBoostEnabled) { MovementTuning.SuppressSlideBoost(__instance); } } } [HarmonyPatch(typeof(FirstPersonController), "HandleBForce")] internal static class FirstPersonController_WallJumpBoost_Patch { private static void Postfix(FirstPersonController __instance) { if (!GlobalModifiersState.WallJumpBoostEnabled) { MovementTuning.ClampWallJumpBoost(__instance, MovementTuning.GetEffectiveSprintSpeed(__instance)); } } } [HarmonyPatch(typeof(FirstPersonController), "Update")] [HarmonyPriority(800)] internal static class FirstPersonController_Speed_Patch { private static float _nextOwnerLogTime; private static void Prefix(FirstPersonController __instance) { float num = (__instance.isAiming ? GlobalModifiersState.AdsSpeedMultiplier : 1f); __instance.movementFactor = GlobalModifiersState.SpeedMultiplier * num; __instance.gravityMultiplier = GlobalModifiersState.GravityMultiplier; if (((NetworkBehaviour)__instance).IsOwner && Time.unscaledTime >= _nextOwnerLogTime) { _nextOwnerLogTime = Time.unscaledTime + 5f; Plugin.Logger.LogInfo((object)$"[GlobalModifiers] Enforcing on local player: movementFactor={__instance.movementFactor:0.00} gravityMultiplier={__instance.gravityMultiplier:0.00} (SpeedMultiplier={GlobalModifiersState.SpeedMultiplier:0.00}, GravityMultiplier={GlobalModifiersState.GravityMultiplier:0.00}, Enabled={GlobalModifiersState.Enabled}, TuningVersion={GlobalModifiersState.TuningVersion})"); } MovementTuning.ApplyTuningIfChanged(__instance, GlobalModifiersState.MomentumPercent, GlobalModifiersState.AirSpeedRatioPercent, GlobalModifiersState.TuningVersion); } } [HarmonyPatch] internal static class FirstPersonController_Tuning_Patch { private static MethodBase? TargetMethod() { return typeof(FirstPersonController).GetMethod("Awake___UserLogic", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(FirstPersonController).GetMethod("Awake", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } private static bool Prepare() { return TargetMethod() != null; } private static void Postfix(FirstPersonController __instance) { MovementTuning.ApplyTuningIfChanged(__instance, GlobalModifiersState.MomentumPercent, GlobalModifiersState.AirSpeedRatioPercent, GlobalModifiersState.TuningVersion); } } [HarmonyPatch(typeof(FirstPersonController), "HandleMovementInput")] internal static class FirstPersonController_Momentum_Patch { private static void Postfix(FirstPersonController __instance) { MovementTuning.BlendHorizontalVelocity(__instance, GlobalModifiersState.MomentumPercent); } } internal static class GlobalModifiersState { internal static bool Enabled; internal static bool WallJumpEnabled = true; internal static bool SlidingEnabled = true; internal static bool SlideBoostEnabled = true; internal static bool WallJumpBoostEnabled = true; internal static float SpeedMultiplier = 1f; internal static float AdsSpeedMultiplier = 0.8f; internal static float GravityMultiplier = 1f; internal static float MomentumPercent = 100f; internal static float AirSpeedRatioPercent = 116.666664f; internal static int TuningVersion; private static readonly ModeSyncState Sync = new ModeSyncState(); internal static void Apply(bool enabled, bool wallJump, bool sliding, bool slideBoost, bool wallJumpBoost, int moveSpeedPercent, int adsSpeedPercent, int gravityPercent, int momentumPercent, int airSpeedRatioPercent) { Enabled = enabled; if (!enabled) { WallJumpEnabled = true; SlidingEnabled = true; SlideBoostEnabled = true; WallJumpBoostEnabled = true; SpeedMultiplier = 1f; AdsSpeedMultiplier = 1f; GravityMultiplier = 1f; MomentumPercent = 100f; AirSpeedRatioPercent = 116.666664f; TuningVersion++; Plugin.Logger.LogInfo((object)"[GlobalModifiers] Apply: disabled - all values reset to stock"); return; } moveSpeedPercent = Mathf.Clamp(moveSpeedPercent, 50, 200); adsSpeedPercent = Mathf.Clamp(adsSpeedPercent, 50, 100); gravityPercent = Mathf.Clamp(gravityPercent, 10, 100); momentumPercent = Mathf.Clamp(momentumPercent, 10, 400); airSpeedRatioPercent = Mathf.Clamp(airSpeedRatioPercent, 50, 200); WallJumpEnabled = wallJump; SlidingEnabled = sliding; SlideBoostEnabled = slideBoost; WallJumpBoostEnabled = wallJumpBoost; SpeedMultiplier = (float)moveSpeedPercent / 100f; AdsSpeedMultiplier = (float)adsSpeedPercent / 100f; GravityMultiplier = (float)gravityPercent / 100f; MomentumPercent = momentumPercent; AirSpeedRatioPercent = airSpeedRatioPercent; TuningVersion++; Plugin.Logger.LogInfo((object)$"[MovementSettings] Apply: SpeedMultiplier={SpeedMultiplier:0.00} AdsSpeedMultiplier={AdsSpeedMultiplier:0.00} GravityMultiplier={GravityMultiplier:0.00} MomentumPercent={MomentumPercent} AirSpeedRatioPercent={AirSpeedRatioPercent} TuningVersion={TuningVersion}"); } private static void ApplyFromHostConfig() { Apply(Plugin.MovementTweaksEnabled.Value, Plugin.WallJumpEnabled.Value, Plugin.SlidingEnabled.Value, Plugin.SlideBoostEnabled.Value, Plugin.WallJumpBoostEnabled.Value, Plugin.MoveSpeedPercent.Value, Plugin.AdsSpeedPercent.Value, Plugin.GravityPercent.Value, Plugin.MomentumPercent.Value, Plugin.AirSpeedRatioPercent.Value); } internal static void PushIfHost() { if (!MyceliumNetwork.InLobby || !MyceliumNetwork.IsHost) { Plugin.Logger.LogInfo((object)$"[GlobalModifiers] PushIfHost skipped: InLobby={MyceliumNetwork.InLobby} IsHost={MyceliumNetwork.IsHost}"); return; } ApplyFromHostConfig(); Plugin.Logger.LogInfo((object)$"[MovementSettings] Host broadcasting movement settings to {MyceliumNetwork.PlayerCount} player(s)"); MyceliumNetwork.RPC(1892737465u, "SyncMovementSettings", (ReliableType)1, RpcArgs(Sync.NextSettingsRevision())); } internal static void PeriodicPushIfHost() { if (Sync.IsSettingsPushDue()) { PushIfHost(); } } internal static void OnLobbyEntered() { Plugin.Logger.LogInfo((object)$"[GlobalModifiers] Lobby session started, IsHost={MyceliumNetwork.IsHost}"); Sync.ResetForLobby(); if (MyceliumNetwork.IsHost) { ApplyFromHostConfig(); } } internal static void ResetForLobbyLeft() { Sync.ResetForLobby(); Apply(enabled: false, wallJump: true, sliding: true, slideBoost: true, wallJumpBoost: true, 100, 100, 100, 100, Mathf.RoundToInt(116.666664f)); } internal static void OnPlayerEntered(CSteamID player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { Plugin.Logger.LogInfo((object)$"[MovementSettings] Sending catch-up movement settings to newly joined player {player}"); MyceliumNetwork.RPCTarget(1892737465u, "SyncMovementSettings", player, (ReliableType)1, RpcArgs(Sync.SettingsRevision)); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } private static object[] RpcArgs(int revision) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) return new object[13] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, revision, Plugin.MovementTweaksEnabled.Value, Plugin.WallJumpEnabled.Value, Plugin.SlidingEnabled.Value, Plugin.SlideBoostEnabled.Value, Plugin.WallJumpBoostEnabled.Value, Plugin.MoveSpeedPercent.Value, Plugin.AdsSpeedPercent.Value, Plugin.GravityPercent.Value, Plugin.MomentumPercent.Value, Plugin.AirSpeedRatioPercent.Value }; } } internal static class MovementTuning { private sealed class VelocityMemory { public Vector3 Value; public bool Initialized; } private const float BaseAcceleration = 15f; internal const float StockAirSpeedRatioPercent = 116.666664f; private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static FieldInfo _globalAcceleration = null; private static FieldInfo _globalDeceleration = null; private static FieldInfo _walkAcceleration = null; private static FieldInfo _sprintAcceleration = null; private static FieldInfo _crouchAcceleration = null; private static FieldInfo _airAcceleration = null; private static FieldInfo _sprintAirAcceleration = null; private static FieldInfo _walkSpeed = null; private static FieldInfo _sprintSpeed = null; private static FieldInfo _airSpeed = null; private static FieldInfo _sprintAirSpeed = null; private static FieldInfo _bforcefinal = null; private static FieldInfo _bfactor = null; private static bool _cached; private static readonly ConditionalWeakTable> LastAppliedVersion = new ConditionalWeakTable>(); private static readonly ConditionalWeakTable VelocityByController = new ConditionalWeakTable(); private static readonly ConditionalWeakTable> WallJumpBForceActive = new ConditionalWeakTable>(); private static readonly ConditionalWeakTable> SlideBForceActive = new ConditionalWeakTable>(); private static void EnsureCached() { if (!_cached) { Type? typeFromHandle = typeof(FirstPersonController); _globalAcceleration = typeFromHandle.GetField("globalAcceleration", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _globalDeceleration = typeFromHandle.GetField("globalDeceleration", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _walkAcceleration = typeFromHandle.GetField("walkAcceleration", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _sprintAcceleration = typeFromHandle.GetField("sprintAcceleration", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _crouchAcceleration = typeFromHandle.GetField("crouchAcceleration", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _airAcceleration = typeFromHandle.GetField("airAcceleration", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _sprintAirAcceleration = typeFromHandle.GetField("sprintAirAcceleration", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _walkSpeed = typeFromHandle.GetField("walkSpeed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _sprintSpeed = typeFromHandle.GetField("sprintSpeed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _airSpeed = typeFromHandle.GetField("airSpeed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _sprintAirSpeed = typeFromHandle.GetField("sprintAirSpeed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _bforcefinal = typeFromHandle.GetField("bforcefinal", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _bfactor = typeFromHandle.GetField("bfactor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _cached = true; } } internal static void ApplyTuning(FirstPersonController controller, float momentumPercent, float airSpeedRatioPercent) { EnsureCached(); float num = ((momentumPercent <= 0f) ? 15f : (1500f / momentumPercent)); _globalAcceleration.SetValue(controller, num); _globalDeceleration.SetValue(controller, num); _walkAcceleration.SetValue(controller, num); _sprintAcceleration.SetValue(controller, num); _crouchAcceleration.SetValue(controller, num); _airAcceleration.SetValue(controller, num); _sprintAirAcceleration.SetValue(controller, num); float num2 = airSpeedRatioPercent / 100f; float num3 = (float)_walkSpeed.GetValue(controller); float num4 = (float)_sprintSpeed.GetValue(controller); _airSpeed.SetValue(controller, num3 * num2); _sprintAirSpeed.SetValue(controller, num4 * num2); } internal static void ApplyTuningIfChanged(FirstPersonController controller, float momentumPercent, float airSpeedRatioPercent, int version) { StrongBox orCreateValue = LastAppliedVersion.GetOrCreateValue(controller); if (orCreateValue.Value != version) { ApplyTuning(controller, momentumPercent, airSpeedRatioPercent); orCreateValue.Value = version; } } internal static float GetSprintSpeed(FirstPersonController controller) { EnsureCached(); return (float)_sprintSpeed.GetValue(controller); } internal static float GetEffectiveSprintSpeed(FirstPersonController controller) { return GetSprintSpeed(controller) * GlobalModifiersState.SpeedMultiplier; } internal static void BlendHorizontalVelocity(FirstPersonController controller, float momentumPercent) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) VelocityMemory orCreateValue = VelocityByController.GetOrCreateValue(controller); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(controller.moveDirection.x, 0f, controller.moveDirection.z); if (!orCreateValue.Initialized) { orCreateValue.Value = val; orCreateValue.Initialized = true; } else { float num = Mathf.Clamp01(((momentumPercent <= 0f) ? 15f : (1500f / momentumPercent)) * Time.deltaTime); orCreateValue.Value = Vector3.Lerp(orCreateValue.Value, val, num); controller.moveDirection = new Vector3(orCreateValue.Value.x, controller.moveDirection.y, orCreateValue.Value.z); } } internal static void MarkWallJumpBForce(FirstPersonController controller, bool isWallJump) { WallJumpBForceActive.GetOrCreateValue(controller).Value = isWallJump; } internal static void MarkSlideBForce(FirstPersonController controller, bool isSliding) { SlideBForceActive.GetOrCreateValue(controller).Value = isSliding; } internal static void SuppressSlideBoost(FirstPersonController controller) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (SlideBForceActive.TryGetValue(controller, out StrongBox value) && value.Value) { EnsureCached(); _bfactor.SetValue(controller, 0f); _bforcefinal.SetValue(controller, Vector3.zero); value.Value = false; } } internal static void ClampWallJumpBoost(FirstPersonController controller, float capSpeed) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) if (!WallJumpBForceActive.TryGetValue(controller, out StrongBox value) || !value.Value) { return; } EnsureCached(); if ((float)_bfactor.GetValue(controller) <= 0f) { value.Value = false; return; } Vector3 val = (Vector3)_bforcefinal.GetValue(controller); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(controller.moveDirection.x, 0f, controller.moveDirection.z); Vector3 val3 = val2 + val; float magnitude = ((Vector3)(ref val3)).magnitude; if (magnitude > capSpeed && magnitude > 0.0001f) { _bforcefinal.SetValue(controller, val3 * (capSpeed / magnitude) - val2); } } } [HarmonyPatch(typeof(PlayerManager), "SpawnPlayer", new Type[] { typeof(int), typeof(int), typeof(Vector3), typeof(Quaternion) })] internal static class PlayerManager_GlobalWeaponsSpawn_Patch { private static void Postfix(PlayerManager __instance) { if (GameModeManager.ShouldIgnoreGlobalWeaponSettings || !WeaponSettingsState.Enabled || !WeaponSettingsState.Cycle || (Object)(object)__instance.player == (Object)null) { return; } ClientInstance component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null && !JuggernautState.IsCurrentJuggernaut(__instance.player)) { string selectedWeapon = WeaponSettingsState.GetSelectedWeapon(component.PlayerId); if (selectedWeapon != null) { WeaponSettingsState.RequestLoadout(component.PlayerId, selectedWeapon); } } } } [HarmonyPatch(typeof(PlayerPickup), "RightHandFix")] internal static class PlayerPickup_WeaponHandPolicy_Patch { private static bool Prefix(PlayerPickup __instance) { if (!GameModeManager.ShouldIgnoreGlobalWeaponSettings && WeaponSettingsState.Enabled && WeaponSettingsState.Cycle) { WeaponService.AttachUnparentedWeapon(__instance); } return !WeaponService.IsOwnerAttachmentPending(__instance); } } internal static class WeaponSettingsState { internal static bool Enabled; internal static bool Cycle; internal static int SpareMagazines = 5; internal static List Allowed = new List(); private static readonly Dictionary SelectedWeapons = new Dictionary(); private static readonly Dictionary PendingLoadouts = new Dictionary(); private static float _nextLoadoutCheckTime; private static readonly ModeSyncState Sync = new ModeSyncState(); internal static void Apply(bool enabled, string allowedWeapons, int spareMagazines, bool cycleWeapons) { spareMagazines = Mathf.Clamp(spareMagazines, 2, 10); if (allowedWeapons == null) { allowedWeapons = string.Empty; } List list = WeaponService.ParseWeaponList(allowedWeapons); bool num = Enabled != enabled || Cycle != cycleWeapons || SpareMagazines != spareMagazines; bool flag = Allowed.Count != list.Count || !Allowed.SequenceEqual(list, StringComparer.Ordinal); Enabled = enabled; Cycle = cycleWeapons; SpareMagazines = spareMagazines; Allowed = list; if (num || flag) { WeaponService.ResetPendingRequests(); PendingLoadouts.Clear(); _nextLoadoutCheckTime = 0f; } if (flag) { SelectedWeapons.Clear(); } } private static void ApplyFromConfig() { Apply(Plugin.WeaponTweaksEnabled.Value, Plugin.AllowedWeapons.Value, Plugin.SpareMagazines.Value, Plugin.CycleWeapons.Value); } internal static void PushIfHost() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplyFromConfig(); MyceliumNetwork.RPC(1618033991u, "SyncWeaponSettings", (ReliableType)1, new object[7] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.NextSettingsRevision(), Plugin.WeaponTweaksEnabled.Value, Plugin.AllowedWeapons.Value, Plugin.SpareMagazines.Value, Plugin.CycleWeapons.Value }); } } internal static void PeriodicPushIfHost() { if (Sync.IsSettingsPushDue()) { PushIfHost(); } } internal static void OnLobbyEntered() { Sync.ResetForLobby(); SelectedWeapons.Clear(); PendingLoadouts.Clear(); if (MyceliumNetwork.IsHost) { ApplyFromConfig(); } } internal static void ResetForLobbyLeft() { SelectedWeapons.Clear(); PendingLoadouts.Clear(); Sync.ResetForLobby(); Apply(enabled: false, string.Empty, 5, cycleWeapons: false); } internal static void OnPlayerEntered(CSteamID player) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { MyceliumNetwork.RPCTarget(1618033991u, "SyncWeaponSettings", player, (ReliableType)1, new object[7] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.SettingsRevision, Plugin.WeaponTweaksEnabled.Value, Plugin.AllowedWeapons.Value, Plugin.SpareMagazines.Value, Plugin.CycleWeapons.Value }); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } internal static void UpdateLocalCycle() { if (!GameModeManager.ShouldIgnoreGlobalWeaponSettings && Enabled && Cycle && Input.GetKeyDown((KeyCode)289) && Allowed.Count != 0 && !((Object)(object)ClientInstance.Instance == (Object)null) && !JuggernautState.IsCurrentJuggernaut(ClientInstance.Instance.PlayerSpawner?.player)) { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { GiveCycledWeapon(ClientInstance.Instance.PlayerId); } else if (MyceliumNetwork.InLobby) { MyceliumNetwork.RPC(1618033991u, "RequestWeaponCycle", (ReliableType)1, new object[1] { ClientInstance.Instance.PlayerId }); } } } internal static void GiveCycledWeapon(int playerId) { if (!GameModeManager.IsActive(GameMode.Juggernaut) || playerId != JuggernautState.CurrentJuggernautPlayerId) { string selectedWeapon = GetSelectedWeapon(playerId); if (selectedWeapon != null) { int num = Allowed.IndexOf(selectedWeapon); string text = Allowed[(num + 1) % Allowed.Count]; SelectedWeapons[playerId] = text; RequestLoadout(playerId, text); } } } internal static void RequestLoadout(int playerId, string weaponName) { if (!WeaponService.IsFinalGameScreen) { PendingLoadouts[playerId] = Time.unscaledTime + 5f; WeaponService.GiveWeapon(playerId, weaponName, SpareMagazines); } } internal static void EnsureCycleLoadouts() { if (GameModeManager.ShouldIgnoreGlobalWeaponSettings || !Enabled || !Cycle || WeaponService.IsFinalGameScreen || !MyceliumNetwork.InLobby || !MyceliumNetwork.IsHost || Time.unscaledTime < _nextLoadoutCheckTime) { return; } _nextLoadoutCheckTime = Time.unscaledTime + 1f; foreach (ClientInstance value2 in ClientInstance.playerInstances.Values) { if ((Object)(object)value2 == (Object)null || !Object.op_Implicit((Object)(object)value2) || (Object)(object)value2.PlayerSpawner == (Object)null || !Object.op_Implicit((Object)(object)value2.PlayerSpawner)) { continue; } FirstPersonController player = value2.PlayerSpawner.player; if ((Object)(object)player == (Object)null || !Object.op_Implicit((Object)(object)player) || (Object)(object)player.playerPickupScript == (Object)null || !Object.op_Implicit((Object)(object)player.playerPickupScript)) { continue; } string selectedWeapon = GetSelectedWeapon(value2.PlayerId); if (selectedWeapon != null && (!GameModeManager.IsActive(GameMode.Juggernaut) || value2.PlayerId != JuggernautState.CurrentJuggernautPlayerId)) { GameObject objInHand = player.playerPickupScript.objInHand; Weapon val = (((Object)(object)objInHand == (Object)null || !Object.op_Implicit((Object)(object)objInHand)) ? null : objInHand.GetComponent()); float value; if ((Object)(object)val != (Object)null && ((Object)val).name.StartsWith(selectedWeapon, StringComparison.Ordinal)) { WeaponAmmoTuning.Initialize(val, SpareMagazines); PendingLoadouts.Remove(value2.PlayerId); } else if (!PendingLoadouts.TryGetValue(value2.PlayerId, out value) || Time.unscaledTime >= value) { RequestLoadout(value2.PlayerId, selectedWeapon); } } } } internal static string? GetSelectedWeapon(int playerId) { if (Allowed.Count == 0) { return null; } if (SelectedWeapons.TryGetValue(playerId, out string value) && Allowed.Contains(value)) { return value; } string text = Allowed[0]; SelectedWeapons[playerId] = text; return text; } } [HarmonyPatch(typeof(PlayerManager), "SpawnPlayer", new Type[] { typeof(int), typeof(int), typeof(Vector3), typeof(Quaternion) })] internal static class PlayerManager_GunGameSpawn_Patch { private static void Postfix(PlayerManager __instance) { if (GameModeManager.IsActive(GameMode.GunGame) && !WeaponService.IsFinalGameScreen && !((Object)(object)__instance.player == (Object)null)) { ClientInstance component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { GunGameState.GiveStartingWeapon(component.PlayerId); } } } } internal static class GunGameState { internal static bool Enabled; internal static readonly Dictionary Progress = new Dictionary(); private static float _nextLoadoutCheckTime; private static readonly Dictionary PendingLoadouts = new Dictionary(); private static readonly ModeSyncState Sync = new ModeSyncState(); internal static List WeaponOrder { get; private set; } = new List(); internal static int ScoreLimit => GameModeManager.EffectivePointsToWin; internal static void ApplySettings(bool enabled, string weaponOrder) { List list = WeaponService.ParseWeaponList(weaponOrder); bool num = Enabled != enabled || !WeaponOrder.SequenceEqual(list, StringComparer.Ordinal); Enabled = enabled; WeaponOrder = list; if (num) { ResetMatchState(); } } private static void ApplyFromConfig() { ApplySettings(Plugin.GunGameEnabled.Value, Plugin.GunGameWeaponOrder.Value); } internal static void PushSettingsIfHost() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplyFromConfig(); MyceliumNetwork.RPC(1618033990u, "SyncGunGameSettings", (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.NextSettingsRevision(), Plugin.GunGameEnabled.Value, Plugin.GunGameWeaponOrder.Value }); } } internal static void PeriodicPushSettingsIfHost() { if (Sync.IsSettingsPushDue()) { PushSettingsIfHost(); } } internal static void PeriodicPushIfHost() { PeriodicPushSettingsIfHost(); if (Sync.IsLivePushDue()) { BroadcastLiveState(); } } internal static void OnLobbyEntered() { Sync.ResetForLobby(); if (MyceliumNetwork.IsHost) { ApplyFromConfig(); ResetMatchState(); } } internal static void OnPlayerEntered(CSteamID player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { MyceliumNetwork.RPCTarget(1618033990u, "SyncGunGameSettings", player, (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.SettingsRevision, Plugin.GunGameEnabled.Value, Plugin.GunGameWeaponOrder.Value }); MyceliumNetwork.RPCTarget(1618033990u, "SyncGunGameLiveState", player, (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, SerializeProgress(), GameModeManager.RoundId, Sync.LiveRevision }); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } internal static void ResetMatchState() { Sync.ResetLiveState(); _nextLoadoutCheckTime = 0f; PendingLoadouts.Clear(); Progress.Clear(); } internal static void ApplyLiveState(CSteamID hostId, string data, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (!Sync.TryAcceptLiveSnapshot(hostId, roundId, revision)) { return; } Progress.Clear(); foreach (KeyValuePair item in ScoreCodec.Parse(data, ScoreLimit)) { Progress[item.Key] = item.Value; } } internal static void OnServerKill(int deadPlayerId, int killerId) { if (Enabled && killerId >= 0 && killerId != deadPlayerId) { Progress.TryGetValue(killerId, out var value); int num = value + 10; Progress[killerId] = num; GameModeHud.ShowScorePopupForPlayer(killerId, 10); if (ScoreLimit > 0 && num >= ScoreLimit) { GameModeManager.CompleteCustomRound(ScoreManager.Instance.GetTeamId(killerId)); } else if (WeaponOrder.Count > 0) { GiveWeaponForProgress(killerId, num); } BroadcastLiveState(); } } private static int GetWeaponIndex(int progress) { if (WeaponOrder.Count <= 1 || ScoreLimit <= 1) { return 0; } return (int)Math.Min((long)progress * (long)(WeaponOrder.Count - 1) / (ScoreLimit - 1), WeaponOrder.Count - 1); } internal static void EnsureLoadouts() { if (!Enabled || !GameModeManager.IsActive(GameMode.GunGame) || !MyceliumNetwork.InLobby || !MyceliumNetwork.IsHost || WeaponService.IsFinalGameScreen || Time.unscaledTime < _nextLoadoutCheckTime) { return; } _nextLoadoutCheckTime = Time.unscaledTime + 1f; foreach (ClientInstance value3 in ClientInstance.playerInstances.Values) { if ((Object)(object)value3 == (Object)null || !Object.op_Implicit((Object)(object)value3) || (Object)(object)value3.PlayerSpawner == (Object)null || !Object.op_Implicit((Object)(object)value3.PlayerSpawner) || (Object)(object)value3.PlayerSpawner.player == (Object)null || !Object.op_Implicit((Object)(object)value3.PlayerSpawner.player)) { continue; } int value; string weaponForProgress = GetWeaponForProgress(Progress.TryGetValue(value3.PlayerId, out value) ? value : 0); if (weaponForProgress != null) { GameObject val = value3.PlayerSpawner.player.playerPickupScript?.objInHand; Weapon val2 = (((Object)(object)val == (Object)null || !Object.op_Implicit((Object)(object)val)) ? null : val.GetComponent()); float value2; if ((Object)(object)val2 != (Object)null && ((Object)val2).name.StartsWith(weaponForProgress, StringComparison.Ordinal)) { PendingLoadouts.Remove(value3.PlayerId); } else if (!PendingLoadouts.TryGetValue(value3.PlayerId, out value2) || Time.unscaledTime >= value2) { GiveStartingWeapon(value3.PlayerId); } } } } internal static void GiveStartingWeapon(int playerId) { if (Enabled && GameModeManager.IsActive(GameMode.GunGame) && MyceliumNetwork.IsHost) { int value; int progress = (Progress.TryGetValue(playerId, out value) ? value : 0); GiveWeaponForProgress(playerId, progress); } } private static void GiveWeaponForProgress(int playerId, int progress) { string weaponForProgress = GetWeaponForProgress(progress); if (weaponForProgress != null) { PendingLoadouts[playerId] = Time.unscaledTime + 5f; WeaponService.GiveWeapon(playerId, weaponForProgress, null, unlimitedAmmo: true); } } private static string? GetWeaponForProgress(int progress) { if (WeaponOrder.Count != 0) { return WeaponOrder[GetWeaponIndex(progress)]; } return null; } internal static string SerializeProgress() { return ScoreCodec.Serialize(Progress); } private static void BroadcastLiveState() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(1618033990u, "SyncGunGameLiveState", (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, SerializeProgress(), GameModeManager.RoundId, Sync.NextLiveRevision() }); } } } [HarmonyPatch] internal static class PlayerHealth_PassiveHealthFeedbackWriter_Patch { private static MethodBase? TargetMethod() { return FishNetCompatibility.FindGeneratedMethod(typeof(PlayerHealth), "RpcWriter___Observers_HitFeedbackObservers_", (MethodInfo method) => method.ReturnType == typeof(void)); } private static bool Prepare() { return TargetMethod() != null; } private static bool Prefix() { return !HealthSettingsTuning.ApplyingPassiveHealth; } } [HarmonyPatch(typeof(PlayerHealth), "Update")] internal static class PlayerHealth_HealthSettings_Patch { private static void Postfix(PlayerHealth __instance) { HealthSettingsTuning.ObserveHealth(__instance); HealthSettingsTuning.ApplyIfChanged(__instance, HealthSettingsState.MaxHealthMultiplier, HealthSettingsState.TuningVersion); HealthSettingsTuning.RegenerateIfNeeded(__instance, HealthSettingsTuning.GetMemory(__instance)); } } internal static class HealthSettingsState { internal static bool Enabled; internal static float MaxHealthMultiplier = 1f; internal static bool RegenEnabled; internal static float RegenDelaySeconds = 5f; internal static float RegenRate = 10f; internal static int TuningVersion; private static float _nextServerScanTime; private static readonly ModeSyncState Sync = new ModeSyncState(); internal static void Apply(bool enabled, int maxHealthPercent, bool regenEnabled, int regenDelaySeconds, int regenRate) { Enabled = enabled; if (!enabled) { MaxHealthMultiplier = 1f; RegenEnabled = false; RegenDelaySeconds = 5f; RegenRate = 25f; TuningVersion++; Plugin.Logger.LogInfo((object)"[HealthSettings] Apply: disabled - all values reset to stock"); } else { MaxHealthMultiplier = (float)Mathf.Clamp(maxHealthPercent, 10, 400) / 100f; RegenEnabled = regenEnabled; RegenDelaySeconds = Mathf.Clamp((float)regenDelaySeconds, 2f, 15f); RegenRate = NormalizeRegenRate(regenRate); TuningVersion++; Plugin.Logger.LogInfo((object)$"[HealthSettings] Apply: maxHealthMultiplier={MaxHealthMultiplier:0.##} enabled={RegenEnabled} delay={RegenDelaySeconds:0.##} rate={RegenRate:0.##} version={TuningVersion}"); } } private static void ApplyFromHostConfig() { Apply(Plugin.HealthTweaksEnabled.Value, Plugin.MaxHealthPercent.Value, Plugin.HealthRegenEnabled.Value, Plugin.HealthRegenDelaySeconds.Value, Plugin.HealthRegenRate.Value); } internal static void PushIfHost() { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplyFromHostConfig(); MyceliumNetwork.RPC(1892737466u, "SyncHealthSettings", (ReliableType)1, RpcArgs(Sync.NextSettingsRevision())); } } internal static void PeriodicPushIfHost() { if (Sync.IsSettingsPushDue()) { PushIfHost(); } } internal static void ServerTick() { if (MyceliumNetwork.IsHost && SessionState.IsActive && Enabled && !(Time.unscaledTime < _nextServerScanTime)) { _nextServerScanTime = Time.unscaledTime + 0.1f; PlayerHealth[] array = Object.FindObjectsOfType(true); foreach (PlayerHealth controller in array) { HealthSettingsTuning.ApplyIfChanged(controller, MaxHealthMultiplier, TuningVersion); HealthSettingsTuning.RegenerateIfNeeded(controller, HealthSettingsTuning.GetMemory(controller)); } } } internal static void OnLobbyEntered() { Sync.ResetForLobby(); if (MyceliumNetwork.IsHost) { ApplyFromHostConfig(); } } internal static void ResetForLobbyLeft() { Sync.ResetForLobby(); Apply(enabled: false, 100, regenEnabled: false, 5, 25); } internal static void OnPlayerEntered(CSteamID player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { MyceliumNetwork.RPCTarget(1892737466u, "SyncHealthSettings", player, (ReliableType)1, RpcArgs(Sync.SettingsRevision)); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } private static object[] RpcArgs(int revision) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) return new object[8] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, revision, Plugin.HealthTweaksEnabled.Value, Plugin.MaxHealthPercent.Value, Plugin.HealthRegenEnabled.Value, Plugin.HealthRegenDelaySeconds.Value, Plugin.HealthRegenRate.Value }; } private static float NormalizeRegenRate(int rate) { int num; switch (rate) { case 25: case 50: case 75: case 100: case 150: case 200: num = rate; break; default: num = 25; break; } return num; } } internal static class HealthSettingsTuning { internal sealed class Memory { public float BaselineFullHealth = -1f; public int LastAppliedVersion = -1; public float LastObservedHealth = -1f; public float LastDamageTime; public float RegenAccumulator; public float LastRegenWriteTime; public float LastLoggedHealth = -1f; public bool LastModeSpecificHealth; } internal static bool ApplyingPassiveHealth; private static readonly ConditionalWeakTable MemoryByInstance = new ConditionalWeakTable(); internal static void ApplyIfChanged(PlayerHealth controller, float healthMultiplier, int version) { Memory orCreateValue = MemoryByInstance.GetOrCreateValue(controller); if (orCreateValue.BaselineFullHealth < 0f) { orCreateValue.BaselineFullHealth = controller.fullHealth; } if (GameModeManager.IsActive(GameMode.Infidel)) { InfidelState.ApplyHealth(controller); orCreateValue.LastModeSpecificHealth = true; orCreateValue.LastAppliedVersion = version; return; } if (GameModeManager.IsActive(GameMode.OneInTheChamber)) { OneInTheChamberState.ApplyHealth(controller); orCreateValue.LastModeSpecificHealth = true; orCreateValue.LastAppliedVersion = version; return; } if (GameModeManager.IsActive(GameMode.Juggernaut) && JuggernautState.IsCurrentJuggernaut(controller)) { JuggernautState.ApplyHealth(controller); orCreateValue.LastModeSpecificHealth = true; orCreateValue.LastAppliedVersion = version; return; } if (GameModeManager.IsActive(GameMode.KillTheRat) && KillTheRatState.IsRat(controller)) { KillTheRatState.ApplyHealth(controller, orCreateValue.BaselineFullHealth); orCreateValue.LastModeSpecificHealth = true; orCreateValue.LastAppliedVersion = version; return; } if (GameModeManager.IsActive(GameMode.SniperBattle)) { SniperBattleState.ApplyHealth(controller); orCreateValue.LastModeSpecificHealth = true; orCreateValue.LastAppliedVersion = version; return; } if (GameModeManager.IsActive(GameMode.Default)) { controller.fullHealth = orCreateValue.BaselineFullHealth; orCreateValue.LastModeSpecificHealth = true; orCreateValue.LastAppliedVersion = version; return; } if (orCreateValue.LastModeSpecificHealth) { orCreateValue.LastModeSpecificHealth = false; orCreateValue.LastAppliedVersion = -1; } if (orCreateValue.LastAppliedVersion == version) { return; } orCreateValue.LastAppliedVersion = version; float num = orCreateValue.BaselineFullHealth * healthMultiplier; float fullHealth = controller.fullHealth; controller.fullHealth = num; Plugin.Logger.LogInfo((object)$"[HealthSettings] Health apply: owner={((NetworkBehaviour)controller).IsOwner} server={((NetworkBehaviour)controller).IsServer} baseline={orCreateValue.BaselineFullHealth:0.##} fullHealth={fullHealth:0.##}->{num:0.##} healthBefore={controller.health:0.##} multiplier={healthMultiplier:0.##} version={version}"); if (!((NetworkBehaviour)controller).IsServer) { return; } float num2 = num - fullHealth; if (!Mathf.Approximately(num2, 0f)) { ApplyingPassiveHealth = true; try { FishNetCompatibility.TryRemoveHealth(controller, 0f - num2); } finally { ApplyingPassiveHealth = false; } Plugin.Logger.LogInfo((object)$"[HealthSettings] Health server write: owner={((NetworkBehaviour)controller).IsOwner} healthAfter={controller.health:0.##} bonus={num2:0.##}"); } } internal static void RegenerateIfNeeded(PlayerHealth controller, Memory memory) { bool num = GameModeManager.IsActive(GameMode.Juggernaut) && JuggernautState.IsCurrentJuggernaut(controller); bool flag = GameModeManager.IsActive(GameMode.KillTheRat) && KillTheRatState.IsRat(controller); if (num || flag || GameModeManager.ShouldIgnoreGlobalHealthSettings || !((NetworkBehaviour)controller).IsServer || !((Component)controller).gameObject.activeInHierarchy || controller.health <= 0f) { return; } float num2 = controller.sync___get_value_health(); if (memory.LastObservedHealth < 0f) { memory.LastObservedHealth = num2; memory.LastDamageTime = Time.unscaledTime; return; } if (num2 < memory.LastObservedHealth - 0.001f) { memory.LastDamageTime = Time.unscaledTime; memory.RegenAccumulator = 0f; } memory.LastObservedHealth = num2; if (!HealthSettingsState.RegenEnabled || Time.unscaledTime - memory.LastDamageTime < HealthSettingsState.RegenDelaySeconds || num2 >= controller.fullHealth) { if (!HealthSettingsState.RegenEnabled) { memory.RegenAccumulator = 0f; } return; } float num3 = HealthSettingsState.RegenRate / 25f; memory.RegenAccumulator += Time.unscaledDeltaTime * num3; if (!(Time.unscaledTime - memory.LastRegenWriteTime < 0.1f) && !(memory.RegenAccumulator <= 0f)) { float num4 = Mathf.Min(memory.RegenAccumulator, controller.fullHealth - num2); memory.RegenAccumulator -= num4; memory.LastRegenWriteTime = Time.unscaledTime; ApplyingPassiveHealth = true; try { FishNetCompatibility.TryRemoveHealth(controller, 0f - num4); } finally { ApplyingPassiveHealth = false; } memory.LastObservedHealth = controller.sync___get_value_health(); } } internal static void ObserveHealth(PlayerHealth controller) { Memory orCreateValue = MemoryByInstance.GetOrCreateValue(controller); float num = controller.sync___get_value_health(); if (!Mathf.Approximately(orCreateValue.LastLoggedHealth, num)) { Plugin.Logger.LogInfo((object)$"[HealthSettings] Health observed: owner={((NetworkBehaviour)controller).IsOwner} server={((NetworkBehaviour)controller).IsServer} health={num:0.###} fullHealth={controller.fullHealth:0.###}"); orCreateValue.LastLoggedHealth = num; } } internal static Memory GetMemory(PlayerHealth controller) { return MemoryByInstance.GetOrCreateValue(controller); } } [HarmonyPatch(typeof(MeleeWeapon), "HitServer")] internal static class MeleeWeapon_HotPotatoBatDamage_Patch { private static void Prefix(MeleeWeapon __instance, PlayerHealth enemyHealth, ref float damageToGive) { if (GameModeManager.IsActive(GameMode.HotPotato) && !((Object)(object)__instance == (Object)null) && ((Object)__instance).name.StartsWith("BaseballBat", StringComparison.Ordinal) && !((Object)(object)enemyHealth == (Object)null)) { damageToGive = Mathf.Max(0.01f, enemyHealth.fullHealth * 0.5f); } } } [HarmonyPatch(typeof(PlayerManager), "SpawnPlayer", new Type[] { typeof(int), typeof(int), typeof(Vector3), typeof(Quaternion) })] internal static class PlayerManager_HotPotatoSpawn_Patch { private static void Postfix(PlayerManager __instance) { if (GameModeManager.IsActive(GameMode.HotPotato) && !WeaponService.IsFinalGameScreen && !((Object)(object)__instance.player == (Object)null)) { ClientInstance component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { HotPotatoState.RequestLoadout(component.PlayerId); } } } } internal static class HotPotatoState { internal const string BatWeaponName = "BaseballBat"; internal const string ShotgunWeaponName = "Shotgun"; internal static bool Enabled; internal static readonly Dictionary Kills = new Dictionary(); private static float _nextLoadoutCheckTime; private static readonly ModeSyncState Sync = new ModeSyncState(); private static readonly Dictionary PendingLoadouts = new Dictionary(); internal static int PotatoPlayerId { get; private set; } = -1; internal static int WinnerId { get; private set; } = -1; internal static int KillsToWin => GameModeManager.EffectivePointsToWin; internal static void ApplySettings(bool enabled) { bool num = Enabled != enabled; Enabled = enabled; if (num) { ResetMatchState(); } } private static void ApplySettingsFromHostConfig() { ApplySettings(Plugin.HotPotatoEnabled.Value); } internal static void PushSettingsIfHost() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); MyceliumNetwork.RPC(2718281830u, "SyncHotPotatoSettings", (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.NextSettingsRevision(), Plugin.HotPotatoEnabled.Value }); } } internal static void PeriodicPushSettingsIfHost() { if (Sync.IsSettingsPushDue()) { PushSettingsIfHost(); } } internal static void PeriodicPushIfHost() { if (Sync.IsLivePushDue()) { BroadcastLiveState(); } } internal static void OnLobbyEntered() { Sync.ResetForLobby(); if (MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); ResetMatchState(); } } internal static void OnPlayerEntered(CSteamID player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { MyceliumNetwork.RPCTarget(2718281830u, "SyncHotPotatoSettings", player, (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.SettingsRevision, Plugin.HotPotatoEnabled.Value }); MyceliumNetwork.RPCTarget(2718281830u, "SyncHotPotatoLiveState", player, (ReliableType)1, new object[6] { MyceliumNetwork.LobbyHost, SerializeKills(), PotatoPlayerId, WinnerId, GameModeManager.RoundId, Sync.LiveRevision }); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } internal static void ResetMatchState() { Sync.ResetLiveState(); _nextLoadoutCheckTime = 0f; PotatoPlayerId = -1; WinnerId = -1; Kills.Clear(); PendingLoadouts.Clear(); } internal static void ApplyLiveState(CSteamID hostId, string killsData, int potatoPlayerId, int winnerId, int roundId, int revision) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (potatoPlayerId < -1 || winnerId < -1 || !Sync.TryAcceptLiveSnapshot(hostId, roundId, revision)) { return; } PotatoPlayerId = potatoPlayerId; WinnerId = winnerId; Kills.Clear(); foreach (KeyValuePair item in ScoreCodec.Parse(killsData, KillsToWin)) { Kills[item.Key] = item.Value; } } internal static void OnRoundStarted() { if (!Enabled || !GameModeManager.IsActive(GameMode.HotPotato) || !MyceliumNetwork.IsHost) { return; } ResetMatchState(); List list = new List(); foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && Object.op_Implicit((Object)(object)value)) { list.Add(value.PlayerId); Kills[value.PlayerId] = 0; } } if (list.Count != 0) { PotatoPlayerId = list[Random.Range(0, list.Count)]; Announce(PlayerLookup.GetPlayerNameTag(PotatoPlayerId) + " has the HOT POTATO!"); EnsureLoadouts(); BroadcastLiveState(); } } internal static void OnServerKill(int deadPlayerId, int killerId) { if (Enabled && GameModeManager.IsActive(GameMode.HotPotato) && WinnerId < 0 && killerId >= 0 && killerId != deadPlayerId) { Kills.TryGetValue(killerId, out var value); int num = value + 10; Kills[killerId] = num; GameModeHud.ShowScorePopupForPlayer(killerId, 10); if (killerId == PotatoPlayerId) { PotatoPlayerId = deadPlayerId; PendingLoadouts.Remove(killerId); WeaponService.GiveWeapon(killerId, "Shotgun"); Announce(PlayerLookup.GetPlayerNameTag(deadPlayerId) + " got the HOT POTATO!"); } if (num >= KillsToWin) { WinnerId = killerId; Announce(PlayerLookup.GetPlayerNameTag(killerId) + " reached " + KillsToWin + " points and won the round!"); GameModeManager.CompleteCustomRound(ScoreManager.Instance.GetTeamId(killerId)); } BroadcastLiveState(); } } internal static void RequestLoadout(int playerId) { if (Enabled && GameModeManager.IsActive(GameMode.HotPotato) && MyceliumNetwork.IsHost) { PendingLoadouts[playerId] = Time.unscaledTime + 2f; GiveExpectedWeapon(playerId); } } internal static bool IsAllowedWeapon(Weapon weapon, int playerId) { if ((Object)(object)weapon == (Object)null) { return true; } string value = ((playerId == PotatoPlayerId) ? "BaseballBat" : "Shotgun"); return ((Object)weapon).name.StartsWith(value, StringComparison.Ordinal); } internal static string GetExpectedWeapon(int playerId) { if (playerId != PotatoPlayerId) { return "Shotgun"; } return "BaseballBat"; } internal static void EnsureLoadouts() { if (!Enabled || !GameModeManager.IsActive(GameMode.HotPotato) || !MyceliumNetwork.InLobby || !MyceliumNetwork.IsHost || WeaponService.IsFinalGameScreen || Time.unscaledTime < _nextLoadoutCheckTime) { return; } _nextLoadoutCheckTime = Time.unscaledTime + 0.5f; foreach (ClientInstance value2 in ClientInstance.playerInstances.Values) { if (!((Object)(object)value2 == (Object)null) && Object.op_Implicit((Object)(object)value2) && !((Object)(object)value2.PlayerSpawner == (Object)null) && Object.op_Implicit((Object)(object)value2.PlayerSpawner) && !((Object)(object)value2.PlayerSpawner.player == (Object)null) && Object.op_Implicit((Object)(object)value2.PlayerSpawner.player) && ((Component)value2.PlayerSpawner.player).gameObject.activeInHierarchy) { Weapon weapon = GetWeapon(value2.PlayerSpawner.player.playerPickupScript?.objInHand); float value; if ((Object)(object)weapon != (Object)null && IsAllowedWeapon(weapon, value2.PlayerId)) { PendingLoadouts.Remove(value2.PlayerId); } else if (!PendingLoadouts.TryGetValue(value2.PlayerId, out value) || Time.unscaledTime >= value) { GiveExpectedWeapon(value2.PlayerId); } } } } private static void GiveExpectedWeapon(int playerId) { PendingLoadouts[playerId] = Time.unscaledTime + 2f; WeaponService.GiveWeapon(playerId, GetExpectedWeapon(playerId)); } private static Weapon? GetWeapon(GameObject? heldObject) { if (!((Object)(object)heldObject == (Object)null) && Object.op_Implicit((Object)(object)heldObject)) { return heldObject.GetComponent(); } return null; } private static string SerializeKills() { return ScoreCodec.Serialize(Kills); } private static void BroadcastLiveState() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(2718281830u, "SyncHotPotatoLiveState", (ReliableType)1, new object[6] { MyceliumNetwork.LobbyHost, SerializeKills(), PotatoPlayerId, WinnerId, GameModeManager.RoundId, Sync.NextLiveRevision() }); } } private static void Announce(string text) { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(2718281830u, "HotPotatoAnnounce", (ReliableType)1, new object[1] { text }); } } } [HarmonyPatch(typeof(PlayerManager), "SpawnPlayer", new Type[] { typeof(int), typeof(int), typeof(Vector3), typeof(Quaternion) })] internal static class PlayerManager_InfidelSpawn_Patch { private static void Postfix(PlayerManager __instance) { if (GameModeManager.IsActive(GameMode.Infidel) && !WeaponService.IsFinalGameScreen && !((Object)(object)__instance.player == (Object)null)) { ClientInstance component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { InfidelState.RequestHealthReset(component.PlayerId); InfidelState.RequestLoadout(component.PlayerId); } } } } internal static class InfidelState { internal const string WeaponName = "AK-K"; internal const int SpareMagazines = 2; internal const float InfidelHealth = 8f; internal const float TerroristHealth = 4f; internal const float MovementMultiplier = 0.7f; internal const float WeaponDelaySeconds = 10f; internal static bool Enabled; internal static readonly Dictionary Scores = new Dictionary(); private static readonly HashSet AlivePlayers = new HashSet(); private static readonly HashSet PendingHealthResets = new HashSet(); private static readonly Dictionary PendingLoadouts = new Dictionary(); private static float _nextLoadoutCheckTime; private static readonly ModeSyncState Sync = new ModeSyncState(); private static int _subRoundId; private static int _localRoleSubRoundId = -1; private static bool _subRoundEnding; internal static int InfidelPlayerId { get; private set; } = -1; internal static int WinnerId { get; private set; } = -1; internal static int KillsToWin => GameModeManager.EffectivePointsToWin; internal static bool WeaponsUnlocked { get; private set; } internal static bool LocalIsInfidel { get; private set; } internal static void ApplySettings(bool enabled) { bool num = Enabled != enabled; Enabled = enabled; if (num) { ResetMatchState(); } } private static void ApplySettingsFromHostConfig() { ApplySettings(Plugin.InfidelEnabled.Value); } internal static void PushSettingsIfHost() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); MyceliumNetwork.RPC(2718281831u, "SyncInfidelSettings", (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.NextSettingsRevision(), Plugin.InfidelEnabled.Value }); } } internal static void PeriodicPushSettingsIfHost() { if (Sync.IsSettingsPushDue()) { PushSettingsIfHost(); } } internal static void PeriodicPushIfHost() { if (Sync.IsLivePushDue()) { BroadcastLiveState(); SendRoleStates(announce: false); } } internal static void OnLobbyEntered() { Sync.ResetForLobby(); if (MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); ResetMatchState(); } } internal static void OnPlayerEntered(CSteamID player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00de: 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_0119: Unknown result type (might be due to invalid IL or missing references) if (!MyceliumNetwork.IsHost) { return; } MyceliumNetwork.RPCTarget(2718281831u, "SyncInfidelSettings", player, (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.SettingsRevision, Plugin.InfidelEnabled.Value }); MyceliumNetwork.RPCTarget(2718281831u, "SyncInfidelLiveState", player, (ReliableType)1, new object[7] { MyceliumNetwork.LobbyHost, SerializeScores(), WinnerId, _subRoundId, WeaponsUnlocked, GameModeManager.RoundId, Sync.LiveRevision }); if (InfidelPlayerId >= 0 && SubRoundIsActive()) { int num = ((player.m_SteamID == 0L) ? (-1) : FindPlayerId(player)); if (num >= 0) { AlivePlayers.Add(num); PendingHealthResets.Add(num); Scores.TryAdd(num, 0); } SendRoleToPlayer(player, num, announce: false); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } internal static void ResetMatchState() { StopSubRoundTransition(); Sync.ResetLiveState(); _nextLoadoutCheckTime = 0f; _subRoundId = 0; _localRoleSubRoundId = -1; _subRoundEnding = false; InfidelPlayerId = -1; WinnerId = -1; WeaponsUnlocked = false; LocalIsInfidel = false; AlivePlayers.Clear(); PendingHealthResets.Clear(); PendingLoadouts.Clear(); Scores.Clear(); } internal static void ApplyLiveState(CSteamID hostId, string scoresData, int winnerId, int subRoundId, bool weaponsUnlocked, int roundId, int revision) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) int lastLiveRoundId = Sync.LastLiveRoundId; if (winnerId < -1 || !Sync.TryAcceptLiveSnapshot(hostId, roundId, revision)) { return; } WinnerId = winnerId; _subRoundId = ((roundId != lastLiveRoundId) ? subRoundId : Math.Max(_subRoundId, subRoundId)); WeaponsUnlocked = weaponsUnlocked; Scores.Clear(); foreach (KeyValuePair item in ScoreCodec.Parse(scoresData, KillsToWin)) { Scores[item.Key] = item.Value; } } internal static void OnRoundStarted() { if (Enabled && GameModeManager.IsActive(GameMode.Infidel) && MyceliumNetwork.IsHost) { Scores.Clear(); WinnerId = -1; StartSubRound(); } } internal static void OnServerKill(int deadPlayerId, int killerId) { if (!Enabled || !GameModeManager.IsActive(GameMode.Infidel) || WinnerId >= 0 || _subRoundEnding) { return; } bool flag = deadPlayerId == InfidelPlayerId; AlivePlayers.Remove(deadPlayerId); if (killerId >= 0 && killerId != deadPlayerId) { bool killerIsInfidel = killerId == InfidelPlayerId; int killerAward = InfidelRules.GetKillerAward(flag, killerIsInfidel, flag ? AlivePlayers.Count : 0); if (killerAward > 0) { AwardScore(killerId, killerAward); } int infidelBonusAward = InfidelRules.GetInfidelBonusAward(flag, killerIsInfidel); if (infidelBonusAward > 0) { AwardScore(InfidelPlayerId, infidelBonusAward); } } BroadcastLiveState(); if (WinnerId >= 0) { Announce(PlayerLookup.GetPlayerNameTag(WinnerId) + " reached " + KillsToWin + " points and won the round!"); GameModeManager.CompleteCustomRound(ScoreManager.Instance.GetTeamId(WinnerId)); } else if (flag || (!flag && AlivePlayers.Count == 1)) { BeginNextSubRound(); } } internal static void RequestLoadout(int playerId) { if (Enabled && GameModeManager.IsActive(GameMode.Infidel) && MyceliumNetwork.IsHost) { if (!WeaponsUnlocked) { PendingLoadouts.Remove(playerId); ClearPlayerWeapons(playerId); } else { GiveWeapon(playerId); } } } internal static void RequestHealthReset(int playerId) { if (playerId >= 0) { PendingHealthResets.Add(playerId); } } internal static void ApplyHealth(PlayerHealth health) { int num = health.playerValues?.playerClient?.PlayerId ?? (-1); float num2 = (health.fullHealth = (IsInfidel(health) ? 8f : 4f)); if (!((NetworkBehaviour)health).IsServer) { return; } float num3 = num2 - health.sync___get_value_health(); if ((num < 0 || !PendingHealthResets.Remove(num)) && num3 >= 0f) { return; } HealthSettingsTuning.ApplyingPassiveHealth = true; try { FishNetCompatibility.TryRemoveHealth(health, 0f - num3); } finally { HealthSettingsTuning.ApplyingPassiveHealth = false; } } internal static bool IsInfidel(PlayerHealth health) { int num = health.playerValues?.playerClient?.PlayerId ?? (-1); if (MyceliumNetwork.IsHost) { if (num >= 0) { return num == InfidelPlayerId; } return false; } if ((Object)(object)ClientInstance.Instance != (Object)null && ClientInstance.Instance.PlayerId == num) { return LocalIsInfidel; } return false; } internal static bool IsAllowedWeapon(Weapon weapon, int playerId) { if ((Object)(object)weapon != (Object)null && WeaponsUnlocked) { return ((Object)weapon).name.StartsWith("AK-K", StringComparison.Ordinal); } return false; } internal static void ApplyLocalRole(CSteamID hostId, int subRoundId, bool isInfidel, bool announce) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!(hostId != MyceliumNetwork.LobbyHost) && subRoundId >= _localRoleSubRoundId) { _localRoleSubRoundId = subRoundId; LocalIsInfidel = isInfidel; if (announce) { GameModeHud.AnnounceTarget(isInfidel ? "You are the INFIDEL." : "You are a TERRORIST.", 10f); } } } private static bool SubRoundIsActive() { if (_subRoundId > 0) { return !_subRoundEnding; } return false; } private static void StartSubRound() { if (WinnerId >= 0 || !MyceliumNetwork.IsHost) { return; } _subRoundId++; _subRoundEnding = false; WeaponsUnlocked = false; _nextLoadoutCheckTime = 0f; PendingLoadouts.Clear(); AlivePlayers.Clear(); List list = new List(); foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && Object.op_Implicit((Object)(object)value)) { list.Add(value.PlayerId); AlivePlayers.Add(value.PlayerId); PendingHealthResets.Add(value.PlayerId); Scores.TryAdd(value.PlayerId, 0); } } if (list.Count == 0) { InfidelPlayerId = -1; return; } InfidelPlayerId = list[Random.Range(0, list.Count)]; ClearCurrentWeapons(); SendRoleStates(announce: true); BroadcastLiveState(); if ((Object)(object)Plugin.Instance != (Object)null) { int subRoundId = _subRoundId; ((MonoBehaviour)Plugin.Instance).StartCoroutine(UnlockWeaponsAfterDelay(subRoundId, SessionState.Generation, GameModeManager.RoundId)); } } private static IEnumerator UnlockWeaponsAfterDelay(int token, int sessionGeneration, int roundId) { yield return (object)new WaitForSeconds(10f); if (SessionState.IsCurrent(sessionGeneration) && GameModeManager.RoundId == roundId && token == _subRoundId && !_subRoundEnding && WinnerId < 0 && GameModeManager.IsActive(GameMode.Infidel)) { WeaponsUnlocked = true; EnsureLoadouts(); BroadcastLiveState(); } } private static void BeginNextSubRound() { if (_subRoundEnding || (Object)(object)Plugin.Instance == (Object)null) { return; } _subRoundEnding = true; WeaponsUnlocked = false; ClearCurrentWeapons(); foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && Object.op_Implicit((Object)(object)value)) { GameModeRespawn.Schedule(value.PlayerId, GameModeManager.EffectiveRespawnDelaySeconds); } } ((MonoBehaviour)Plugin.Instance).StartCoroutine(StartNextSubRoundAfterRespawn(GameModeManager.EffectiveRespawnDelaySeconds + 0.75f, SessionState.Generation, GameModeManager.RoundId)); } private static IEnumerator StartNextSubRoundAfterRespawn(float delay, int sessionGeneration, int roundId) { yield return (object)new WaitForSeconds(delay); if (SessionState.IsCurrent(sessionGeneration) && GameModeManager.RoundId == roundId && WinnerId < 0 && GameModeManager.IsActive(GameMode.Infidel)) { StartSubRound(); } } private static void StopSubRoundTransition() { _subRoundEnding = false; } private static void ClearCurrentWeapons() { if (!MyceliumNetwork.IsHost) { return; } foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && Object.op_Implicit((Object)(object)value)) { ClearPlayerWeapons(value.PlayerId); } } } private static void ClearPlayerWeapons(int playerId) { if (ClientInstance.playerInstances.TryGetValue(playerId, out var value) && !((Object)(object)value == (Object)null) && Object.op_Implicit((Object)(object)value) && !((Object)(object)value.PlayerSpawner == (Object)null) && Object.op_Implicit((Object)(object)value.PlayerSpawner) && !((Object)(object)value.PlayerSpawner.player == (Object)null) && Object.op_Implicit((Object)(object)value.PlayerSpawner.player)) { PlayerPickup playerPickupScript = value.PlayerSpawner.player.playerPickupScript; if ((Object)(object)playerPickupScript != (Object)null) { WeaponService.ClearHeldWeapons(playerPickupScript); } } } internal static void EnsureLoadouts() { if (!Enabled || !GameModeManager.IsActive(GameMode.Infidel) || !MyceliumNetwork.InLobby || !MyceliumNetwork.IsHost || WeaponService.IsFinalGameScreen || Time.unscaledTime < _nextLoadoutCheckTime) { return; } _nextLoadoutCheckTime = Time.unscaledTime + 0.5f; foreach (ClientInstance value2 in ClientInstance.playerInstances.Values) { if ((Object)(object)value2 == (Object)null || !Object.op_Implicit((Object)(object)value2) || (Object)(object)value2.PlayerSpawner == (Object)null || !Object.op_Implicit((Object)(object)value2.PlayerSpawner) || (Object)(object)value2.PlayerSpawner.player == (Object)null || !Object.op_Implicit((Object)(object)value2.PlayerSpawner.player) || !((Component)value2.PlayerSpawner.player).gameObject.activeInHierarchy) { continue; } PlayerPickup playerPickupScript = value2.PlayerSpawner.player.playerPickupScript; if (!WeaponsUnlocked) { if ((Object)(object)playerPickupScript != (Object)null && (((Object)(object)playerPickupScript.objInHand != (Object)null && Object.op_Implicit((Object)(object)playerPickupScript.objInHand)) || ((Object)(object)playerPickupScript.objInLeftHand != (Object)null && Object.op_Implicit((Object)(object)playerPickupScript.objInLeftHand)))) { WeaponService.ClearHeldWeapons(playerPickupScript); } continue; } Weapon weapon = GetWeapon(playerPickupScript?.objInHand); float value; if ((Object)(object)weapon != (Object)null && ((Object)weapon).name.StartsWith("AK-K", StringComparison.Ordinal)) { PendingLoadouts.Remove(value2.PlayerId); } else if (!PendingLoadouts.TryGetValue(value2.PlayerId, out value) || Time.unscaledTime >= value) { GiveWeapon(value2.PlayerId); } } } private static void GiveWeapon(int playerId) { PendingLoadouts[playerId] = Time.unscaledTime + 3f; WeaponService.GiveWeapon(playerId, "AK-K", 2); } private static void AwardScore(int playerId, int amount) { Scores.TryGetValue(playerId, out var value); int num = value + amount; Scores[playerId] = num; if (amount > 0) { GameModeHud.ShowScorePopupForPlayer(playerId, amount); } if (WinnerId < 0 && num >= KillsToWin) { WinnerId = playerId; } } private static void SendRoleStates(bool announce) { //IL_0070: 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) foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && Object.op_Implicit((Object)(object)value) && value.PlayerSteamID != 0L) { SendRoleToPlayer(new CSteamID(value.PlayerSteamID), value.PlayerId, announce); } } if ((Object)(object)ClientInstance.Instance != (Object)null) { ApplyLocalRole(MyceliumNetwork.LobbyHost, _subRoundId, ClientInstance.Instance.PlayerId == InfidelPlayerId, announce); } } private static void SendRoleToPlayer(CSteamID target, int playerId, bool announce) { //IL_0004: 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: Unknown result type (might be due to invalid IL or missing references) if (playerId >= 0 && target.m_SteamID != 0L) { MyceliumNetwork.RPCTarget(2718281831u, "SyncInfidelRole", target, (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, _subRoundId, playerId == InfidelPlayerId, announce }); } } private static int FindPlayerId(CSteamID steamId) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && Object.op_Implicit((Object)(object)value) && value.PlayerSteamID == steamId.m_SteamID) { return value.PlayerId; } } return -1; } private static Weapon? GetWeapon(GameObject? heldObject) { if (!((Object)(object)heldObject == (Object)null) && Object.op_Implicit((Object)(object)heldObject)) { return heldObject.GetComponent(); } return null; } private static string SerializeScores() { return ScoreCodec.Serialize(Scores); } private static void BroadcastLiveState() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(2718281831u, "SyncInfidelLiveState", (ReliableType)1, new object[7] { MyceliumNetwork.LobbyHost, SerializeScores(), WinnerId, _subRoundId, WeaponsUnlocked, GameModeManager.RoundId, Sync.NextLiveRevision() }); } } private static void Announce(string text) { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(2718281831u, "InfidelAnnounce", (ReliableType)1, new object[1] { text }); } } } internal static class JuggernautOutline { private static PlayerHealth? _outlinedPlayer; internal static void ResetState() { _outlinedPlayer = null; } internal static void EnforceOutline() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (PlayerOutline.UpdateMode(GameModeManager.ActiveMode)) { _outlinedPlayer = null; } if (!GameModeManager.ShouldClearPlayerOutlines) { if (!GameModeManager.IsActive(GameMode.Juggernaut)) { PlayerOutline.ClearTarget(ref _outlinedPlayer); return; } PlayerHealth target = PlayerLookup.FindPlayerHealthById(JuggernautState.CurrentJuggernautPlayerId); PlayerOutline.ApplySingleTarget(ref _outlinedPlayer, target, new Color(1f, 0.42f, 0f)); } } } [HarmonyPatch(typeof(GameManager), "Update")] internal static class GameManager_JuggernautTick_Patch { private static void Postfix(GameManager __instance) { if (((NetworkBehaviour)__instance).IsServer && GameModeManager.IsActive(GameMode.Juggernaut)) { JuggernautState.ServerTick(Time.deltaTime); } } } [HarmonyPatch(typeof(Minigun), "Update")] internal static class Minigun_JuggernautAmmoDisplay_Patch { private static void Postfix(Minigun __instance) { if (GameModeManager.IsActive(GameMode.Juggernaut) && !((Object)(object)__instance == (Object)null) && ((Object)__instance).name.StartsWith("Minigun", StringComparison.Ordinal)) { ((Weapon)__instance).currentAmmo = 1; if (((NetworkBehaviour)__instance).IsOwner && (Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.ChangeAmmoText("1", ((Weapon)__instance).chargedBullets + " / ", ((Weapon)__instance).inRightHand); } } } } [HarmonyPatch(typeof(Minigun), "Reload")] internal static class Minigun_JuggernautReload_Patch { private static bool Prefix(Minigun __instance) { if (GameModeManager.IsActive(GameMode.Juggernaut) && !((Object)(object)__instance == (Object)null)) { return !((Object)__instance).name.StartsWith("Minigun", StringComparison.Ordinal); } return true; } } internal static class JuggernautState { internal const string WeaponName = "Minigun"; internal const float BaseHealth = 8f; internal const float HealthPerKill = 2f; internal const float MovementMultiplier = 0.5f; internal static bool Enabled; internal static int CurrentJuggernautPlayerId = -1; internal static int CurrentJuggernautKills; internal static int WinnerId = -1; internal static readonly Dictionary Points = new Dictionary(); private static float _nextLoadoutCheckTime; private static readonly ModeSyncState Sync = new ModeSyncState(3f, 1f); private static readonly Dictionary PendingLoadouts = new Dictionary(); internal static int PointsToWin => GameModeManager.EffectivePointsToWin; internal static void ResetMatchState() { Sync.ResetLiveState(); CurrentJuggernautPlayerId = -1; CurrentJuggernautKills = 0; WinnerId = -1; Points.Clear(); PendingLoadouts.Clear(); } internal static void ApplySettings(bool enabled) { bool enabled2 = Enabled; Enabled = enabled; if (enabled2 && !enabled) { ResetMatchState(); } } private static void ApplySettingsFromHostConfig() { ApplySettings(Plugin.JuggernautEnabled.Value); } internal static void PushSettingsIfHost() { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); Plugin.Logger.LogInfo((object)$"[Juggernaut] Host broadcasting settings to {MyceliumNetwork.PlayerCount} player(s)"); MyceliumNetwork.RPC(3141592653u, "SyncJuggernautSettings", (ReliableType)1, SettingsRpcArgs(Sync.NextSettingsRevision())); } } internal static void PeriodicPushSettingsIfHost() { if (Sync.IsSettingsPushDue()) { PushSettingsIfHost(); } } internal static void PeriodicPushIfHost() { PeriodicPushSettingsIfHost(); if (Enabled && GameModeManager.IsActive(GameMode.Juggernaut) && Sync.IsLivePushDue()) { BroadcastLiveState(); } } internal static void OnLobbyEntered() { Sync.ResetForLobby(); Plugin.Logger.LogInfo((object)$"[Juggernaut] Lobby session started, IsHost={MyceliumNetwork.IsHost}"); if (MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); ResetMatchState(); } } internal static void OnPlayerEntered(CSteamID player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { Plugin.Logger.LogInfo((object)$"[Juggernaut] Sending catch-up settings/state to newly joined player {player}"); MyceliumNetwork.RPCTarget(3141592653u, "SyncJuggernautSettings", player, (ReliableType)1, SettingsRpcArgs(Sync.SettingsRevision)); MyceliumNetwork.RPCTarget(3141592653u, "SyncJuggernautLiveState", player, (ReliableType)1, new object[6] { MyceliumNetwork.LobbyHost, CurrentJuggernautPlayerId, CurrentJuggernautKills, SerializePoints(), GameModeManager.RoundId, Sync.LiveRevision }); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } private static object[] SettingsRpcArgs(int revision) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) return new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, revision, Plugin.JuggernautEnabled.Value }; } internal static void ApplyLiveState(CSteamID hostId, int juggernautPlayerId, int juggernautKills, string pointsData, int roundId, int revision) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (juggernautPlayerId < -1 || juggernautKills < 0 || !Sync.TryAcceptLiveSnapshot(hostId, roundId, revision)) { return; } CurrentJuggernautPlayerId = juggernautPlayerId; CurrentJuggernautKills = juggernautKills; Points.Clear(); foreach (KeyValuePair item in ScoreCodec.Parse(pointsData, PointsToWin)) { Points[item.Key] = item.Value; } } internal static string SerializePoints() { return ScoreCodec.Serialize(Points); } internal static void ServerTick(float deltaTime) { if (Enabled && Sync.IsLivePushDue()) { BroadcastLiveState(); } } internal static void OnServerKill(int deadPlayerId, int killerId) { if (!Enabled || WinnerId >= 0) { return; } bool flag = killerId >= 0 && killerId != deadPlayerId; if (CurrentJuggernautPlayerId < 0) { if (flag) { AwardPoints(killerId, 10); BecomeJuggernaut(killerId, PlayerLookup.GetPlayerNameTag(killerId) + " drew first blood and is the JUGGERNAUT!"); } } else if (deadPlayerId == CurrentJuggernautPlayerId) { if (flag) { AwardPoints(killerId, 20); BecomeJuggernaut(killerId, PlayerLookup.GetPlayerNameTag(killerId) + " slayed the Juggernaut and claimed the crown!"); } } else if (flag && killerId == CurrentJuggernautPlayerId) { CurrentJuggernautKills++; AwardPoints(killerId, 10); GrantHealthForKill(killerId); BroadcastLiveState(); } } private static void BecomeJuggernaut(int playerId, string announcement) { CurrentJuggernautPlayerId = playerId; CurrentJuggernautKills = 0; Announce(announcement); AnnounceTarget(playerId, "You are now the JUGGERNAUT!"); SetHealthToMax(playerId); GiveStartingWeapon(playerId); BroadcastLiveState(); } private static void AwardPoints(int playerId, int amount) { Points.TryGetValue(playerId, out var value); int num = value + amount; Points[playerId] = num; GameModeHud.ShowScorePopupForPlayer(playerId, amount); if (num >= PointsToWin) { WinnerId = playerId; Announce(PlayerLookup.GetPlayerNameTag(playerId) + " reached " + PointsToWin + " points and won the round!"); GameModeManager.CompleteCustomRound(ScoreManager.Instance.GetTeamId(playerId)); } } internal static bool IsCurrentJuggernaut(PlayerHealth health) { if (GameModeManager.IsActive(GameMode.Juggernaut)) { return health.playerValues?.playerClient?.PlayerId == CurrentJuggernautPlayerId; } return false; } internal static bool IsCurrentJuggernaut(FirstPersonController? controller) { if ((Object)(object)controller == (Object)null) { return false; } PlayerHealth component = ((Component)controller).GetComponent(); if ((Object)(object)component != (Object)null) { return IsCurrentJuggernaut(component); } return false; } internal static bool IsCurrentJuggernautWeapon(Weapon weapon) { if ((Object)(object)weapon == (Object)null || !((Object)weapon).name.StartsWith("Minigun", StringComparison.Ordinal)) { return false; } PlayerHealth val = ((Component)weapon).GetComponentInParent(); if ((Object)(object)val == (Object)null && (Object)(object)weapon.rootObject != (Object)null) { val = weapon.rootObject.GetComponent(); } if ((Object)(object)val != (Object)null) { return IsCurrentJuggernaut(val); } return false; } internal static void ApplyHealth(PlayerHealth health) { if (IsCurrentJuggernaut(health)) { health.fullHealth = GetMaximumHealth(); } } internal static void EnsureLoadout() { if (Enabled && CurrentJuggernautPlayerId >= 0 && MyceliumNetwork.InLobby && MyceliumNetwork.IsHost && !WeaponService.IsFinalGameScreen && !(Time.unscaledTime < _nextLoadoutCheckTime)) { _nextLoadoutCheckTime = Time.unscaledTime + 1f; PlayerHealth val = PlayerLookup.FindPlayerHealthById(CurrentJuggernautPlayerId); GameObject val2 = ((((Object)(object)val == (Object)null) ? null : GameModeRespawn.FindManager(val))?.player?.playerPickupScript)?.objInHand; Weapon val3 = (((Object)(object)val2 == (Object)null || !Object.op_Implicit((Object)(object)val2)) ? null : val2.GetComponent()); float value; if ((Object)(object)val3 != (Object)null && IsCurrentJuggernautWeapon(val3)) { PendingLoadouts.Remove(CurrentJuggernautPlayerId); } else if (!PendingLoadouts.TryGetValue(CurrentJuggernautPlayerId, out value) || Time.unscaledTime >= value) { GiveStartingWeapon(CurrentJuggernautPlayerId); } } } internal static void GiveStartingWeapon(int playerId) { if (Enabled && playerId == CurrentJuggernautPlayerId) { PendingLoadouts[playerId] = Time.unscaledTime + 5f; WeaponService.GiveWeapon(playerId, "Minigun", null, unlimitedAmmo: true); } } internal static float GetMaximumHealth() { return 8f + (float)CurrentJuggernautKills * 2f; } private static void SetHealthToMax(int playerId) { PlayerHealth val = PlayerLookup.FindPlayerHealthById(playerId); if (!((Object)(object)val == (Object)null) && ((NetworkBehaviour)val).IsServer) { float num = (val.fullHealth = GetMaximumHealth()) - val.sync___get_value_health(); if (!Mathf.Approximately(num, 0f)) { FishNetCompatibility.TryRemoveHealth(val, 0f - num); } } } private static void GrantHealthForKill(int playerId) { PlayerHealth val = PlayerLookup.FindPlayerHealthById(playerId); if (!((Object)(object)val == (Object)null) && ((NetworkBehaviour)val).IsServer) { val.fullHealth = GetMaximumHealth(); FishNetCompatibility.TryRemoveHealth(val, -2f); } } private static void AnnounceTarget(int playerId, string text) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { if ((Object)(object)ClientInstance.Instance != (Object)null && ClientInstance.Instance.PlayerId == playerId && (Object)(object)PauseManager.Instance != (Object)null) { GameModeHud.AnnounceTarget(ClientInstance.ReplaceAllPlayerNameTags(text)); } if (ClientInstance.playerInstances.TryGetValue(playerId, out var value) && value.PlayerSteamID != 0L) { MyceliumNetwork.RPCTarget(3141592653u, "JuggernautAnnounceTarget", new CSteamID(value.PlayerSteamID), (ReliableType)1, new object[1] { text }); } } } internal static void BroadcastLiveState() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(3141592653u, "SyncJuggernautLiveState", (ReliableType)1, new object[6] { MyceliumNetwork.LobbyHost, CurrentJuggernautPlayerId, CurrentJuggernautKills, SerializePoints(), GameModeManager.RoundId, Sync.NextLiveRevision() }); } } private static void Announce(string text) { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(3141592653u, "JuggernautAnnounce", (ReliableType)1, new object[1] { text }); } } } internal static class KillTheRatOutline { private static PlayerHealth? _outlinedPlayer; internal static void ResetState() { _outlinedPlayer = null; } internal static void EnforceOutline() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (PlayerOutline.UpdateMode(GameModeManager.ActiveMode)) { _outlinedPlayer = null; } if (GameModeManager.ShouldClearPlayerOutlines) { return; } if (!GameModeManager.IsActive(GameMode.KillTheRat)) { if (!GameModeManager.IsActive(GameMode.Juggernaut) && !GameModeManager.IsActive(GameMode.MichaelMeyers) && (Object)(object)_outlinedPlayer != (Object)null) { PlayerOutline.ClearTarget(ref _outlinedPlayer); } _outlinedPlayer = null; } else { PlayerHealth target = PlayerLookup.FindPlayerHealthById(KillTheRatState.CurrentRatPlayerId); PlayerOutline.ApplySingleTarget(ref _outlinedPlayer, target, Color.yellow); } } } [HarmonyPatch(typeof(GameManager), "Update")] internal static class GameManager_KillTheRatTick_Patch { private static void Postfix(GameManager __instance) { if (((NetworkBehaviour)__instance).IsServer) { KillTheRatState.ServerTick(Time.unscaledDeltaTime); } } } [HarmonyPatch(typeof(PlayerManager), "SpawnPlayer", new Type[] { typeof(int), typeof(int), typeof(Vector3), typeof(Quaternion) })] internal static class PlayerManager_KillTheRatSpawn_Patch { private static void Postfix(PlayerManager __instance) { if (GameModeManager.IsActive(GameMode.KillTheRat) && !WeaponService.IsFinalGameScreen && !((Object)(object)__instance.player == (Object)null)) { ClientInstance component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { KillTheRatState.RequestLoadout(component.PlayerId); } } } } internal static class KillTheRatState { internal const string HumanWeaponName = "Glock"; internal const string RatWeaponName = "Taser"; internal const float RatHealthMultiplier = 0.5f; internal const float RatMovementMultiplier = 2f; internal static bool Enabled; internal static int CurrentRatPlayerId = -1; internal static int WinnerId = -1; internal static readonly Dictionary Points = new Dictionary(); private static float _nextLoadoutCheckTime; private static float _survivalAccumulator; private static readonly ModeSyncState Sync = new ModeSyncState(); private static readonly Dictionary PendingLoadouts = new Dictionary(); internal static int PointsToWin => GameModeManager.EffectivePointsToWin; internal static void ApplySettings(bool enabled) { bool num = Enabled != enabled; Enabled = enabled; if (num) { ResetMatchState(); } } private static void ApplySettingsFromHostConfig() { ApplySettings(Plugin.KillTheRatEnabled.Value); } internal static void PushSettingsIfHost() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); MyceliumNetwork.RPC(3141592655u, "SyncKillTheRatSettings", (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.NextSettingsRevision(), Plugin.KillTheRatEnabled.Value }); } } internal static void PeriodicPushSettingsIfHost() { if (Sync.IsSettingsPushDue()) { PushSettingsIfHost(); } } internal static void PeriodicPushIfHost() { if (Sync.IsLivePushDue()) { BroadcastLiveState(); } } internal static void OnLobbyEntered() { Sync.ResetForLobby(); if (MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); ResetMatchState(); } } internal static void OnPlayerEntered(CSteamID player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { MyceliumNetwork.RPCTarget(3141592655u, "SyncKillTheRatSettings", player, (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.SettingsRevision, Plugin.KillTheRatEnabled.Value }); MyceliumNetwork.RPCTarget(3141592655u, "SyncKillTheRatLiveState", player, (ReliableType)1, new object[6] { MyceliumNetwork.LobbyHost, CurrentRatPlayerId, SerializePoints(), WinnerId, GameModeManager.RoundId, Sync.LiveRevision }); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } internal static void ResetMatchState() { bool num = !MyceliumNetwork.IsHost && Sync.LastLiveRoundId >= GameModeManager.RoundId; Sync.ResetLiveState(); _survivalAccumulator = 0f; _nextLoadoutCheckTime = 0f; if (!num) { CurrentRatPlayerId = -1; WinnerId = -1; Points.Clear(); } PendingLoadouts.Clear(); } internal static void ApplyLiveState(CSteamID hostId, int ratPlayerId, string pointsData, int winnerId, int roundId, int revision) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (ratPlayerId < -1 || winnerId < -1 || !Sync.TryAcceptLiveSnapshot(hostId, roundId, revision)) { return; } CurrentRatPlayerId = ratPlayerId; WinnerId = winnerId; Points.Clear(); foreach (KeyValuePair item in ScoreCodec.Parse(pointsData, PointsToWin)) { Points[item.Key] = item.Value; } } internal static void ServerTick(float deltaTime) { if (!Enabled || !GameModeManager.IsActive(GameMode.KillTheRat) || !MyceliumNetwork.InLobby || !MyceliumNetwork.IsHost || CurrentRatPlayerId < 0 || WinnerId >= 0) { return; } _survivalAccumulator += Mathf.Max(0f, deltaTime); int num = Mathf.FloorToInt(_survivalAccumulator); if (num <= 0) { return; } _survivalAccumulator -= num; for (int i = 0; i < num; i++) { if (WinnerId >= 0) { break; } AwardPoints(CurrentRatPlayerId, 1); } } internal static void OnServerKill(int deadPlayerId, int killerId) { if (!Enabled || !GameModeManager.IsActive(GameMode.KillTheRat) || WinnerId >= 0) { return; } bool flag = killerId >= 0 && killerId != deadPlayerId; if (deadPlayerId == CurrentRatPlayerId) { if (flag && killerId != CurrentRatPlayerId && AwardPoints(killerId, 10)) { BecomeRat(killerId, PlayerLookup.GetPlayerNameTag(killerId) + " killed the RAT and is now the RAT! Kill them!"); } else if (WinnerId < 0) { ClearRat(); } } else if (CurrentRatPlayerId < 0 && flag) { BecomeRat(killerId, PlayerLookup.GetPlayerNameTag(killerId) + " got the first kill and is now the RAT! Kill them!"); } } internal static bool IsRat(PlayerHealth health) { if (GameModeManager.IsActive(GameMode.KillTheRat)) { return GetPlayerId(health) == CurrentRatPlayerId; } return false; } internal static bool IsRat(FirstPersonController controller) { if ((Object)(object)controller == (Object)null || !GameModeManager.IsActive(GameMode.KillTheRat)) { return false; } if (((Component)controller).GetComponent()?.playerClient?.PlayerId != CurrentRatPlayerId) { return IsRat(((Component)controller).GetComponent()); } return true; } internal static bool IsRatWeapon(Weapon weapon) { if ((Object)(object)weapon != (Object)null) { return ((Object)weapon).name.StartsWith("Taser", StringComparison.Ordinal); } return false; } internal static bool IsHumanWeapon(Weapon weapon) { if ((Object)(object)weapon != (Object)null) { return ((Object)weapon).name.StartsWith("Glock", StringComparison.Ordinal); } return false; } internal static void ApplyHealth(PlayerHealth controller, float baselineFullHealth) { float num = (controller.fullHealth = baselineFullHealth * 0.5f); if (!((NetworkBehaviour)controller).IsServer) { return; } float num2 = controller.sync___get_value_health() - num; if (num2 <= 0f) { return; } HealthSettingsTuning.ApplyingPassiveHealth = true; try { FishNetCompatibility.TryRemoveHealth(controller, num2); } finally { HealthSettingsTuning.ApplyingPassiveHealth = false; } } internal static void EnsureLoadouts() { if (!Enabled || !GameModeManager.IsActive(GameMode.KillTheRat) || !MyceliumNetwork.InLobby || !MyceliumNetwork.IsHost || WeaponService.IsFinalGameScreen || Time.unscaledTime < _nextLoadoutCheckTime) { return; } _nextLoadoutCheckTime = Time.unscaledTime + 0.5f; foreach (ClientInstance value2 in ClientInstance.playerInstances.Values) { if ((Object)(object)value2 == (Object)null || !Object.op_Implicit((Object)(object)value2) || (Object)(object)value2.PlayerSpawner == (Object)null || !Object.op_Implicit((Object)(object)value2.PlayerSpawner)) { continue; } PlayerPickup val = value2.PlayerSpawner.player?.playerPickupScript; if ((Object)(object)val == (Object)null) { continue; } bool flag = value2.PlayerId == CurrentRatPlayerId; string text = (flag ? "Taser" : "Glock"); Weapon val2 = GetWeapon(val.objInHand) ?? GetWeapon(val.objInLeftHand); float value; if ((Object)(object)val2 != (Object)null && ((Object)val2).name.StartsWith(text, StringComparison.Ordinal)) { if (!flag) { WeaponAmmoTuning.InitializeUnlimited(val2); } PendingLoadouts.Remove(value2.PlayerId); } else if (!PendingLoadouts.TryGetValue(value2.PlayerId, out value) || Time.unscaledTime >= value) { PendingLoadouts[value2.PlayerId] = Time.unscaledTime + 2f; int playerId = value2.PlayerId; bool unlimitedAmmo = !flag; WeaponService.GiveWeapon(playerId, text, null, unlimitedAmmo); } } } internal static void RequestLoadout(int playerId) { if (Enabled && GameModeManager.IsActive(GameMode.KillTheRat) && MyceliumNetwork.IsHost) { bool flag = playerId == CurrentRatPlayerId; PendingLoadouts[playerId] = Time.unscaledTime + 2f; string weaponName = (flag ? "Taser" : "Glock"); bool unlimitedAmmo = !flag; WeaponService.GiveWeapon(playerId, weaponName, null, unlimitedAmmo); } } private static bool AwardPoints(int playerId, int amount) { Points.TryGetValue(playerId, out var value); int num = value + amount; Points[playerId] = num; GameModeHud.ShowScorePopupForPlayer(playerId, amount); if (num >= PointsToWin) { WinnerId = playerId; Announce(PlayerLookup.GetPlayerNameTag(playerId) + " reached " + PointsToWin + " points and won the KILL THE RAT round!"); BroadcastLiveState(); GameModeManager.CompleteCustomRound(ScoreManager.Instance.GetTeamId(playerId)); return false; } BroadcastLiveState(); return true; } private static void BecomeRat(int playerId, string announcement) { CurrentRatPlayerId = playerId; _survivalAccumulator = 0f; Announce(announcement); PendingLoadouts.Remove(playerId); RequestLoadout(playerId); BroadcastLiveState(); } private static void ClearRat() { CurrentRatPlayerId = -1; _survivalAccumulator = 0f; PendingLoadouts.Clear(); Announce("The RAT died. Humans, kill each other to choose a new RAT!"); BroadcastLiveState(); } private static Weapon? GetWeapon(GameObject? heldObject) { if (!((Object)(object)heldObject == (Object)null) && Object.op_Implicit((Object)(object)heldObject)) { return heldObject.GetComponent(); } return null; } private static int GetPlayerId(PlayerHealth health) { return health.playerValues?.playerClient?.PlayerId ?? (-1); } private static string SerializePoints() { return ScoreCodec.Serialize(Points); } private static void BroadcastLiveState() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(3141592655u, "SyncKillTheRatLiveState", (ReliableType)1, new object[6] { MyceliumNetwork.LobbyHost, CurrentRatPlayerId, SerializePoints(), WinnerId, GameModeManager.RoundId, Sync.NextLiveRevision() }); } } private static void Announce(string text) { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(3141592655u, "KillTheRatAnnounce", (ReliableType)1, new object[1] { text }); } } } internal static class MichaelMeyersOutline { private static readonly Dictionary OutlinedPlayers = new Dictionary(); internal static void ResetState() { OutlinedPlayers.Clear(); } internal static void EnforceOutline() { //IL_015c: Unknown result type (might be due to invalid IL or missing references) if (PlayerOutline.UpdateMode(GameModeManager.ActiveMode)) { OutlinedPlayers.Clear(); } if (!GameModeManager.IsActive(GameMode.MichaelMeyers)) { if (!GameModeManager.IsActive(GameMode.Juggernaut) && PlayerOutline.HasAppliedRenderers) { PlayerOutline.ClearApplied(); OutlinedPlayers.Clear(); } return; } int num = (((Object)(object)ClientInstance.Instance == (Object)null) ? (-1) : ClientInstance.Instance.PlayerId); if (num < 0 || MichaelMeyersState.CurrentMichaelPlayerId != num) { ClearApplied(); return; } Dictionary dictionary = new Dictionary(); foreach (int connectedPlayerId in PlayerLookup.GetConnectedPlayerIds()) { if (connectedPlayerId != num) { PlayerHealth val = PlayerLookup.FindPlayerHealthById(connectedPlayerId); if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy) { dictionary[connectedPlayerId] = val; } } } if (!TargetsMatch(dictionary)) { PlayerOutline.ClearApplied(); OutlinedPlayers.Clear(); foreach (KeyValuePair item in dictionary) { OutlinedPlayers[item.Key] = item.Value; } } foreach (PlayerHealth value in OutlinedPlayers.Values) { if ((Object)(object)value != (Object)null && ((Component)value).gameObject.activeInHierarchy) { PlayerOutline.Apply(value, Color.red); } } } private static bool TargetsMatch(Dictionary currentTargets) { if (currentTargets.Count != OutlinedPlayers.Count) { return false; } foreach (KeyValuePair currentTarget in currentTargets) { if (!OutlinedPlayers.TryGetValue(currentTarget.Key, out PlayerHealth value) || value != currentTarget.Value) { return false; } } return true; } private static void ClearApplied() { PlayerOutline.ClearApplied(); OutlinedPlayers.Clear(); } } internal static class MichaelMeyersState { internal const string WeaponName = "Couperet"; internal const string SurvivorWeaponName = "Couperet"; private const float SurvivorWeaponDelaySeconds = 3f; internal const float MovementMultiplier = 1.05f; internal static bool Enabled; internal static int CurrentMichaelPlayerId = -1; private static int _oneVsOneSurvivorId = -1; internal static bool OneVsOne; private static int _winnerId = -1; private static int _roundToken; private static float _nextLoadoutCheckTime; private static readonly ModeSyncState Sync = new ModeSyncState(); private static readonly HashSet RoundPlayers = new HashSet(); private static readonly HashSet AlivePlayers = new HashSet(); private static readonly Dictionary PendingLoadouts = new Dictionary(); internal static int SurvivorCount { get; private set; } internal static void ApplySettings(bool enabled) { if (Enabled != enabled) { ResetMatchState(); } Enabled = enabled; } private static void ApplyFromConfig() { ApplySettings(Plugin.MichaelMeyersEnabled.Value); } internal static void PushSettingsIfHost() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplyFromConfig(); MyceliumNetwork.RPC(3141592654u, "SyncMichaelMeyersSettings", (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.NextSettingsRevision(), Plugin.MichaelMeyersEnabled.Value }); } } internal static void PeriodicPushSettingsIfHost() { if (Sync.IsSettingsPushDue()) { PushSettingsIfHost(); } } internal static void PeriodicPushLiveStateIfHost() { if (Sync.IsLivePushDue()) { BroadcastLiveState(); } } internal static void OnLobbyEntered() { Sync.ResetForLobby(); if (MyceliumNetwork.IsHost) { ApplyFromConfig(); ResetMatchState(); } } internal static void OnPlayerEntered(CSteamID player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { MyceliumNetwork.RPCTarget(3141592654u, "SyncMichaelMeyersSettings", player, (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.SettingsRevision, Plugin.MichaelMeyersEnabled.Value }); MyceliumNetwork.RPCTarget(3141592654u, "SyncMichaelMeyersLiveState", player, (ReliableType)1, new object[6] { MyceliumNetwork.LobbyHost, CurrentMichaelPlayerId, SurvivorCount, OneVsOne, GameModeManager.RoundId, Sync.LiveRevision }); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } internal static void ResetMatchState() { _roundToken++; Sync.ResetLiveState(); _winnerId = -1; CurrentMichaelPlayerId = -1; SurvivorCount = 0; _oneVsOneSurvivorId = -1; OneVsOne = false; RoundPlayers.Clear(); AlivePlayers.Clear(); PendingLoadouts.Clear(); _nextLoadoutCheckTime = 0f; } internal static void ApplyLiveState(CSteamID hostId, int michaelPlayerId, int survivorCount, bool oneVsOne, int roundId, int revision) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (michaelPlayerId >= -1 && survivorCount >= 0 && Sync.TryAcceptLiveSnapshot(hostId, roundId, revision)) { CurrentMichaelPlayerId = michaelPlayerId; SurvivorCount = survivorCount; OneVsOne = oneVsOne; } } internal static void OnRoundStarted() { if (!Enabled || !GameModeManager.IsActive(GameMode.MichaelMeyers) || !MyceliumNetwork.IsHost) { return; } ResetMatchState(); foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && Object.op_Implicit((Object)(object)value)) { RoundPlayers.Add(value.PlayerId); AlivePlayers.Add(value.PlayerId); } } SurvivorCount = Math.Max(0, AlivePlayers.Count - 1); if (RoundPlayers.Count >= 2 && !((Object)(object)Plugin.Instance == (Object)null)) { int roundToken = _roundToken; ((MonoBehaviour)Plugin.Instance).StartCoroutine(SelectMichaelAfterDelay(roundToken, SessionState.Generation, GameModeManager.RoundId)); BroadcastLiveState(); } } private static IEnumerator SelectMichaelAfterDelay(int token, int sessionGeneration, int roundId) { yield return (object)new WaitForSeconds(5f); if (!SessionState.IsCurrent(sessionGeneration) || GameModeManager.RoundId != roundId || token != _roundToken || !Enabled || !GameModeManager.IsActive(GameMode.MichaelMeyers) || _winnerId >= 0) { yield break; } List list = new List(); foreach (int alivePlayer in AlivePlayers) { if (ClientInstance.playerInstances.ContainsKey(alivePlayer)) { list.Add(alivePlayer); } } if (list.Count >= 2) { CurrentMichaelPlayerId = list[Random.Range(0, list.Count)]; PrepareOneVsOneSurvivor(); Announce(PlayerLookup.GetPlayerNameTag(CurrentMichaelPlayerId) + " is MICHAEL MEYERS!"); BroadcastLiveState(); GiveStartingWeapon(CurrentMichaelPlayerId); } } internal static void EnsureLoadouts() { if (!Enabled || !GameModeManager.IsActive(GameMode.MichaelMeyers) || !MyceliumNetwork.InLobby || !MyceliumNetwork.IsHost || WeaponService.IsFinalGameScreen || Time.unscaledTime < _nextLoadoutCheckTime) { return; } _nextLoadoutCheckTime = Time.unscaledTime + 0.5f; foreach (int alivePlayer in AlivePlayers) { PlayerPickup val = FindPickup(alivePlayer); if ((Object)(object)val == (Object)null) { continue; } if (CanHoldCouperet(alivePlayer)) { float value; if (IsCouperetHeld(val)) { PendingLoadouts.Remove(alivePlayer); } else if (!PendingLoadouts.TryGetValue(alivePlayer, out value) || Time.unscaledTime >= value) { PendingLoadouts[alivePlayer] = Time.unscaledTime + 2f; WeaponService.GiveWeapon(alivePlayer, "Couperet"); } } else if (CanHoldSurvivorWeapon(alivePlayer)) { float value2; if (IsWeaponHeld(val, "Couperet")) { PendingLoadouts.Remove(alivePlayer); } else if (!PendingLoadouts.TryGetValue(alivePlayer, out value2) || Time.unscaledTime >= value2) { PendingLoadouts[alivePlayer] = Time.unscaledTime + 2f; WeaponService.GiveWeapon(alivePlayer, "Couperet"); } } else if (HasHeldObject(val)) { WeaponService.ClearHeldWeapons(val); } } } internal static void OnServerKill(int deadPlayerId, int killerId) { if (!Enabled || !GameModeManager.IsActive(GameMode.MichaelMeyers) || _winnerId >= 0) { return; } EnsureTrackedPlayers(); if (!AlivePlayers.Remove(deadPlayerId)) { return; } SurvivorCount = Math.Max(0, AlivePlayers.Count - 1); if (AlivePlayers.Count <= 1) { int winnerId = -1; using (HashSet.Enumerator enumerator = AlivePlayers.GetEnumerator()) { if (enumerator.MoveNext()) { winnerId = enumerator.Current; } } FinishRound(winnerId); } else { if (CurrentMichaelPlayerId >= 0 && deadPlayerId != CurrentMichaelPlayerId && AlivePlayers.Count == 2 && AlivePlayers.Contains(CurrentMichaelPlayerId)) { PrepareOneVsOneSurvivor(); } BroadcastLiveState(); } } internal static bool IsMichael(PlayerHealth health) { if (GameModeManager.IsActive(GameMode.MichaelMeyers)) { return health.playerValues?.playerClient?.PlayerId == CurrentMichaelPlayerId; } return false; } internal static bool CanHoldCouperet(PlayerHealth health) { return CanHoldCouperet(health.playerValues?.playerClient?.PlayerId ?? (-1)); } private static bool CanHoldCouperet(int playerId) { if (playerId >= 0 && GameModeManager.IsActive(GameMode.MichaelMeyers)) { return playerId == CurrentMichaelPlayerId; } return false; } internal static bool CanHoldSurvivorWeapon(PlayerHealth health) { return CanHoldSurvivorWeapon(health.playerValues?.playerClient?.PlayerId ?? (-1)); } private static bool CanHoldSurvivorWeapon(int playerId) { if (playerId >= 0 && GameModeManager.IsActive(GameMode.MichaelMeyers) && OneVsOne) { return playerId == _oneVsOneSurvivorId; } return false; } internal static bool IsMichael(FirstPersonController controller) { PlayerHealth val = (((Object)(object)controller == (Object)null) ? null : ((Component)controller).GetComponent()); if ((Object)(object)val != (Object)null) { return IsMichael(val); } return false; } internal static bool IsCouperet(Weapon weapon) { if ((Object)(object)weapon != (Object)null) { return ((Object)weapon).name.StartsWith("Couperet", StringComparison.Ordinal); } return false; } private static void EnsureTrackedPlayers() { if (RoundPlayers.Count != 0) { return; } foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && Object.op_Implicit((Object)(object)value)) { RoundPlayers.Add(value.PlayerId); AlivePlayers.Add(value.PlayerId); } } } private static PlayerPickup? FindPickup(int playerId) { if (!ClientInstance.playerInstances.TryGetValue(playerId, out var value) || (Object)(object)value == (Object)null || !Object.op_Implicit((Object)(object)value) || (Object)(object)value.PlayerSpawner == (Object)null || !Object.op_Implicit((Object)(object)value.PlayerSpawner)) { return null; } return value.PlayerSpawner.player?.playerPickupScript; } private static bool IsCouperetHeld(PlayerPickup pickup) { return IsWeaponHeld(pickup, "Couperet"); } private static bool IsWeaponHeld(PlayerPickup pickup, string weaponName) { Weapon weapon = GetWeapon(pickup.objInHand); Weapon weapon2 = GetWeapon(pickup.objInLeftHand); if (!((Object)(object)weapon != (Object)null) || !((Object)weapon).name.StartsWith(weaponName, StringComparison.Ordinal)) { if ((Object)(object)weapon2 != (Object)null) { return ((Object)weapon2).name.StartsWith(weaponName, StringComparison.Ordinal); } return false; } return true; } private static bool HasHeldObject(PlayerPickup pickup) { if (!((Object)(object)pickup.objInHand != (Object)null) || !Object.op_Implicit((Object)(object)pickup.objInHand)) { if ((Object)(object)pickup.objInLeftHand != (Object)null) { return Object.op_Implicit((Object)(object)pickup.objInLeftHand); } return false; } return true; } private static Weapon? GetWeapon(GameObject? heldObject) { if (!((Object)(object)heldObject == (Object)null) && Object.op_Implicit((Object)(object)heldObject)) { return heldObject.GetComponent(); } return null; } private static void FinishRound(int winnerId) { if (winnerId >= 0 && !((Object)(object)ScoreManager.Instance == (Object)null)) { _winnerId = winnerId; Announce(PlayerLookup.GetPlayerNameTag(winnerId) + " won the MICHAEL MEYERS round!"); BroadcastLiveState(); GameModeManager.CompleteCustomRound(ScoreManager.Instance.GetTeamId(winnerId)); } } internal static void GiveStartingWeapon(int playerId) { bool flag = playerId == CurrentMichaelPlayerId || (OneVsOne && AlivePlayers.Contains(playerId)); if (Enabled && GameModeManager.IsActive(GameMode.MichaelMeyers) && flag) { PendingLoadouts[playerId] = Time.unscaledTime + 2f; WeaponService.GiveWeapon(playerId, "Couperet"); } } private static void PrepareOneVsOneSurvivor() { if (AlivePlayers.Count != 2 || CurrentMichaelPlayerId < 0 || !AlivePlayers.Contains(CurrentMichaelPlayerId)) { return; } foreach (int alivePlayer in AlivePlayers) { if (alivePlayer != CurrentMichaelPlayerId) { _oneVsOneSurvivorId = alivePlayer; OneVsOne = true; PendingLoadouts[alivePlayer] = Time.unscaledTime + 3f; Announce(PlayerLookup.GetPlayerNameTag(alivePlayer) + " will receive a COUPERET for the final fight!"); break; } } } private static void BroadcastLiveState() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(3141592654u, "SyncMichaelMeyersLiveState", (ReliableType)1, new object[6] { MyceliumNetwork.LobbyHost, CurrentMichaelPlayerId, SurvivorCount, OneVsOne, GameModeManager.RoundId, Sync.NextLiveRevision() }); } } private static void Announce(string text) { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(3141592654u, "MichaelMeyersAnnounce", (ReliableType)1, new object[1] { text }); } } } [HarmonyPatch(typeof(PlayerManager), "SpawnPlayer", new Type[] { typeof(int), typeof(int), typeof(Vector3), typeof(Quaternion) })] internal static class PlayerManager_OneInTheChamberSpawn_Patch { private static void Postfix(PlayerManager __instance) { if (GameModeManager.IsActive(GameMode.OneInTheChamber) && !WeaponService.IsFinalGameScreen && !((Object)(object)__instance.player == (Object)null)) { ClientInstance component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { OneInTheChamberState.RequestLoadout(component.PlayerId); } } } } internal static class OneInTheChamberState { internal const float PlayerHealth = 10f; internal const string PistolWeaponName = "Pistol"; internal const string CouperetWeaponName = "Couperet"; internal static bool Enabled; internal static readonly HashSet AlivePlayers = new HashSet(); internal static readonly Dictionary ReserveBullets = new Dictionary(); private static float _nextLoadoutCheckTime; private static readonly ModeSyncState Sync = new ModeSyncState(); private static readonly HashSet RoundPlayers = new HashSet(); private static readonly Dictionary PendingRightLoadouts = new Dictionary(); private static readonly Dictionary PendingLeftLoadouts = new Dictionary(); internal static int AliveCount => AlivePlayers.Count; internal static void ApplySettings(bool enabled) { bool num = Enabled != enabled; Enabled = enabled; if (num) { ResetMatchState(); } } private static void ApplySettingsFromHostConfig() { ApplySettings(Plugin.OneInTheChamberEnabled.Value); } internal static void PushSettingsIfHost() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); MyceliumNetwork.RPC(2718281829u, "SyncOneInTheChamberSettings", (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.NextSettingsRevision(), Plugin.OneInTheChamberEnabled.Value }); } } internal static void PeriodicPushSettingsIfHost() { if (Sync.IsSettingsPushDue()) { PushSettingsIfHost(); } } internal static void PeriodicPushIfHost() { if (Sync.IsLivePushDue()) { SyncReserveBulletsFromWeapons(); BroadcastLiveState(); } } internal static void OnLobbyEntered() { Sync.ResetForLobby(); if (MyceliumNetwork.IsHost) { ApplySettingsFromHostConfig(); ResetMatchState(); } } internal static void OnPlayerEntered(CSteamID player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { MyceliumNetwork.RPCTarget(2718281829u, "SyncOneInTheChamberSettings", player, (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.SettingsRevision, Plugin.OneInTheChamberEnabled.Value }); MyceliumNetwork.RPCTarget(2718281829u, "SyncOneInTheChamberLiveState", player, (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, SerializeAlive(), SerializeBullets(), GameModeManager.RoundId, Sync.LiveRevision }); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } internal static void ResetMatchState() { Sync.ResetLiveState(); _nextLoadoutCheckTime = 0f; AlivePlayers.Clear(); RoundPlayers.Clear(); ReserveBullets.Clear(); PendingRightLoadouts.Clear(); PendingLeftLoadouts.Clear(); } internal static void ApplyLiveState(CSteamID hostId, string aliveData, string bulletsData, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (!Sync.TryAcceptLiveSnapshot(hostId, roundId, revision)) { return; } AlivePlayers.Clear(); foreach (int item in ParseIds(aliveData)) { AlivePlayers.Add(item); } ReserveBullets.Clear(); foreach (KeyValuePair item2 in ScoreCodec.Parse(bulletsData, int.MaxValue)) { ReserveBullets[item2.Key] = item2.Value; } ApplyLocalReserveBullets(); } internal static void OnRoundStarted() { if (!Enabled || !GameModeManager.IsActive(GameMode.OneInTheChamber) || !MyceliumNetwork.IsHost) { return; } ResetMatchState(); foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && Object.op_Implicit((Object)(object)value)) { RoundPlayers.Add(value.PlayerId); AlivePlayers.Add(value.PlayerId); ReserveBullets[value.PlayerId] = 0; } } EnsureLoadouts(); BroadcastLiveState(); } internal static void OnServerKill(int deadPlayerId, int killerId) { if (!Enabled || !GameModeManager.IsActive(GameMode.OneInTheChamber)) { return; } EnsureTrackedPlayers(); if (OneInTheChamberRules.ApplyDeath(AlivePlayers, ReserveBullets, deadPlayerId, killerId)) { if (killerId >= 0 && killerId != deadPlayerId && AlivePlayers.Contains(killerId)) { AddBulletToPistol(killerId); } if (AlivePlayers.Count <= 1) { FinishRound(GetLastAlivePlayerId()); return; } SyncReserveBulletsFromWeapons(); BroadcastLiveState(); } } internal static void RequestLoadout(int playerId) { if (Enabled && GameModeManager.IsActive(GameMode.OneInTheChamber) && MyceliumNetwork.IsHost) { RequestRightLoadout(playerId); } } internal static bool IsPistol(Weapon weapon) { if ((Object)(object)weapon != (Object)null) { return ((Object)weapon).name.StartsWith("Pistol", StringComparison.Ordinal); } return false; } internal static bool IsCouperet(Weapon weapon) { if ((Object)(object)weapon != (Object)null) { return ((Object)weapon).name.StartsWith("Couperet", StringComparison.Ordinal); } return false; } internal static void ApplyHealth(PlayerHealth health) { health.fullHealth = 10f; if (!((NetworkBehaviour)health).IsServer) { return; } float num = health.sync___get_value_health() - 10f; if (num <= 0f) { return; } HealthSettingsTuning.ApplyingPassiveHealth = true; try { FishNetCompatibility.TryRemoveHealth(health, num); } finally { HealthSettingsTuning.ApplyingPassiveHealth = false; } } internal static void EnforcePistolAmmo(Weapon weapon, int playerId) { ReserveBullets.TryGetValue(playerId, out var value); WeaponAmmoTuning.InitializeSingleShot(weapon, value); WeaponAmmoTuning.TryStartManualReload(weapon, enabled: true, 0); if (((NetworkBehaviour)weapon).IsOwner && weapon.inRightHand && (Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.MoveAmmoDisplay(true, true); PauseManager.Instance.ChangeAmmoText(Math.Max(0, weapon.currentAmmo).ToString(), WeaponAmmoTuning.GetSpareRounds(weapon) + " / ", true); } } private static void EnsureTrackedPlayers() { if (RoundPlayers.Count != 0) { return; } foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && Object.op_Implicit((Object)(object)value)) { RoundPlayers.Add(value.PlayerId); AlivePlayers.Add(value.PlayerId); ReserveBullets.TryAdd(value.PlayerId, 0); } } } internal static void EnsureLoadouts() { if (!Enabled || !GameModeManager.IsActive(GameMode.OneInTheChamber) || !MyceliumNetwork.InLobby || !MyceliumNetwork.IsHost || WeaponService.IsFinalGameScreen || Time.unscaledTime < _nextLoadoutCheckTime) { return; } _nextLoadoutCheckTime = Time.unscaledTime + 0.5f; foreach (int alivePlayer in AlivePlayers) { if (!ClientInstance.playerInstances.TryGetValue(alivePlayer, out var value) || (Object)(object)value == (Object)null || !Object.op_Implicit((Object)(object)value) || (Object)(object)value.PlayerSpawner == (Object)null || !Object.op_Implicit((Object)(object)value.PlayerSpawner)) { continue; } PlayerPickup val = value.PlayerSpawner.player?.playerPickupScript; if (!((Object)(object)val == (Object)null)) { Weapon weapon = GetWeapon(val.objInHand); Weapon weapon2 = GetWeapon(val.objInLeftHand); float value3; if ((Object)(object)weapon != (Object)null && IsPistol(weapon)) { ReserveBullets.TryGetValue(alivePlayer, out var value2); WeaponAmmoTuning.InitializeSingleShot(weapon, value2); PendingRightLoadouts.Remove(alivePlayer); } else if (!PendingRightLoadouts.TryGetValue(alivePlayer, out value3) || Time.unscaledTime >= value3) { RequestRightLoadout(alivePlayer); } float value4; if ((Object)(object)weapon2 != (Object)null && IsCouperet(weapon2)) { PendingLeftLoadouts.Remove(alivePlayer); } else if (!PendingLeftLoadouts.TryGetValue(alivePlayer, out value4) || Time.unscaledTime >= value4) { PendingLeftLoadouts[alivePlayer] = Time.unscaledTime + 2f; WeaponService.GiveWeaponToLeftHand(alivePlayer, "Couperet"); } } } } private static void RequestRightLoadout(int playerId) { PendingRightLoadouts[playerId] = Time.unscaledTime + 2f; WeaponService.GiveWeapon(playerId, "Pistol"); } private static void AddBulletToPistol(int playerId) { PlayerPickup val = FindPickup(playerId); Weapon val2 = (((Object)(object)val == (Object)null) ? null : GetWeapon(val.objInHand)); if ((Object)(object)val2 != (Object)null && IsPistol(val2)) { WeaponAmmoTuning.AddSingleShotSpareRounds(val2, 1); } } private static void SyncReserveBulletsFromWeapons() { if (!MyceliumNetwork.IsHost) { return; } foreach (int alivePlayer in AlivePlayers) { PlayerPickup val = FindPickup(alivePlayer); Weapon val2 = (((Object)(object)val == (Object)null) ? null : GetWeapon(val.objInHand)); if ((Object)(object)val2 != (Object)null && IsPistol(val2) && WeaponAmmoTuning.IsSingleShot(val2)) { ReserveBullets[alivePlayer] = WeaponAmmoTuning.GetSpareRounds(val2); } } } private static void ApplyLocalReserveBullets() { if ((Object)(object)ClientInstance.Instance == (Object)null) { return; } int playerId = ClientInstance.Instance.PlayerId; if (ReserveBullets.TryGetValue(playerId, out var value)) { PlayerPickup val = ClientInstance.Instance.PlayerSpawner?.player?.playerPickupScript; Weapon val2 = (((Object)(object)val == (Object)null) ? null : GetWeapon(val.objInHand)); if ((Object)(object)val2 != (Object)null && IsPistol(val2)) { WeaponAmmoTuning.SetSingleShotSpareRounds(val2, value); } } } private static PlayerPickup? FindPickup(int playerId) { if (!ClientInstance.playerInstances.TryGetValue(playerId, out var value) || (Object)(object)value == (Object)null || !Object.op_Implicit((Object)(object)value) || (Object)(object)value.PlayerSpawner == (Object)null || !Object.op_Implicit((Object)(object)value.PlayerSpawner)) { return null; } return value.PlayerSpawner.player?.playerPickupScript; } private static Weapon? GetWeapon(GameObject? heldObject) { if (!((Object)(object)heldObject == (Object)null) && Object.op_Implicit((Object)(object)heldObject)) { return heldObject.GetComponent(); } return null; } private static int GetLastAlivePlayerId() { using (HashSet.Enumerator enumerator = AlivePlayers.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } } return -1; } private static void FinishRound(int winnerId) { if (winnerId >= 0 && !((Object)(object)ScoreManager.Instance == (Object)null)) { Announce(PlayerLookup.GetPlayerNameTag(winnerId) + " won the ONE IN THE CHAMBER round!"); BroadcastLiveState(); GameModeManager.CompleteCustomRound(ScoreManager.Instance.GetTeamId(winnerId)); } } private static string SerializeAlive() { return string.Join(",", AlivePlayers); } private static string SerializeBullets() { return ScoreCodec.Serialize(ReserveBullets); } private static IEnumerable ParseIds(string data) { string[] array = (data ?? string.Empty).Split(',', ';'); for (int i = 0; i < array.Length; i++) { if (int.TryParse(array[i], out var result) && result >= 0) { yield return result; } } } private static void BroadcastLiveState() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(2718281829u, "SyncOneInTheChamberLiveState", (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, SerializeAlive(), SerializeBullets(), GameModeManager.RoundId, Sync.NextLiveRevision() }); } } private static void Announce(string text) { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(2718281829u, "OneInTheChamberAnnounce", (ReliableType)1, new object[1] { text }); } } } internal static class FishNetCompatibility { private const BindingFlags MethodFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static MethodInfo? cmdRespawnLogic; private static bool cmdRespawnResolved; private static bool respawnUnavailableLogged; private static MethodInfo? removeHealthLogic; private static bool removeHealthResolved; private static bool removeHealthUnavailableLogged; internal static bool CanRespawn => ResolveCmdRespawnLogic() != null; internal static MethodInfo? FindGeneratedMethod(Type type, string namePrefix, Func signature) { return (from method in type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where method.Name.StartsWith(namePrefix, StringComparison.Ordinal) select method).Where(signature).OrderBy((MethodInfo method) => method.Name, StringComparer.Ordinal).FirstOrDefault(); } internal static void LogPreflight() { LogAssembly("Game", typeof(GameManager).Assembly); LogAssembly("FishNet", typeof(NetworkManager).Assembly); LogAssembly("Mycelium", typeof(MyceliumNetwork).Assembly); LogAssembly("Computerys", typeof(StraftatModAttribute).Assembly); MethodInfo methodInfo = ResolveCmdRespawnLogic(); MethodInfo methodInfo2 = ResolveRemoveHealthLogic(); Plugin.Logger.LogInfo((object)("[Compatibility] Respawn=" + ((methodInfo == null) ? "missing" : methodInfo.Name) + ", RemoveHealth=" + ((methodInfo2 == null) ? "missing" : methodInfo2.Name))); } private static void LogAssembly(string label, Assembly assembly) { Plugin.Logger.LogInfo((object)("[Compatibility] " + label + " assembly: " + assembly.GetName().FullName)); } internal static bool TryInvokeRespawn(PlayerManager manager) { MethodInfo methodInfo = ResolveCmdRespawnLogic(); if (methodInfo == null || (Object)(object)manager == (Object)null) { return false; } try { methodInfo.Invoke(manager, null); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[Compatibility] FishNet respawn invocation failed: " + ex.GetBaseException().Message)); return false; } } internal static bool TryRemoveHealth(PlayerHealth health, float damage) { MethodInfo methodInfo = ResolveRemoveHealthLogic(); if (methodInfo == null || (Object)(object)health == (Object)null) { return false; } try { methodInfo.Invoke(health, new object[1] { damage }); return true; } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[Compatibility] FishNet health invocation failed: " + ex.GetBaseException().Message)); return false; } } private static MethodInfo? ResolveCmdRespawnLogic() { if (cmdRespawnResolved) { return cmdRespawnLogic; } cmdRespawnResolved = true; cmdRespawnLogic = FindGeneratedMethod(typeof(PlayerManager), "RpcLogic___CmdRespawn_", (MethodInfo method) => method.ReturnType == typeof(void) && method.GetParameters().Length == 0); if (cmdRespawnLogic == null && !respawnUnavailableLogged) { respawnUnavailableLogged = true; Plugin.Logger.LogError((object)"[Compatibility] FishNet respawn method was not found. Custom respawn is disabled."); } return cmdRespawnLogic; } private static MethodInfo? ResolveRemoveHealthLogic() { if (removeHealthResolved) { return removeHealthLogic; } removeHealthResolved = true; removeHealthLogic = FindGeneratedMethod(typeof(PlayerHealth), "RpcLogic___RemoveHealth_", delegate(MethodInfo method) { if (method.ReturnType == typeof(void)) { ParameterInfo[] parameters = method.GetParameters(); if (parameters != null && parameters.Length == 1) { return parameters[0].ParameterType == typeof(float); } } return false; }); if (removeHealthLogic == null && !removeHealthUnavailableLogged) { removeHealthUnavailableLogged = true; Plugin.Logger.LogError((object)"[Compatibility] FishNet health method was not found. Health writes are disabled."); } return removeHealthLogic; } } internal sealed class GameModeHud : MonoBehaviour { private const float RefreshInterval = 0.25f; private const float AnnouncementDuration = 2f; private const float AnnouncementVerticalOffset = 260f; private const float ScorePopupDuration = 1f; private const float ScorePopupVerticalOffset = -70f; private const int MaxDisplayedNameLength = 14; private static GameModeHud? _instance; private GameObject _panel; private TextMeshProUGUI _announcement; private TextMeshProUGUI _scorePopup; private TextMeshProUGUI _scoreboard; private float _nextRefreshTime; private float _announcementUntil; private float _scorePopupUntil; private void Awake() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_0152: 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_017a: Expected O, but got Unknown //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_023c: 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_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Expected O, but got Unknown //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_0311: 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_033b: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0378: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Expected O, but got Unknown //IL_03f0: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Expected O, but got Unknown _instance = this; Canvas obj = ((Component)this).gameObject.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 1000; CanvasScaler obj2 = ((Component)this).gameObject.AddComponent(); obj2.uiScaleMode = (ScaleMode)1; obj2.referenceResolution = new Vector2(1920f, 1080f); GameObject val = new GameObject("GameModeAnnouncement"); val.transform.SetParent(((Component)this).transform, false); RectTransform obj3 = val.AddComponent(); obj3.anchorMin = new Vector2(0.5f, 0.5f); obj3.anchorMax = new Vector2(0.5f, 0.5f); obj3.pivot = new Vector2(0.5f, 0.5f); obj3.sizeDelta = new Vector2(1000f, 140f); obj3.anchoredPosition = new Vector2(0f, 260f); _announcement = val.AddComponent(); ((TMP_Text)_announcement).fontSize = 64f; ((TMP_Text)_announcement).fontStyle = (FontStyles)1; ((TMP_Text)_announcement).richText = true; ((TMP_Text)_announcement).alignment = (TextAlignmentOptions)514; ((TMP_Text)_announcement).enableWordWrapping = false; ((TMP_Text)_announcement).outlineWidth = 0.2f; ((TMP_Text)_announcement).outlineColor = Color32.op_Implicit(new Color(0f, 0f, 0f, 0.9f)); ((Graphic)_announcement).raycastTarget = false; val.SetActive(false); GameObject val2 = new GameObject("GameModeScorePopup"); val2.transform.SetParent(((Component)this).transform, false); RectTransform obj4 = val2.AddComponent(); obj4.anchorMin = new Vector2(0.5f, 0.5f); obj4.anchorMax = new Vector2(0.5f, 0.5f); obj4.pivot = new Vector2(0.5f, 0.5f); obj4.sizeDelta = new Vector2(400f, 80f); obj4.anchoredPosition = new Vector2(0f, -70f); _scorePopup = val2.AddComponent(); ((TMP_Text)_scorePopup).fontSize = 44f; ((TMP_Text)_scorePopup).fontStyle = (FontStyles)1; ((Graphic)_scorePopup).color = Color32.op_Implicit(new Color32((byte)130, byte.MaxValue, (byte)150, byte.MaxValue)); ((TMP_Text)_scorePopup).richText = true; ((TMP_Text)_scorePopup).alignment = (TextAlignmentOptions)514; ((TMP_Text)_scorePopup).enableWordWrapping = false; ((TMP_Text)_scorePopup).outlineWidth = 0.2f; ((TMP_Text)_scorePopup).outlineColor = Color32.op_Implicit(new Color(0f, 0f, 0f, 0.9f)); ((Graphic)_scorePopup).raycastTarget = false; val2.SetActive(false); _panel = new GameObject("GameModePanel"); _panel.transform.SetParent(((Component)this).transform, false); RectTransform obj5 = _panel.AddComponent(); obj5.anchorMin = new Vector2(0f, 1f); obj5.anchorMax = new Vector2(0f, 1f); obj5.pivot = new Vector2(0f, 1f); obj5.sizeDelta = new Vector2(420f, 0f); obj5.anchoredPosition = new Vector2(18f, -120f); ((Graphic)_panel.AddComponent()).color = new Color(0f, 0f, 0f, 0.45f); VerticalLayoutGroup obj6 = _panel.AddComponent(); ((LayoutGroup)obj6).padding = new RectOffset(12, 12, 8, 8); ((LayoutGroup)obj6).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)obj6).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj6).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj6).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)obj6).childForceExpandHeight = false; ContentSizeFitter obj7 = _panel.AddComponent(); obj7.horizontalFit = (FitMode)2; obj7.verticalFit = (FitMode)2; GameObject val3 = new GameObject("GameModeScoreboard", new Type[1] { typeof(RectTransform) }); val3.transform.SetParent(_panel.transform, false); _scoreboard = val3.AddComponent(); ((TMP_Text)_scoreboard).fontSize = 22f; ((TMP_Text)_scoreboard).richText = true; ((TMP_Text)_scoreboard).enableWordWrapping = false; ((TMP_Text)_scoreboard).alignment = (TextAlignmentOptions)257; ((Graphic)_scoreboard).raycastTarget = false; _panel.SetActive(false); } private void Update() { if (((Component)_scorePopup).gameObject.activeSelf && Time.unscaledTime >= _scorePopupUntil) { ((Component)_scorePopup).gameObject.SetActive(false); } if (GameModeManager.IsMatchOver) { ((Component)_announcement).gameObject.SetActive(false); _panel.SetActive(false); return; } if (((Component)_announcement).gameObject.activeSelf && Time.unscaledTime >= _announcementUntil) { ((Component)_announcement).gameObject.SetActive(false); } if (!(Time.unscaledTime < _nextRefreshTime)) { _nextRefreshTime = Time.unscaledTime + 0.25f; bool flag = GameModeManager.IsCustomMode && !GameModeManager.ShouldHideCustomHud && !GameModeManager.IsMatchOver && (Object)(object)PauseManager.Instance != (Object)null && !PauseManager.Instance.inMainMenu && !PauseManager.Instance.inVictoryMenu && PlayerLookup.GetConnectedPlayerIds().Count > 0; _panel.SetActive(flag); if (flag) { RefreshScoreboard(); } } } internal static void AnnounceActiveMode() { if (!((Object)(object)_instance == (Object)null) && GameModeManager.IsCustomMode && !GameModeManager.IsMatchOver) { ((TMP_Text)_instance._announcement).text = GameModeManager.GetModeLabelMarkup(GameModeManager.ActiveMode); _instance._announcementUntil = Time.unscaledTime + 2f; ((Component)_instance._announcement).gameObject.SetActive(true); } } internal static void AnnounceTarget(string text) { AnnounceTarget(text, 2f); } internal static void AnnounceTarget(string text, float durationSeconds) { if (!((Object)(object)_instance == (Object)null) && !GameModeManager.IsMatchOver) { ((TMP_Text)_instance._announcement).text = text; _instance._announcementUntil = Time.unscaledTime + Mathf.Max(0f, durationSeconds); ((Component)_instance._announcement).gameObject.SetActive(true); } } internal static void ShowScorePopupForPlayer(int playerId, int amount) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (playerId >= 0 && amount > 0 && MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ClientInstance value; if ((Object)(object)ClientInstance.Instance != (Object)null && ClientInstance.Instance.PlayerId == playerId) { ShowScorePopup(amount); } else if (ClientInstance.playerInstances.TryGetValue(playerId, out value) && (Object)(object)value != (Object)null && Object.op_Implicit((Object)(object)value) && value.PlayerSteamID != 0L) { MyceliumNetwork.RPCTarget(1618033988u, "SyncScorePopup", new CSteamID(value.PlayerSteamID), (ReliableType)1, new object[1] { amount }); } } } internal static void ShowScorePopup(int amount) { if (!((Object)(object)_instance == (Object)null) && amount > 0) { ((TMP_Text)_instance._scorePopup).text = "+" + amount; _instance._scorePopupUntil = Time.unscaledTime + 1f; ((Component)_instance._scorePopup).gameObject.SetActive(true); } } private void RefreshScoreboard() { if (GameModeManager.IsActive(GameMode.MichaelMeyers)) { ((TMP_Text)_scoreboard).text = GameModeManager.GetModeLabelMarkup(GameModeManager.ActiveMode) + " Survivors: " + MichaelMeyersState.SurvivorCount; return; } if (GameModeManager.IsActive(GameMode.OneInTheChamber)) { ((TMP_Text)_scoreboard).text = GameModeManager.GetModeLabelMarkup(GameModeManager.ActiveMode) + " " + OneInTheChamberState.AliveCount + " players alive"; return; } string value; Dictionary scores; bool crownFirst; if (GameModeManager.IsActive(GameMode.FreeForAll)) { value = GameModeManager.GetModeLabelMarkup(GameModeManager.ActiveMode) + " " + FFAState.KillsToWin + " points to win"; scores = FFAState.Kills; crownFirst = false; } else if (GameModeManager.IsActive(GameMode.Juggernaut)) { value = GameModeManager.GetModeLabelMarkup(GameModeManager.ActiveMode) + " " + JuggernautState.PointsToWin + " points to win"; scores = JuggernautState.Points; crownFirst = true; } else if (GameModeManager.IsActive(GameMode.SniperBattle)) { value = GameModeManager.GetModeLabelMarkup(GameModeManager.ActiveMode) + " " + SniperBattleState.PointsToWin + " points to win"; scores = SniperBattleState.Points; crownFirst = false; } else if (GameModeManager.IsActive(GameMode.KillTheRat)) { value = GameModeManager.GetModeLabelMarkup(GameModeManager.ActiveMode) + " " + KillTheRatState.PointsToWin + " points to win"; scores = KillTheRatState.Points; crownFirst = false; } else if (GameModeManager.IsActive(GameMode.HotPotato)) { value = GameModeManager.GetModeLabelMarkup(GameModeManager.ActiveMode) + " " + HotPotatoState.KillsToWin + " points to win"; scores = HotPotatoState.Kills; crownFirst = false; } else if (GameModeManager.IsActive(GameMode.Infidel)) { value = GameModeManager.GetModeLabelMarkup(GameModeManager.ActiveMode) + " " + InfidelState.KillsToWin + " points to win"; scores = InfidelState.Scores; crownFirst = false; } else { value = GameModeManager.GetModeLabelMarkup(GameModeManager.ActiveMode) + " " + GunGameState.ScoreLimit + " points to win"; scores = GunGameState.Progress; crownFirst = false; } List connectedPlayerIds = PlayerLookup.GetConnectedPlayerIds(); connectedPlayerIds.Sort(delegate(int left, int right) { bool flag2 = crownFirst && left == JuggernautState.CurrentJuggernautPlayerId; bool flag3 = crownFirst && right == JuggernautState.CurrentJuggernautPlayerId; if (flag2 != flag3) { if (!flag2) { return 1; } return -1; } scores.TryGetValue(left, out var value3); scores.TryGetValue(right, out var value4); return value4.CompareTo(value3); }); StringBuilder stringBuilder = new StringBuilder(value); foreach (int item in connectedPlayerIds) { scores.TryGetValue(item, out var value2); string text = ClientInstance.ReplaceAllPlayerNameTags(PlayerLookup.GetPlayerNameTag(item)); if (text.Length > 14) { text = text.Substring(0, 14); } bool flag = crownFirst && item == JuggernautState.CurrentJuggernautPlayerId; stringBuilder.Append('\n').Append(flag ? "" : "").Append(text) .Append(" ") .Append(value2); if (flag) { stringBuilder.Append(" JUG"); } stringBuilder.Append(""); } ((TMP_Text)_scoreboard).text = stringBuilder.ToString(); } } internal enum GameMode { None, FreeForAll, Juggernaut, GunGame, SniperBattle, Default, MichaelMeyers, KillTheRat, OneInTheChamber, HotPotato, Infidel } internal enum GameModePhase { Inactive, Lobby, ActiveRound, EndingRound } [Flags] internal enum GameModeCapabilities { None = 0, CustomRound = 1, IgnoreGlobalWeapons = 2, IgnoreGlobalHealth = 4, HideHud = 8, ClearOutlines = 0x10 } internal static class GameModeManager { private sealed class ModeDescriptor { internal readonly string Label; internal readonly Color Color; internal readonly Func IsEnabled; internal readonly Action Reset; internal readonly GameModeCapabilities Capabilities; internal readonly Action PeriodicSettingsPush; internal readonly Action PeriodicPush; internal readonly Action EnsureLoadouts; internal ModeDescriptor(string label, Color color, Func isEnabled, Action reset, GameModeCapabilities capabilities, Action? periodicPush = null, Action? ensureLoadouts = null, Action? periodicSettingsPush = null) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) Label = label; Color = color; IsEnabled = isEnabled; Reset = reset; Capabilities = capabilities; PeriodicSettingsPush = periodicSettingsPush ?? periodicPush ?? new Action(Noop); PeriodicPush = periodicPush ?? new Action(Noop); EnsureLoadouts = ensureLoadouts ?? new Action(Noop); } } internal const uint ModId = 1618033988u; private static readonly GameMode[] ModeOrder = new GameMode[10] { GameMode.Default, GameMode.FreeForAll, GameMode.Juggernaut, GameMode.GunGame, GameMode.SniperBattle, GameMode.MichaelMeyers, GameMode.KillTheRat, GameMode.OneInTheChamber, GameMode.HotPotato, GameMode.Infidel }; private static readonly Dictionary Modes = new Dictionary { [GameMode.Default] = new ModeDescriptor("DEFAULT", Color32.op_Implicit(new Color32((byte)220, (byte)220, (byte)220, byte.MaxValue)), () => Plugin.DefaultGameModeEnabled.Value, DefaultReset, GameModeCapabilities.IgnoreGlobalWeapons | GameModeCapabilities.IgnoreGlobalHealth), [GameMode.FreeForAll] = new ModeDescriptor("FFA", Color32.op_Implicit(new Color32((byte)85, (byte)204, byte.MaxValue, byte.MaxValue)), () => Plugin.FFAEnabled.Value, FfaReset, GameModeCapabilities.CustomRound, FFAState.PeriodicPushIfHost, null, FFAState.PeriodicPushSettingsIfHost), [GameMode.Juggernaut] = new ModeDescriptor("JUGGERNAUT", Color32.op_Implicit(new Color32(byte.MaxValue, (byte)106, (byte)0, byte.MaxValue)), () => Plugin.JuggernautEnabled.Value, JuggernautReset, GameModeCapabilities.CustomRound, JuggernautState.PeriodicPushIfHost, JuggernautState.EnsureLoadout, JuggernautState.PeriodicPushSettingsIfHost), [GameMode.GunGame] = new ModeDescriptor("GUN GAME", Color32.op_Implicit(new Color32(byte.MaxValue, (byte)221, (byte)85, byte.MaxValue)), () => Plugin.GunGameEnabled.Value, GunGameReset, GameModeCapabilities.CustomRound | GameModeCapabilities.IgnoreGlobalWeapons, GunGameState.PeriodicPushIfHost, GunGameState.EnsureLoadouts, GunGameState.PeriodicPushSettingsIfHost), [GameMode.SniperBattle] = new ModeDescriptor("SNIPER BATTLE", Color32.op_Implicit(new Color32(byte.MaxValue, (byte)96, (byte)128, byte.MaxValue)), () => Plugin.SniperBattleEnabled.Value, SniperBattleReset, GameModeCapabilities.CustomRound | GameModeCapabilities.IgnoreGlobalWeapons | GameModeCapabilities.IgnoreGlobalHealth | GameModeCapabilities.ClearOutlines, SniperBattleState.PeriodicPushIfHost, SniperBattleState.EnsureLoadouts, SniperBattleState.PeriodicPushSettingsIfHost), [GameMode.MichaelMeyers] = new ModeDescriptor("MICHAEL MEYERS", Color32.op_Implicit(new Color32((byte)204, (byte)34, (byte)34, byte.MaxValue)), () => Plugin.MichaelMeyersEnabled.Value, MichaelMeyersReset, GameModeCapabilities.CustomRound | GameModeCapabilities.IgnoreGlobalWeapons | GameModeCapabilities.HideHud | GameModeCapabilities.ClearOutlines, MichaelMeyersPeriodicPush, MichaelMeyersState.EnsureLoadouts, MichaelMeyersState.PeriodicPushSettingsIfHost), [GameMode.KillTheRat] = new ModeDescriptor("KILL THE RAT", Color32.op_Implicit(new Color32((byte)170, (byte)170, (byte)170, byte.MaxValue)), () => Plugin.KillTheRatEnabled.Value, KillTheRatReset, GameModeCapabilities.CustomRound | GameModeCapabilities.IgnoreGlobalWeapons, KillTheRatState.PeriodicPushIfHost, KillTheRatState.EnsureLoadouts, KillTheRatState.PeriodicPushSettingsIfHost), [GameMode.OneInTheChamber] = new ModeDescriptor("ONE IN THE CHAMBER", Color32.op_Implicit(new Color32((byte)180, (byte)180, (byte)180, byte.MaxValue)), () => Plugin.OneInTheChamberEnabled.Value, OneInTheChamberReset, GameModeCapabilities.CustomRound | GameModeCapabilities.IgnoreGlobalWeapons | GameModeCapabilities.IgnoreGlobalHealth, OneInTheChamberState.PeriodicPushIfHost, OneInTheChamberState.EnsureLoadouts, OneInTheChamberState.PeriodicPushSettingsIfHost), [GameMode.HotPotato] = new ModeDescriptor("HOT POTATO", Color32.op_Implicit(new Color32(byte.MaxValue, (byte)170, (byte)70, byte.MaxValue)), () => Plugin.HotPotatoEnabled.Value, HotPotatoReset, GameModeCapabilities.CustomRound | GameModeCapabilities.IgnoreGlobalWeapons, HotPotatoState.PeriodicPushIfHost, HotPotatoState.EnsureLoadouts, HotPotatoState.PeriodicPushSettingsIfHost), [GameMode.Infidel] = new ModeDescriptor("INFIDEL", Color32.op_Implicit(new Color32((byte)204, (byte)64, (byte)64, byte.MaxValue)), () => Plugin.InfidelEnabled.Value, InfidelReset, GameModeCapabilities.CustomRound | GameModeCapabilities.IgnoreGlobalWeapons | GameModeCapabilities.IgnoreGlobalHealth, InfidelState.PeriodicPushIfHost, InfidelState.EnsureLoadouts, InfidelState.PeriodicPushSettingsIfHost) }; internal static ConfigEntry RespawnDelaySeconds = null; internal static ConfigEntry PointsToWin = null; private static readonly ModeSyncState Sync = new ModeSyncState(); private static readonly HashSet PendingDeaths = new HashSet(); private static bool _customRoundTransitionPending; internal static GameMode ActiveMode { get; private set; } internal static GameModePhase Phase { get; private set; } = GameModePhase.Inactive; internal static int RoundId { get; private set; } internal static float EffectiveRespawnDelaySeconds { get; set; } = 3f; internal static int EffectivePointsToWin { get; private set; } = 100; internal static bool ShouldIgnoreGlobalWeaponSettings => HasCapability(GameModeCapabilities.IgnoreGlobalWeapons); internal static bool ShouldIgnoreGlobalHealthSettings => HasCapability(GameModeCapabilities.IgnoreGlobalHealth); internal static bool IsCustomMode => HasCapability(GameModeCapabilities.CustomRound); internal static bool ShouldHideCustomHud => HasCapability(GameModeCapabilities.HideHud); internal static bool ShouldClearPlayerOutlines => HasCapability(GameModeCapabilities.ClearOutlines); internal static bool IsMatchOver { get { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)PauseManager.Instance != (Object)null) || !PauseManager.Instance.inVictoryMenu) { Scene activeScene = SceneManager.GetActiveScene(); if (!(((Scene)(ref activeScene)).name == "VictoryScene")) { activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).name == "EndGame"; } } return true; } } private static void DefaultReset() { } private static void Noop() { } private static void MichaelMeyersPeriodicPush() { MichaelMeyersState.PeriodicPushSettingsIfHost(); MichaelMeyersState.PeriodicPushLiveStateIfHost(); } private static void FfaReset() { FFAState.ResetMatchState(); } private static void JuggernautReset() { JuggernautState.ResetMatchState(); } private static void GunGameReset() { GunGameState.ResetMatchState(); } private static void SniperBattleReset() { SniperBattleState.ResetMatchState(); } private static void MichaelMeyersReset() { MichaelMeyersState.ResetMatchState(); } private static void KillTheRatReset() { KillTheRatState.ResetMatchState(); } private static void OneInTheChamberReset() { OneInTheChamberState.ResetMatchState(); } private static void HotPotatoReset() { HotPotatoState.ResetMatchState(); } private static void InfidelReset() { InfidelState.ResetMatchState(); } internal static void Initialize() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown RespawnDelaySeconds = ((BaseUnityPlugin)Plugin.Instance).Config.Bind("Global Settings", "Respawn Delay (seconds)", 3f, new ConfigDescription("Host-controlled: how long a killed player waits before respawning.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty())); RespawnDelaySeconds.SettingChanged += delegate { OnGlobalSettingsChanged(); }; PointsToWin = ((BaseUnityPlugin)Plugin.Instance).Config.Bind("Global Settings", "Points To Win", 100, "Fixed score limit for all point-based game modes."); PointsToWin.Value = 100; PointsToWin.SettingChanged += delegate { OnGlobalSettingsChanged(); }; MyceliumNetwork.RegisterNetworkObject((object)Plugin.Instance, 1618033988u, 0); MyceliumNetwork.LobbyCreated += OnLobbyEntered; MyceliumNetwork.LobbyEntered += OnLobbyEntered; MyceliumNetwork.LobbyLeft += OnLobbyLeft; MyceliumNetwork.PlayerEntered += OnPlayerEntered; } private static void OnGlobalSettingsChanged() { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplyGlobalSettingsFromHostConfig(); BroadcastGlobalSettings(); } } private static void ApplyGlobalSettingsFromHostConfig() { ApplyGlobalSettings(RespawnDelaySeconds.Value, PointsToWin.Value); } internal static void ApplyGlobalSettings(float respawnDelaySeconds, int pointsToWin) { EffectiveRespawnDelaySeconds = Mathf.Clamp(respawnDelaySeconds, 0f, 10f); if (EffectivePointsToWin != pointsToWin) { EffectivePointsToWin = pointsToWin; ResetPointModeStates(); } } private static void ResetPointModeStates() { FFAState.ResetMatchState(); JuggernautState.ResetMatchState(); GunGameState.ResetMatchState(); SniperBattleState.ResetMatchState(); } private static void BroadcastGlobalSettings() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) MyceliumNetwork.RPC(1618033988u, "SyncGlobalSettings", (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, RoundId, Sync.NextSettingsRevision(), EffectiveRespawnDelaySeconds, EffectivePointsToWin }); } internal static void OnSettingsChanged() { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost && !IsMatchOver) { EnsureActiveMode(); } } internal static void PeriodicPushIfHost() { if (!Sync.IsSettingsPushDue()) { return; } ApplyGlobalSettingsFromHostConfig(); BroadcastGlobalSettings(); BroadcastActiveMode(); foreach (ModeDescriptor value in Modes.Values) { value.PeriodicSettingsPush(); } } internal static void PeriodicActiveModePushIfHost() { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost && Modes.TryGetValue(ActiveMode, out ModeDescriptor value)) { value.PeriodicPush(); } } internal static void EnsureActiveModeLoadouts() { if (Modes.TryGetValue(ActiveMode, out ModeDescriptor value)) { value.EnsureLoadouts(); } } internal static void CycleForNextMap() { if (MyceliumNetwork.IsHost) { _customRoundTransitionPending = false; ActivateMode(NextEnabledMode(ActiveMode), forceReset: true); } } internal static void HandleSceneChange() { if (MyceliumNetwork.IsHost) { if (IsFinalMatchTransition()) { EndMatch(); } else { CycleForNextMap(); } } } internal static void StartMatch() { if (MyceliumNetwork.IsHost && MyceliumNetwork.InLobby && (ActiveMode == GameMode.None || IsMatchOver)) { ActivateMode(NextEnabledMode(GameMode.None), forceReset: true); } } private static bool IsFinalMatchTransition() { if ((Object)(object)SceneMotor.Instance == (Object)null || (Object)(object)ScoreManager.Instance == (Object)null) { return false; } if (!SceneMotor.Instance.firstToXWins) { return SceneMotor.Instance.sceneIndex == 0; } if (SceneMotor.Instance.roundAmount <= 0) { return false; } foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value != (Object)null && ScoreManager.Instance.GetPoints(ScoreManager.Instance.GetTeamId(value.PlayerId)) >= SceneMotor.Instance.roundAmount) { return true; } } return false; } private static void EndMatch() { ResetMatchState(); ActiveMode = GameMode.None; Phase = GameModePhase.Inactive; RoundId++; if (MyceliumNetwork.InLobby) { BroadcastActiveMode(); } } internal static bool IsActive(GameMode mode) { return ActiveMode == mode; } internal static bool ShouldIgnoreGlobalWeaponSettingsFor(Weapon weapon) { if (!ShouldIgnoreGlobalWeaponSettings) { if (ActiveMode == GameMode.Juggernaut) { return JuggernautState.IsCurrentJuggernautWeapon(weapon); } return false; } return true; } internal static string GetModeLabelMarkup(GameMode mode) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!Modes.TryGetValue(mode, out ModeDescriptor value)) { return "UNKNOWN"; } return "" + value.Label + ""; } internal static void EnsureActiveMode() { if (!IsEnabled(ActiveMode)) { SetActiveMode(NextEnabledMode(ActiveMode)); } } private static void OnLobbyEntered() { Sync.ResetForLobby(); SessionState.BeginLobby(); if (MyceliumNetwork.IsHost) { ApplyGlobalSettingsFromHostConfig(); BroadcastGlobalSettings(); ActivateMode(NextEnabledMode(GameMode.None), forceReset: true); } } private static void OnLobbyLeft() { SessionState.EndLobby(); ResetMatchState(); ActiveMode = GameMode.None; Phase = GameModePhase.Inactive; RoundId++; EffectiveRespawnDelaySeconds = 3f; EffectivePointsToWin = 100; GlobalModifiersState.ResetForLobbyLeft(); HealthSettingsState.ResetForLobbyLeft(); WeaponSettingsState.ResetForLobbyLeft(); WeaponService.ResetPendingRequests(); GameModeRespawn.ResetForLobbyLeft(); } private static void OnPlayerEntered(CSteamID player) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { MyceliumNetwork.RPCTarget(1618033988u, "SyncGlobalSettings", player, (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, RoundId, Sync.SettingsRevision, EffectiveRespawnDelaySeconds, EffectivePointsToWin }); MyceliumNetwork.RPCTarget(1618033988u, "SyncActiveGameMode", player, (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, (int)ActiveMode, RoundId, (int)Phase, Sync.LiveRevision }); } } private static GameMode NextEnabledMode(GameMode current) { if (!ModeCycle.TrySelectRandom(GetConfiguredModes(), current, Random.Range(0, int.MaxValue), out var next)) { return GameMode.None; } return next; } private static List GetConfiguredModes() { List list = new List(); GameMode[] modeOrder = ModeOrder; foreach (GameMode gameMode in modeOrder) { if (IsEnabled(gameMode) && !list.Contains(gameMode)) { list.Add(gameMode); } } return list; } private static bool IsEnabled(GameMode mode) { if (Modes.TryGetValue(mode, out ModeDescriptor value) && value.IsEnabled()) { return HarmonyPatchStatus.IsModeAvailable(mode); } return false; } private static void SetActiveMode(GameMode mode) { ActivateMode(mode, forceReset: false); } private static void ActivateMode(GameMode mode, bool forceReset) { if (forceReset || ActiveMode != mode) { ResetMatchState(); ActiveMode = mode; Phase = (MyceliumNetwork.InLobby ? GameModePhase.Lobby : GameModePhase.Inactive); RoundId++; if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { BroadcastActiveMode(); } } } internal static bool TryAcceptGlobalSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } internal static bool TryAcceptActiveModeSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptLiveSnapshot(hostId, roundId, revision); } internal static void ApplyActiveMode(int mode, int roundId, int phase) { if (!Enum.IsDefined(typeof(GameMode), mode) || !Enum.IsDefined(typeof(GameModePhase), phase) || roundId < RoundId) { return; } if (!HarmonyPatchStatus.IsModeAvailable((GameMode)mode)) { ResetMatchState(); ActiveMode = GameMode.None; Phase = GameModePhase.Inactive; RoundId = roundId; return; } bool flag = roundId > RoundId; bool num = ActiveMode != (GameMode)mode || flag; bool flag2 = Phase != (GameModePhase)phase; if (num) { ResetMatchState(); ActiveMode = (GameMode)mode; } RoundId = roundId; Phase = (GameModePhase)phase; if (num || flag2) { Plugin.Logger.LogInfo((object)$"[GameMode] Applied host state: mode={(GameMode)mode} round={roundId} phase={(GameModePhase)phase}"); } } internal static void ResetMatchState() { _customRoundTransitionPending = false; PendingDeaths.Clear(); foreach (ModeDescriptor value in Modes.Values) { value.Reset(); } } internal static void ResetGameState() { ResetMatchState(); if (MyceliumNetwork.IsHost) { RoundId++; Phase = ((ActiveMode != GameMode.None && MyceliumNetwork.InLobby) ? GameModePhase.Lobby : GameModePhase.Inactive); } else { Phase = ((ActiveMode != GameMode.None) ? GameModePhase.Lobby : GameModePhase.Inactive); } } internal static void BeginRound() { if (ActiveMode != GameMode.None) { Phase = GameModePhase.ActiveRound; if (MyceliumNetwork.IsHost) { RoundId++; BroadcastActiveMode(); } } } private static bool HasCapability(GameModeCapabilities capability) { if (Modes.TryGetValue(ActiveMode, out ModeDescriptor value)) { return value.Capabilities.HasFlag(capability); } return false; } private static void BroadcastActiveMode() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) MyceliumNetwork.RPC(1618033988u, "SyncActiveGameMode", (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, (int)ActiveMode, RoundId, (int)Phase, Sync.NextLiveRevision() }); } internal static void CompleteCustomRound(int winningTeamId) { if (MyceliumNetwork.IsHost && !_customRoundTransitionPending && !((Object)(object)RoundManager.Instance == (Object)null) && !((Object)(object)ScoreManager.Instance == (Object)null) && !((Object)(object)SceneMotor.Instance == (Object)null) && !((Object)(object)Plugin.Instance == (Object)null)) { _customRoundTransitionPending = true; Phase = GameModePhase.EndingRound; BroadcastActiveMode(); int roundId = RoundId; ScoreManager.Instance.ResetRound(); ScoreManager.Instance.AddPoints(winningTeamId, 1); RoundManager.Instance.CmdEndRound(winningTeamId); ((MonoBehaviour)Plugin.Instance).StartCoroutine(AdvanceAfterCustomRound(roundId)); } } private static IEnumerator AdvanceAfterCustomRound(int roundId) { int sessionGeneration = SessionState.Generation; yield return (object)new WaitForSeconds(4f); if (SessionState.IsCurrent(sessionGeneration) && roundId == RoundId && Phase == GameModePhase.EndingRound && (Object)(object)SceneMotor.Instance != (Object)null) { SceneMotor.Instance.ChangeNetworkScene(); } } internal static bool HandleServerDeath(int playerId) { GameMode activeMode = ActiveMode; if (activeMode == GameMode.None || (Object)(object)Plugin.Instance == (Object)null) { return false; } if (activeMode != GameMode.MichaelMeyers && activeMode != GameMode.OneInTheChamber && !FishNetCompatibility.CanRespawn) { Plugin.Logger.LogWarning((object)$"[GameMode] Custom death handling disabled for {activeMode}: FishNet respawn API is unavailable."); return false; } if (PendingDeaths.Add(playerId)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(ProcessServerDeath(playerId, activeMode, RoundId, SessionState.Generation)); } return true; } private static IEnumerator ProcessServerDeath(int playerId, GameMode mode, int roundId, int sessionGeneration) { yield return null; PendingDeaths.Remove(playerId); if (SessionState.IsCurrent(sessionGeneration) && ActiveMode == mode && RoundId == roundId && Phase != GameModePhase.EndingRound) { int num = PlayerLookup.FindKillerId(PlayerLookup.FindPlayerHealthById(playerId)); Plugin.Logger.LogInfo((object)$"[GameMode] Server death: mode={mode} deadPlayer={playerId} killer={num}"); switch (mode) { case GameMode.FreeForAll: FFAState.OnServerKill(playerId, num); GameModeRespawn.Schedule(playerId, EffectiveRespawnDelaySeconds); break; case GameMode.Juggernaut: JuggernautState.OnServerKill(playerId, num); GameModeRespawn.Schedule(playerId, EffectiveRespawnDelaySeconds); break; case GameMode.GunGame: GunGameState.OnServerKill(playerId, num); GameModeRespawn.Schedule(playerId, EffectiveRespawnDelaySeconds); break; case GameMode.SniperBattle: SniperBattleState.OnServerKill(playerId, num); GameModeRespawn.Schedule(playerId, EffectiveRespawnDelaySeconds); break; case GameMode.MichaelMeyers: MichaelMeyersState.OnServerKill(playerId, num); break; case GameMode.KillTheRat: KillTheRatState.OnServerKill(playerId, num); GameModeRespawn.Schedule(playerId, EffectiveRespawnDelaySeconds); break; case GameMode.OneInTheChamber: OneInTheChamberState.OnServerKill(playerId, num); break; case GameMode.HotPotato: HotPotatoState.OnServerKill(playerId, num); GameModeRespawn.Schedule(playerId, EffectiveRespawnDelaySeconds); break; case GameMode.Infidel: InfidelState.OnServerKill(playerId, num); break; } } } } [HarmonyPatch] internal static class GameManager_GameModeDeath_Patch { private static MethodBase? TargetMethod() { return FishNetCompatibility.FindGeneratedMethod(typeof(GameManager), "RpcLogic___PlayerDied_", delegate(MethodInfo method) { if (method.ReturnType == typeof(void)) { ParameterInfo[] parameters = method.GetParameters(); if (parameters != null && parameters.Length == 1) { return parameters[0].ParameterType == typeof(int); } } return false; }); } private static bool Prepare() { return TargetMethod() != null; } private static bool Prefix(GameManager __instance, int playerId) { if (!((NetworkBehaviour)__instance).IsServer || !GameModeManager.IsCustomMode) { return true; } return !GameModeManager.HandleServerDeath(playerId); } } [HarmonyPatch(typeof(GameManager), "ResetGame")] internal static class GameManager_GameModeReset_Patch { private static void Postfix() { GameModeManager.ResetGameState(); PlayerOutline.ResetState(); JuggernautOutline.ResetState(); MichaelMeyersOutline.ResetState(); KillTheRatOutline.ResetState(); } } [HarmonyPatch(typeof(SceneMotor), "ChangeNetworkScene")] internal static class SceneMotor_GameModeCycle_Patch { private static void Prefix() { GameModeManager.HandleSceneChange(); } } [HarmonyPatch(typeof(GameManager), "StartGame")] internal static class GameManager_GameModeStart_Patch { private static void Postfix() { GameModeManager.StartMatch(); } } internal static class GameModeRespawn { private const float MinimumPlayerSeparation = 1.25f; private static readonly Vector3[] SpawnOffsets = (Vector3[])(object)new Vector3[13] { Vector3.zero, new Vector3(1f, 0f, 0f), new Vector3(-1f, 0f, 0f), new Vector3(0f, 0f, 1f), new Vector3(0f, 0f, -1f), new Vector3(0.7f, 0f, 0.7f), new Vector3(-0.7f, 0f, 0.7f), new Vector3(0.7f, 0f, -0.7f), new Vector3(-0.7f, 0f, -0.7f), new Vector3(1.5f, 0f, 0f), new Vector3(-1.5f, 0f, 0f), new Vector3(0f, 0f, 1.5f), new Vector3(0f, 0f, -1.5f) }; private static readonly HashSet PendingManagers = new HashSet(); private static readonly HashSet SuppressedRoundStarts = new HashSet(); private static readonly HashSet PendingSpawnAdjustments = new HashSet(); internal static bool AnyModeEnabled => GameModeManager.IsCustomMode; internal static void ResetForLobbyLeft() { PendingManagers.Clear(); SuppressedRoundStarts.Clear(); PendingSpawnAdjustments.Clear(); } internal static void Schedule(PlayerManager manager, float delay) { if (!((Object)(object)Plugin.Instance == (Object)null) && PendingManagers.Add(((Object)manager).GetInstanceID())) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(RespawnAfterDelay(manager, delay, 0, SessionState.Generation, GameModeManager.RoundId)); } } internal static void Schedule(int playerId, float delay) { if (!((Object)(object)Plugin.Instance == (Object)null) && PendingManagers.Add(playerId)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(RespawnPlayerAfterDelay(playerId, delay, SessionState.Generation, GameModeManager.RoundId)); } } private static IEnumerator RespawnAfterDelay(PlayerManager manager, float delay, int attempt, int sessionGeneration, int roundId) { int managerId = ((Object)manager).GetInstanceID(); yield return (object)new WaitForSeconds(delay); if (!SessionState.IsCurrent(sessionGeneration) || GameModeManager.RoundId != roundId) { PendingManagers.Remove(managerId); yield break; } bool flag = false; if ((Object)(object)manager != (Object)null) { try { MarkRoundStartSuppressed(manager); flag = FishNetCompatibility.TryInvokeRespawn(manager); if (flag) { FinalizeRespawn(manager); ClearSpawnAdjustment(manager); } } catch (Exception ex) { ClearRoundStartSuppressed(manager); Plugin.Logger.LogWarning((object)("[Respawn] PlayerManager respawn failed: " + ex.GetBaseException().Message)); flag = false; } } PendingManagers.Remove(managerId); if (!flag && attempt < 2 && (Object)(object)Plugin.Instance != (Object)null && (Object)(object)manager != (Object)null && SessionState.IsCurrent(sessionGeneration) && GameModeManager.RoundId == roundId) { PendingManagers.Add(managerId); ((MonoBehaviour)Plugin.Instance).StartCoroutine(RespawnAfterDelay(manager, 0.25f, attempt + 1, sessionGeneration, roundId)); } } private static IEnumerator RespawnPlayerAfterDelay(int playerId, float delay, int sessionGeneration, int roundId) { yield return (object)new WaitForSeconds(delay); for (int attempt = 0; attempt < 3; attempt++) { if (!SessionState.IsCurrent(sessionGeneration) || GameModeManager.RoundId != roundId) { PendingManagers.Remove(playerId); yield break; } PlayerManager val = FindManagerByPlayerId(playerId); if ((Object)(object)val != (Object)null) { try { MarkRoundStartSuppressed(val); if (FishNetCompatibility.TryInvokeRespawn(val)) { FinalizeRespawn(val); ClearSpawnAdjustment(val); PendingManagers.Remove(playerId); yield break; } } catch (Exception ex) { ClearRoundStartSuppressed(val); Plugin.Logger.LogWarning((object)$"[Respawn] player={playerId} attempt={attempt + 1} failed: {ex.GetBaseException().Message}"); } } yield return (object)new WaitForSeconds(0.25f); } PendingManagers.Remove(playerId); Plugin.Logger.LogWarning((object)$"[Respawn] player={playerId} failed after 3 attempts"); } internal static void MarkRoundStartSuppressed(PlayerManager manager) { SuppressedRoundStarts.Add(((Object)manager).GetInstanceID()); PendingSpawnAdjustments.Add(((Object)manager).GetInstanceID()); } internal static void ClearRoundStartSuppressed(PlayerManager manager) { SuppressedRoundStarts.Remove(((Object)manager).GetInstanceID()); ClearSpawnAdjustment(manager); } internal static bool ConsumeRoundStartSuppressed(PlayerManager manager) { return SuppressedRoundStarts.Remove(((Object)manager).GetInstanceID()); } internal static bool ConsumeSpawnAdjustment(PlayerManager manager) { return PendingSpawnAdjustments.Remove(((Object)manager).GetInstanceID()); } private static void ClearSpawnAdjustment(PlayerManager manager) { PendingSpawnAdjustments.Remove(((Object)manager).GetInstanceID()); } private static void FinalizeRespawn(PlayerManager manager) { manager.SetPlayerMove(true); if ((Object)(object)manager.player != (Object)null) { manager.player.sync___set_value_canMove(true, true); manager.player.startOfRound = false; } if ((Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.startRound = false; } if ((Object)(object)GameManager.Instance != (Object)null && (Object)(object)Plugin.Instance != (Object)null) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(KeepPlayerMovable(manager, 4f, SessionState.Generation, GameModeManager.RoundId)); } } private static IEnumerator KeepPlayerMovable(PlayerManager manager, float duration, int sessionGeneration, int roundId) { float endTime = Time.time + duration; while (Time.time < endTime && SessionState.IsCurrent(sessionGeneration) && GameModeManager.RoundId == roundId) { if ((Object)(object)manager != (Object)null) { manager.SetPlayerMove(true); if ((Object)(object)manager.player != (Object)null) { manager.player.sync___set_value_canMove(true, true); manager.player.startOfRound = false; } if ((Object)(object)PauseManager.Instance != (Object)null) { PauseManager.Instance.startRound = false; } } yield return null; } } private static PlayerManager? FindManagerByPlayerId(int playerId) { if (!ClientInstance.playerInstances.TryGetValue(playerId, out var value)) { return null; } return value.PlayerSpawner; } private static int FindPlayerId(PlayerManager manager) { foreach (KeyValuePair playerInstance in ClientInstance.playerInstances) { if ((Object)(object)playerInstance.Value != (Object)null && Object.op_Implicit((Object)(object)playerInstance.Value) && (Object)(object)playerInstance.Value.PlayerSpawner == (Object)(object)manager) { return playerInstance.Key; } } return -1; } internal static Transform ChooseDistantSpawn(Transform currentResult) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (!AnyModeEnabled) { return currentResult; } SpawnPoint[] array = FindFreeForAllSpawnPoints(); Transform val = null; float num = float.MinValue; List activePlayerPositions = GetActivePlayerPositions(); if (activePlayerPositions.Count == 0) { return currentResult; } SpawnPoint[] array2 = array; foreach (SpawnPoint val2 in array2) { if (!((Object)(object)val2 == (Object)null) && ((Component)val2).gameObject.activeInHierarchy) { float nearestPlayerDistance = GetNearestPlayerDistance(((Component)val2).transform.position, activePlayerPositions); if (nearestPlayerDistance > num) { num = nearestPlayerDistance; val = ((Component)val2).transform; } } } return val ?? currentResult; } internal static Vector3 ChooseSpawnPosition(Vector3 currentPosition) { //IL_0014: 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) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) if (!AnyModeEnabled) { return currentPosition; } List activePlayerPositions = GetActivePlayerPositions(); SpawnPoint[] array = FindFreeForAllSpawnPoints(); Vector3 result = currentPosition; float bestDistance = float.MinValue; bool bestCrowded = true; bool flag = false; SpawnPoint[] array2 = array; foreach (SpawnPoint val in array2) { if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy) { continue; } Vector3[] spawnOffsets = SpawnOffsets; foreach (Vector3 val2 in spawnOffsets) { Vector3 val3 = ((Component)val).transform.position + val2; float nearestPlayerDistance = GetNearestPlayerDistance(val3, activePlayerPositions); bool flag2 = nearestPlayerDistance < 1.25f; if (!flag || IsBetterCandidate(flag2, nearestPlayerDistance, bestCrowded, bestDistance)) { result = val3; bestDistance = nearestPlayerDistance; bestCrowded = flag2; flag = true; } } } return result; } private static bool IsBetterCandidate(bool candidateCrowded, float candidateDistance, bool bestCrowded, float bestDistance) { if (candidateCrowded != bestCrowded) { return !candidateCrowded; } return candidateDistance > bestDistance; } private static List GetActivePlayerPositions() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (ClientInstance value in ClientInstance.playerInstances.Values) { PlayerHealth val = (((Object)(object)value == (Object)null) ? null : PlayerLookup.FindPlayerHealthById(value.PlayerId)); if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy && !(val.health <= 0f)) { list.Add(((Component)val).transform.position); } } return list; } private static float GetNearestPlayerDistance(Vector3 position, List playerPositions) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_002c: Unknown result type (might be due to invalid IL or missing references) if (playerPositions.Count == 0) { return float.MaxValue; } float num = float.MaxValue; foreach (Vector3 playerPosition in playerPositions) { Vector3 val = position - playerPosition; val.y = 0f; num = Mathf.Min(num, ((Vector3)(ref val)).magnitude); } return num; } private static SpawnPoint[] FindFreeForAllSpawnPoints() { GameObject val = GameObject.FindGameObjectWithTag("Spawnpoints4Player"); if ((Object)(object)val == (Object)null) { val = GameObject.FindGameObjectWithTag("Spawnpoints"); } if (!((Object)(object)val == (Object)null)) { return val.GetComponentsInChildren(true); } return Object.FindObjectsOfType(); } internal static PlayerManager? FindManager(PlayerHealth health) { return health.playerValues?.playerClient?.PlayerSpawner; } } [HarmonyPatch(typeof(PlayerManager), "ReturnSpawnPoint")] internal static class PlayerManager_DistantSpawn_Patch { private static void Postfix(ref Transform __result) { __result = GameModeRespawn.ChooseDistantSpawn(__result); } } [HarmonyPatch(typeof(PlayerManager), "SpawnPlayer", new Type[] { typeof(int), typeof(int), typeof(Vector3), typeof(Quaternion) })] internal static class PlayerManager_CustomRespawnSpawn_Patch { private static void Prefix(PlayerManager __instance, ref Vector3 position) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (GameModeRespawn.ConsumeSpawnAdjustment(__instance)) { position = GameModeRespawn.ChooseSpawnPosition(position); } } } [HarmonyPatch(typeof(PlayerManager), "WaitForRoundStartCoroutineStart")] internal static class PlayerManager_CustomRespawnRoundStart_Patch { private static bool Prefix(PlayerManager __instance) { return !GameModeRespawn.ConsumeRoundStartSuppressed(__instance); } } internal static class HarmonyPatchStatus { private static readonly Dictionary FailedPatches = new Dictionary(); private static readonly HashSet UnavailableWarnings = new HashSet(); private static readonly Type[] RequiredCustomModePatches = new Type[10] { typeof(GameManager_GameModeDeath_Patch), typeof(GameManager_GameModeReset_Patch), typeof(PauseManager_RoundLifecycle_Patch), typeof(SceneMotor_GameModeCycle_Patch), typeof(GameManager_GameModeStart_Patch), typeof(ItemSpawner_WeaponPolicy_Patch), typeof(PlayerPickup_WeaponPolicy_Patch), typeof(Weapon_AmmoInitialization_Patch), typeof(FirstPersonController_MovementPolicy_Patch), typeof(Weapon_UpdatePolicy_Patch) }; private static readonly Dictionary RequiredModePatches = new Dictionary { [GameMode.Juggernaut] = new Type[3] { typeof(GameManager_JuggernautTick_Patch), typeof(Minigun_JuggernautAmmoDisplay_Patch), typeof(Minigun_JuggernautReload_Patch) }, [GameMode.GunGame] = new Type[1] { typeof(PlayerManager_GunGameSpawn_Patch) }, [GameMode.SniperBattle] = new Type[1] { typeof(PlayerManager_SniperBattleSpawn_Patch) }, [GameMode.KillTheRat] = new Type[2] { typeof(GameManager_KillTheRatTick_Patch), typeof(PlayerManager_KillTheRatSpawn_Patch) }, [GameMode.OneInTheChamber] = new Type[1] { typeof(PlayerManager_OneInTheChamberSpawn_Patch) }, [GameMode.HotPotato] = new Type[2] { typeof(PlayerManager_HotPotatoSpawn_Patch), typeof(MeleeWeapon_HotPotatoBatDamage_Patch) }, [GameMode.Infidel] = new Type[1] { typeof(PlayerManager_InfidelSpawn_Patch) } }; internal static void RecordFailure(Type patchType, string reason) { FailedPatches[patchType] = reason; } internal static bool IsModeAvailable(GameMode mode) { if (mode == GameMode.None || mode == GameMode.Default) { return true; } Type[] requiredCustomModePatches = RequiredCustomModePatches; foreach (Type type in requiredCustomModePatches) { if (FailedPatches.ContainsKey(type)) { WarnUnavailable(mode, type, FailedPatches[type]); return false; } } if (!RequiredModePatches.TryGetValue(mode, out Type[] value)) { return true; } requiredCustomModePatches = value; foreach (Type type2 in requiredCustomModePatches) { if (FailedPatches.TryGetValue(type2, out string value2)) { WarnUnavailable(mode, type2, value2); return false; } } return true; } private static void WarnUnavailable(GameMode mode, Type patchType, string reason) { if (UnavailableWarnings.Add(mode)) { Plugin.Logger.LogError((object)$"[Harmony] Mode {mode} is disabled because patch {patchType.Name} failed: {reason}"); } } } internal static class HostSettingsSync { internal static bool IsDue(ref float nextPushTime, float intervalSeconds = 3f) { if (!MyceliumNetwork.InLobby || !MyceliumNetwork.IsHost) { nextPushTime = 0f; return false; } if (Time.unscaledTime < nextPushTime) { return false; } nextPushTime = Time.unscaledTime + intervalSeconds; return true; } } internal sealed class ModeSyncState { private readonly float _settingsPushInterval; private readonly float _livePushInterval; private float _nextSettingsPushTime; private float _nextLivePushTime; private int _lastSettingsRoundId = -1; private int _lastSettingsRevision = -1; private int _lastLiveRoundId = -1; private int _lastLiveRevision = -1; internal int SettingsRevision { get; private set; } internal int LiveRevision { get; private set; } internal int LastLiveRoundId { get; private set; } = -1; internal ModeSyncState(float settingsPushInterval = 3f, float livePushInterval = 3f) { _settingsPushInterval = settingsPushInterval; _livePushInterval = livePushInterval; } internal int NextSettingsRevision() { return ++SettingsRevision; } internal int NextLiveRevision() { return ++LiveRevision; } internal bool IsSettingsPushDue() { return HostSettingsSync.IsDue(ref _nextSettingsPushTime, _settingsPushInterval); } internal bool IsLivePushDue() { return HostSettingsSync.IsDue(ref _nextLivePushTime, _livePushInterval); } internal bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return SessionState.TryAcceptSettingsSnapshot(hostId, roundId, revision, ref _lastSettingsRoundId, ref _lastSettingsRevision); } internal bool TryAcceptLiveSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) bool num = SessionState.TryAcceptSettingsSnapshot(hostId, roundId, revision, ref _lastLiveRoundId, ref _lastLiveRevision); if (num) { LastLiveRoundId = roundId; } return num; } internal void ResetForLobby() { _lastSettingsRoundId = -1; _lastSettingsRevision = -1; _lastLiveRoundId = -1; _lastLiveRevision = -1; LastLiveRoundId = -1; _nextSettingsPushTime = 0f; _nextLivePushTime = 0f; } internal void ResetLiveState() { NextLiveRevision(); } } internal static class MovementPolicy { private static bool IsJuggernautMinigunFiring(FirstPersonController controller) { GameObject val = controller.playerPickupScript?.objInHand; Weapon val2 = (((Object)(object)val == (Object)null || !Object.op_Implicit((Object)(object)val)) ? null : val.GetComponent()); if ((Object)(object)val2 == (Object)null || !((Object)val2).name.StartsWith("Minigun", StringComparison.Ordinal)) { return false; } InputAction val3 = (val2.invertFire ? val2.fire2 : val2.fire1); if (val3 != null) { return val3.ReadValue() > 0.1f; } return false; } internal static bool CanSlide(FirstPersonController controller) { if (!GameModeManager.IsActive(GameMode.MichaelMeyers) && !JuggernautState.IsCurrentJuggernaut(controller)) { return GlobalModifiersState.SlidingEnabled; } return false; } internal static bool CanRunJump(FirstPersonController controller) { if (JuggernautState.IsCurrentJuggernaut(controller)) { return false; } if (GameModeManager.IsActive(GameMode.MichaelMeyers) || GameModeManager.IsActive(GameMode.Infidel)) { controller.CanWallJump = false; } return true; } internal static void Apply(FirstPersonController controller) { switch (GameModeManager.ActiveMode) { case GameMode.Juggernaut: if (JuggernautState.IsCurrentJuggernaut(controller)) { controller.movementFactor = (IsJuggernautMinigunFiring(controller) ? 0.5f : 1f); } break; case GameMode.KillTheRat: if (KillTheRatState.IsRat(controller)) { controller.movementFactor = 2f; } break; case GameMode.MichaelMeyers: controller.movementFactor = (MichaelMeyersState.IsMichael(controller) ? 1.05f : 1f); break; case GameMode.Infidel: controller.movementFactor = 0.7f; controller.CanWallJump = false; break; } } } [HarmonyPatch(typeof(FirstPersonController), "Slide")] internal static class FirstPersonController_SlidePolicy_Patch { private static bool Prefix(FirstPersonController __instance) { return MovementPolicy.CanSlide(__instance); } } [HarmonyPatch(typeof(FirstPersonController), "HandleSlide")] internal static class FirstPersonController_HandleSlidePolicy_Patch { private static bool Prefix() { return !GameModeManager.IsActive(GameMode.MichaelMeyers); } } [HarmonyPatch(typeof(FirstPersonController), "Jump")] [HarmonyPriority(0)] internal static class FirstPersonController_JumpPolicy_Patch { private static bool Prefix(FirstPersonController __instance) { return MovementPolicy.CanRunJump(__instance); } } [HarmonyPatch(typeof(FirstPersonController), "OnControllerColliderHit")] internal static class FirstPersonController_WallJumpPolicy_Patch { private static void Postfix(FirstPersonController __instance) { if (!GlobalModifiersState.WallJumpEnabled || GameModeManager.IsActive(GameMode.MichaelMeyers) || GameModeManager.IsActive(GameMode.Infidel)) { __instance.CanWallJump = false; } } } [HarmonyPatch(typeof(FirstPersonController), "Update")] [HarmonyPriority(0)] internal static class FirstPersonController_MovementPolicy_Patch { private static void Prefix(FirstPersonController __instance) { if (GameModeManager.IsActive(GameMode.MichaelMeyers)) { __instance.CanWallJump = false; __instance.isSliding = false; __instance.isCrouching = false; } } private static void Postfix(FirstPersonController __instance) { MovementPolicy.Apply(__instance); } } internal static class PlayerLookup { internal static List GetConnectedPlayerIds() { try { HashSet hashSet = new HashSet(); foreach (KeyValuePair playerInstance in ClientInstance.playerInstances) { if ((Object)(object)playerInstance.Value != (Object)null && Object.op_Implicit((Object)(object)playerInstance.Value)) { hashSet.Add(playerInstance.Key); } } ClientInstance[] array = Object.FindObjectsOfType(); foreach (ClientInstance val in array) { if ((Object)(object)val != (Object)null && Object.op_Implicit((Object)(object)val) && val.PlayerId >= 0) { hashSet.Add(val.PlayerId); } } List list = hashSet.ToList(); list.Sort(); return list; } catch { return new List(); } } internal static PlayerHealth? FindPlayerHealthById(int playerId) { if (playerId < 0) { return null; } if (ClientInstance.playerInstances.TryGetValue(playerId, out var value) && (Object)(object)value != (Object)null) { if ((Object)(object)value.PlayerSpawner != (Object)null && (Object)(object)value.PlayerSpawner.player != (Object)null) { PlayerHealth component = ((Component)value.PlayerSpawner.player).GetComponent(); if (IsPlayerHealthForId(component, playerId)) { return component; } if (IsMappedLivePlayerHealth(component, value)) { return component; } } PlayerHealth component2 = ((Component)value).GetComponent(); if (IsPlayerHealthForId(component2, playerId)) { return component2; } } PlayerHealth[] array = Object.FindObjectsOfType(true); foreach (PlayerHealth val in array) { if (IsPlayerHealthForId(val, playerId)) { return val; } } ClientInstance[] array2 = Object.FindObjectsOfType(); foreach (ClientInstance val2 in array2) { if ((Object)(object)val2 == (Object)null || !Object.op_Implicit((Object)(object)val2) || val2.PlayerId != playerId) { continue; } PlayerManager playerSpawner = val2.PlayerSpawner; if ((Object)(object)playerSpawner != (Object)null && (Object)(object)playerSpawner.player != (Object)null) { PlayerHealth component3 = ((Component)playerSpawner.player).GetComponent(); if (IsPlayerHealthForId(component3, playerId)) { return component3; } if (IsMappedLivePlayerHealth(component3, val2)) { return component3; } } PlayerHealth component4 = ((Component)val2).GetComponent(); if (IsPlayerHealthForId(component4, playerId)) { return component4; } } return null; } private static bool IsPlayerHealthForId(PlayerHealth? health, int playerId) { if ((Object)(object)health != (Object)null) { PlayerValues playerValues = health.playerValues; if (playerValues == null) { return false; } return playerValues.playerClient?.PlayerId == playerId; } return false; } private static bool IsMappedLivePlayerHealth(PlayerHealth? health, ClientInstance client) { if ((Object)(object)health != (Object)null && Object.op_Implicit((Object)(object)health) && ((Component)health).gameObject.activeInHierarchy && (Object)(object)client.PlayerSpawner != (Object)null && (Object)(object)client.PlayerSpawner.player != (Object)null) { return (Object)(object)((Component)client.PlayerSpawner.player).GetComponent() == (Object)(object)health; } return false; } internal static int FindKillerId(PlayerHealth? deadPlayerHealth) { if ((Object)(object)deadPlayerHealth == (Object)null || (Object)(object)deadPlayerHealth.killer == (Object)null) { return -1; } PlayerHealth componentInParent = ((Component)deadPlayerHealth.killer).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.playerValues != (Object)null && (Object)(object)componentInParent.playerValues.playerClient != (Object)null) { return componentInParent.playerValues.playerClient.PlayerId; } ClientInstance componentInParent2 = ((Component)deadPlayerHealth.killer).GetComponentInParent(); if (!((Object)(object)componentInParent2 != (Object)null)) { return -1; } return componentInParent2.PlayerId; } internal static string GetPlayerNameTag(int playerId) { return $"{{PLAYER_NAME}}:{{{playerId}}}"; } } internal static class PlayerOutline { private static readonly List AppliedRenderers = new List(); private static GameMode _lastMode = GameMode.None; internal static bool HasAppliedRenderers => AppliedRenderers.Count > 0; internal static void ResetState() { ClearApplied(); _lastMode = GameMode.None; } internal static bool UpdateMode(GameMode activeMode) { if (_lastMode == activeMode) { return false; } ClearAll(); _lastMode = activeMode; return true; } internal static void ApplySingleTarget(ref PlayerHealth? current, PlayerHealth? target, Color color) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null || !Object.op_Implicit((Object)(object)target) || !((Component)target).gameObject.activeInHierarchy) { ClearTarget(ref current); return; } if ((Object)(object)current != (Object)(object)target) { ClearApplied(); current = target; } Apply(target, color); } internal static void ClearTarget(ref PlayerHealth? current) { if ((Object)(object)current != (Object)null && Object.op_Implicit((Object)(object)current)) { ClearApplied(); } current = null; } internal static void Apply(PlayerHealth player, Color color) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } SkinnedMeshRenderer[] componentsInChildren = ((Component)player).GetComponentsInChildren(true); foreach (SkinnedMeshRenderer val in componentsInChildren) { if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy) { continue; } Material[] materials = ((Renderer)val).materials; if (materials.Length != 0 && !((Object)(object)materials[0] == (Object)null) && materials[0].HasProperty("_ASEOutlineWidth")) { float num = ((((Object)((Component)val).gameObject).name == "SM_Aboubi_Head00") ? 0.02f : 0.04f); if (!Mathf.Approximately(materials[0].GetFloat("_ASEOutlineWidth"), num) || materials[0].GetColor("_ASEOutlineColor") != color) { materials[0].SetFloat("_ASEOutlineWidth", num); materials[0].SetColor("_ASEOutlineColor", color); ((Renderer)val).materials = materials; } if (!AppliedRenderers.Contains(val)) { AppliedRenderers.Add(val); } } } } internal static void Clear(PlayerHealth player) { if (!((Object)(object)player == (Object)null)) { SkinnedMeshRenderer[] componentsInChildren = ((Component)player).GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { ClearRenderer(((Component)componentsInChildren[i]).gameObject); } } } internal static void ClearAll() { foreach (ClientInstance value in ClientInstance.playerInstances.Values) { if ((Object)(object)value == (Object)null || !Object.op_Implicit((Object)(object)value)) { continue; } PlayerManager playerSpawner = value.PlayerSpawner; if (!((Object)(object)playerSpawner == (Object)null) && Object.op_Implicit((Object)(object)playerSpawner) && !((Object)(object)playerSpawner.player == (Object)null) && Object.op_Implicit((Object)(object)playerSpawner.player)) { PlayerHealth component = ((Component)playerSpawner.player).GetComponent(); if ((Object)(object)component != (Object)null && Object.op_Implicit((Object)(object)component)) { Clear(component); } } } foreach (SkinnedMeshRenderer appliedRenderer in AppliedRenderers) { if ((Object)(object)appliedRenderer != (Object)null) { ClearRenderer(((Component)appliedRenderer).gameObject); } } AppliedRenderers.Clear(); } internal static void ClearApplied() { foreach (SkinnedMeshRenderer appliedRenderer in AppliedRenderers) { if ((Object)(object)appliedRenderer != (Object)null) { ClearRenderer(((Component)appliedRenderer).gameObject); } } AppliedRenderers.Clear(); } private static void ClearRenderer(GameObject meshObject) { if ((Object)(object)meshObject == (Object)null) { return; } SkinnedMeshRenderer component = meshObject.GetComponent(); if (!((Object)(object)component == (Object)null)) { Material[] materials = ((Renderer)component).materials; if (materials.Length != 0 && !((Object)(object)materials[0] == (Object)null) && materials[0].HasProperty("_ASEOutlineWidth")) { materials[0].SetFloat("_ASEOutlineWidth", 0f); ((Renderer)component).materials = materials; } } } } internal static class SnapshotValidation { internal static bool TryAccept(int roundId, int revision, ref int lastRoundId, ref int lastRevision) { if (roundId < 0 || revision < 0 || roundId < lastRoundId || (roundId == lastRoundId && revision < lastRevision)) { return false; } lastRoundId = roundId; lastRevision = revision; return true; } } internal sealed class ModeSyncValidation { private int lastSettingsRoundId = -1; private int lastSettingsRevision = -1; private int lastLiveRoundId = -1; private int lastLiveRevision = -1; internal int SettingsRevision { get; private set; } internal int LiveRevision { get; private set; } internal int NextSettingsRevision() { return ++SettingsRevision; } internal int NextLiveRevision() { return ++LiveRevision; } internal bool TryAcceptSettings(int roundId, int revision) { return SnapshotValidation.TryAccept(roundId, revision, ref lastSettingsRoundId, ref lastSettingsRevision); } internal bool TryAcceptLive(int roundId, int revision) { return SnapshotValidation.TryAccept(roundId, revision, ref lastLiveRoundId, ref lastLiveRevision); } internal void ResetForLobby() { lastSettingsRoundId = -1; lastSettingsRevision = -1; lastLiveRoundId = -1; lastLiveRevision = -1; } } internal static class WeaponListParser { internal static List Parse(string value, IEnumerable validNames) { HashSet valid = new HashSet(validNames, StringComparer.Ordinal); return (from item in (value ?? string.Empty).Split(',', ';') select item.Trim() into item where item.Length > 0 && valid.Contains(item) select item).Distinct(StringComparer.Ordinal).ToList(); } } internal static class ScoreCodec { internal static string Serialize(IReadOnlyDictionary scores) { return string.Join(";", scores.Select((KeyValuePair entry) => $"{entry.Key}:{entry.Value}")); } internal static Dictionary Parse(string data, int maximumScore) { Dictionary dictionary = new Dictionary(); string[] array = (data ?? string.Empty).Split(';'); foreach (string text in array) { int num = text.IndexOf(':'); if (num > 0 && int.TryParse(text.Substring(0, num), out var result) && int.TryParse(text.Substring(num + 1), out var result2) && result >= 0 && result2 >= 0 && result2 <= maximumScore) { dictionary[result] = result2; } } return dictionary; } } internal static class OneInTheChamberRules { internal const int PlayerHealth = 10; internal static bool IsAllowedWeapon(string weaponName) { if (!weaponName.StartsWith("Pistol", StringComparison.Ordinal)) { return weaponName.StartsWith("Couperet", StringComparison.Ordinal); } return true; } internal static bool ApplyDeath(HashSet alivePlayers, Dictionary reserveBullets, int deadPlayerId, int killerId) { if (!alivePlayers.Remove(deadPlayerId)) { return false; } if (killerId >= 0 && killerId != deadPlayerId && alivePlayers.Contains(killerId)) { reserveBullets.TryGetValue(killerId, out var value); reserveBullets[killerId] = value + 1; } return true; } } internal static class HotPotatoRules { internal static bool IsAllowedWeapon(string weaponName, bool hasPotato) { string value = (hasPotato ? "BaseballBat" : "Shotgun"); return weaponName.StartsWith(value, StringComparison.Ordinal); } internal static int ResolvePotato(int potatoPlayerId, int killerId, int deadPlayerId) { if (killerId != potatoPlayerId || killerId == deadPlayerId) { return potatoPlayerId; } return deadPlayerId; } } internal static class InfidelRules { internal const int PointsPerKill = 10; internal static int GetKillerAward(bool deadWasInfidel, bool killerIsInfidel, int aliveTerroristsAfterDeath) { if (deadWasInfidel) { return Math.Max(0, aliveTerroristsAfterDeath) * 10; } if (!killerIsInfidel) { return 0; } return 10; } internal static int GetInfidelBonusAward(bool deadWasInfidel, bool killerIsInfidel) { if (deadWasInfidel || killerIsInfidel) { return 0; } return 10; } } internal static class ModeCycle { internal static bool TrySelectNext(IReadOnlyList modes, T current, out T next) { if (modes.Count == 0) { next = default(T); return false; } int num = -1; EqualityComparer equalityComparer = EqualityComparer.Default; for (int i = 0; i < modes.Count; i++) { if (equalityComparer.Equals(modes[i], current)) { num = i; break; } } next = modes[(num + 1) % modes.Count]; return true; } internal static bool TrySelectRandom(IReadOnlyList modes, T current, int selectionIndex, out T next) { if (modes.Count == 0) { next = default(T); return false; } int num = -1; EqualityComparer equalityComparer = EqualityComparer.Default; for (int i = 0; i < modes.Count; i++) { if (equalityComparer.Equals(modes[i], current)) { num = i; break; } } int num2 = ((num < 0) ? modes.Count : (modes.Count - 1)); if (num2 == 0) { next = modes[0]; return true; } int num3 = (int)((uint)selectionIndex % (uint)num2); if (num >= 0 && num3 >= num) { num3++; } next = modes[num3]; return true; } } internal sealed class RequestVersionTracker { private readonly Dictionary versions = new Dictionary(); internal int Next(int playerId) { int value; int num = ((!versions.TryGetValue(playerId, out value)) ? 1 : (value + 1)); versions[playerId] = num; return num; } internal bool IsCurrent(int playerId, int version) { if (versions.TryGetValue(playerId, out var value)) { return value == version; } return false; } internal void Clear() { versions.Clear(); } } [HarmonyPatch(typeof(PauseManager), "InvokeRoundStarted")] internal static class PauseManager_RoundLifecycle_Patch { private static void Postfix() { GameModeManager.BeginRound(); switch (GameModeManager.ActiveMode) { case GameMode.MichaelMeyers: MichaelMeyersState.OnRoundStarted(); break; case GameMode.OneInTheChamber: OneInTheChamberState.OnRoundStarted(); break; case GameMode.HotPotato: HotPotatoState.OnRoundStarted(); break; case GameMode.Infidel: InfidelState.OnRoundStarted(); break; } if (!GameModeManager.ShouldIgnoreGlobalWeaponSettings) { WeaponAmmoTuning.ScheduleLocalAmmoHudRefresh(); } GameModeHud.AnnounceActiveMode(); } } internal static class ScoreRules { internal const int PointsToWin = 100; internal const int PointsPerKill = 10; internal const int PointsPerJuggernautCrown = 20; internal const int PointsPerRatSurvivalSecond = 1; } internal static class SessionState { private static bool active; internal static int Generation { get; private set; } internal static bool IsActive { get { if (active) { return MyceliumNetwork.InLobby; } return false; } } internal static void BeginLobby() { if (!active || !MyceliumNetwork.InLobby) { active = true; Generation++; } } internal static void EndLobby() { active = false; Generation++; } internal static bool IsCurrent(int generation) { if (IsActive) { return generation == Generation; } return false; } internal static bool TryAcceptSnapshot(int roundId, int revision, ref int lastRoundId, ref int lastRevision) { return SnapshotValidation.TryAccept(roundId, revision, ref lastRoundId, ref lastRevision); } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision, ref int lastRoundId, ref int lastRevision) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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) if (!MyceliumNetwork.InLobby || hostId.m_SteamID == 0L || MyceliumNetwork.LobbyHost.m_SteamID != hostId.m_SteamID) { return false; } return TryAcceptSnapshot(roundId, revision, ref lastRoundId, ref lastRevision); } } [HarmonyPatch] internal static class Weapon_AmmoInitialization_Patch { private static MethodBase? TargetMethod() { return typeof(Weapon).GetMethod("Awake___UserLogic", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(Weapon).GetMethod("Awake", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } private static bool Prepare() { return TargetMethod() != null; } private static void Postfix(Weapon __instance) { WeaponAmmoTuning.CaptureMagazineSize(__instance); } } [HarmonyPatch(typeof(PauseManager), "MoveAmmoDisplay")] internal static class PauseManager_RemotePlayerHudCleanup_Patch { private static bool Prefix() { return !PlayerSetup_WeaponAmmoHudReset_Patch.SuppressRemoteHudCleanup; } } [HarmonyPatch(typeof(PauseManager), "ChangeAmmoText")] internal static class PauseManager_RemotePlayerAmmoTextCleanup_Patch { private static bool Prefix() { return !PlayerSetup_WeaponAmmoHudReset_Patch.SuppressRemoteHudCleanup; } } [HarmonyPatch] internal static class PlayerSetup_WeaponAmmoHudReset_Patch { [ThreadStatic] internal static bool SuppressRemoteHudCleanup; private static MethodBase? TargetMethod() { return typeof(PlayerSetup).GetMethod("OnDisable___UserLogic", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(PlayerSetup).GetMethod("OnDisable", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } private static void Prefix(PlayerSetup __instance) { SuppressRemoteHudCleanup = (Object)(object)__instance != (Object)null && !((NetworkBehaviour)__instance).IsOwner; } private static void Postfix(PlayerSetup __instance) { bool num = (Object)(object)__instance != (Object)null && ((NetworkBehaviour)__instance).IsOwner; SuppressRemoteHudCleanup = false; if (num) { WeaponAmmoTuning.ScheduleLocalAmmoHudRefresh(); } } } internal static class WeaponAmmoTuning { private sealed class Memory { public int MagazineSize; public int SpareRounds; public bool Initialized; public bool Reloading; public bool ManualReloadPressed; public bool OriginalInHandDespawn; public bool UnlimitedAmmo; public bool SingleShot; } private const float CustomReloadTime = 1.75f; private static readonly ConditionalWeakTable MemoryByWeapon = new ConditionalWeakTable(); private static bool fallbackReloadClipResolved; private static AudioClip? fallbackReloadClip; private static Coroutine? hudRefreshCoroutine; internal static void CaptureMagazineSize(Weapon weapon) { if ((Object)(object)weapon == (Object)null || !weapon.needsAmmo) { return; } Memory orCreateValue = MemoryByWeapon.GetOrCreateValue(weapon); if (!orCreateValue.Initialized && !orCreateValue.SingleShot) { int num = ((!weapon.reloadWeapon) ? weapon.currentAmmo : ((weapon.ammoCharge > 0) ? weapon.ammoCharge : Mathf.RoundToInt(weapon.chargedBullets))); if (num > 0) { orCreateValue.MagazineSize = num; orCreateValue.Initialized = true; } } } internal static void Initialize(Weapon weapon, int spareMagazines) { if (!((Object)(object)weapon == (Object)null) && weapon.needsAmmo) { CaptureMagazineSize(weapon); Memory orCreateValue = MemoryByWeapon.GetOrCreateValue(weapon); if (!orCreateValue.Initialized) { orCreateValue.MagazineSize = Mathf.Max(1, weapon.currentAmmo); orCreateValue.Initialized = true; } if (orCreateValue.SpareRounds <= 0) { orCreateValue.SpareRounds = orCreateValue.MagazineSize * Mathf.Max(0, spareMagazines); } } } internal static void InitializeUnlimited(Weapon weapon) { if (!((Object)(object)weapon == (Object)null) && weapon.needsAmmo) { Memory orCreateValue = MemoryByWeapon.GetOrCreateValue(weapon); CaptureMagazineSize(weapon); if (!orCreateValue.Initialized) { orCreateValue.MagazineSize = Mathf.Max(1, weapon.currentAmmo); orCreateValue.Initialized = true; } orCreateValue.UnlimitedAmmo = true; } } internal static void InitializeSingleShot(Weapon weapon, int spareRounds) { if (!((Object)(object)weapon == (Object)null) && weapon.needsAmmo) { Memory orCreateValue = MemoryByWeapon.GetOrCreateValue(weapon); if (!orCreateValue.SingleShot) { orCreateValue.MagazineSize = 1; orCreateValue.SpareRounds = Mathf.Max(0, spareRounds); orCreateValue.Initialized = true; orCreateValue.UnlimitedAmmo = false; orCreateValue.SingleShot = true; weapon.reloadWeapon = false; weapon.currentAmmo = 1; } } } internal static void SetSingleShotSpareRounds(Weapon weapon, int spareRounds) { InitializeSingleShot(weapon, spareRounds); if (MemoryByWeapon.TryGetValue(weapon, out Memory value)) { value.SpareRounds = Mathf.Max(0, spareRounds); } } internal static void AddSingleShotSpareRounds(Weapon weapon, int amount) { if (amount > 0) { InitializeSingleShot(weapon, 0); if (MemoryByWeapon.TryGetValue(weapon, out Memory value)) { value.SpareRounds += amount; } } } internal static bool IsSingleShot(Weapon weapon) { if (MemoryByWeapon.TryGetValue(weapon, out Memory value)) { return value.SingleShot; } return false; } internal static void InitializeFromSpawnerPickup(Weapon weapon, int spareMagazines) { if ((Object)(object)weapon == (Object)null || !weapon.needsAmmo) { return; } CaptureMagazineSize(weapon); Memory orCreateValue = MemoryByWeapon.GetOrCreateValue(weapon); orCreateValue.Reloading = false; orCreateValue.ManualReloadPressed = false; orCreateValue.UnlimitedAmmo = false; if (weapon.reloadWeapon) { if (orCreateValue.MagazineSize <= 0) { orCreateValue.MagazineSize = Mathf.Max(1, (weapon.ammoCharge > 0) ? weapon.ammoCharge : Mathf.RoundToInt(weapon.chargedBullets)); } weapon.currentAmmo = orCreateValue.MagazineSize * Mathf.Max(0, spareMagazines); orCreateValue.SpareRounds = weapon.currentAmmo; } else { if (orCreateValue.MagazineSize <= 0) { orCreateValue.MagazineSize = Mathf.Max(1, weapon.currentAmmo); } orCreateValue.SpareRounds = orCreateValue.MagazineSize * Mathf.Max(0, spareMagazines); weapon.currentAmmo = orCreateValue.MagazineSize; } orCreateValue.Initialized = true; } private static object? GetFieldValue(Weapon weapon, string fieldName) { Type type = ((object)weapon).GetType(); while (type != null) { FieldInfo field = type.GetField(fieldName, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(weapon); } type = type.BaseType; } return null; } private static void SetFieldValue(Weapon weapon, string fieldName, object value) { Type type = ((object)weapon).GetType(); while (type != null) { FieldInfo field = type.GetField(fieldName, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(weapon, value); break; } type = type.BaseType; } } internal static int GetSpareRounds(Weapon weapon) { if (!MemoryByWeapon.TryGetValue(weapon, out Memory value)) { return 0; } return value.SpareRounds; } internal static bool IsReloading(Weapon weapon) { if (MemoryByWeapon.TryGetValue(weapon, out Memory value)) { return value.Reloading; } return false; } internal static void ScheduleLocalAmmoHudRefresh() { if (!((Object)(object)Plugin.Instance == (Object)null) && hudRefreshCoroutine == null) { hudRefreshCoroutine = ((MonoBehaviour)Plugin.Instance).StartCoroutine(RefreshLocalAmmoHudAfterReset(SessionState.Generation)); } } private static IEnumerator RefreshLocalAmmoHudAfterReset(int sessionGeneration) { for (int attempt = 0; attempt < 40; attempt++) { if (!SessionState.IsCurrent(sessionGeneration)) { hudRefreshCoroutine = null; yield break; } yield return (object)new WaitForSeconds(0.1f); RefreshLocalAmmoHud(); } hudRefreshCoroutine = null; } internal static void RefreshLocalAmmoHud() { if (WeaponSettingsState.Enabled && !((Object)(object)PauseManager.Instance == (Object)null) && !((Object)(object)ClientInstance.Instance == (Object)null)) { PlayerPickup val = (ClientInstance.Instance.PlayerSpawner?.player)?.playerPickupScript; if (!((Object)(object)val == (Object)null)) { RefreshHeldWeaponHud(val.objInHand, rightHand: true); RefreshHeldWeaponHud(val.objInLeftHand, rightHand: false); } } } private static void RefreshHeldWeaponHud(GameObject? heldObject, bool rightHand) { Weapon val = ((heldObject != null) ? heldObject.GetComponent() : null); if (!((Object)(object)val == (Object)null) && val.needsAmmo && !val.reloadWeapon && ((Component)val).gameObject.layer == 8) { Initialize(val, WeaponSettingsState.SpareMagazines); int num = Mathf.Max(0, val.currentAmmo); PauseManager.Instance.MoveAmmoDisplay(true, rightHand); PauseManager.Instance.ChangeAmmoText(GetSpareRounds(val).ToString(), num + " / ", rightHand); } } internal static void TryStartManualReload(Weapon weapon, bool enabled, int spareMagazines) { if ((Object)(object)weapon == (Object)null || !weapon.needsAmmo) { return; } Initialize(weapon, spareMagazines); if (MemoryByWeapon.TryGetValue(weapon, out Memory value)) { bool flag = ((Component)weapon).gameObject.layer == 8 && weapon.reload != null && weapon.reload.ReadValue() > 0.1f; bool manualReloadPressed = value.ManualReloadPressed; value.ManualReloadPressed = flag; if (!(!enabled || !flag || manualReloadPressed) && !weapon.reloadWeapon && ((NetworkBehaviour)weapon).IsOwner && ((Component)weapon).gameObject.layer == 8 && !value.Reloading && (value.UnlimitedAmmo || value.SpareRounds > 0) && weapon.currentAmmo < value.MagazineSize) { StartReload(weapon, value); } } } internal static void ApplyToWeapon(Weapon weapon, bool enabled, int spareMagazines) { if (enabled && !((Object)(object)weapon == (Object)null) && weapon.needsAmmo) { Initialize(weapon, spareMagazines); if (MemoryByWeapon.TryGetValue(weapon, out Memory value)) { value.UnlimitedAmmo = false; } if (((Component)weapon).gameObject.layer == 8) { ReloadIfEmpty(weapon, spareMagazines); } } } internal static void ApplyUnlimitedToWeapon(Weapon weapon) { if (!((Object)(object)weapon == (Object)null) && weapon.needsAmmo) { InitializeUnlimited(weapon); if (((Component)weapon).gameObject.layer == 8) { ReloadIfEmpty(weapon, 0); } } } private static void ReloadIfEmpty(Weapon weapon, int spareMagazines) { if (!((Object)(object)weapon == (Object)null) && !weapon.reloadWeapon && weapon.needsAmmo) { Initialize(weapon, spareMagazines); if (MemoryByWeapon.TryGetValue(weapon, out Memory value) && (value.UnlimitedAmmo || value.SpareRounds > 0) && weapon.currentAmmo <= 0 && !value.Reloading) { StartReload(weapon, value); } } } private static void StartReload(Weapon weapon, Memory memory) { memory.Reloading = true; object fieldValue = GetFieldValue(weapon, "inHandDespawn"); bool flag = default(bool); int num; if (fieldValue is bool) { flag = (bool)fieldValue; num = 1; } else { num = 0; } memory.OriginalInHandDespawn = (byte)((uint)num & (flag ? 1u : 0u)) != 0; weapon.cantTakeSafeBool = true; SetFieldValue(weapon, "inHandDespawn", false); weapon.isReloading = true; ((MonoBehaviour)weapon).StartCoroutine(Reload(weapon, memory)); } private static IEnumerator Reload(Weapon weapon, Memory memory) { object? fieldValue = GetFieldValue(weapon, "reloadClip"); AudioClip val = (AudioClip)(((fieldValue is AudioClip) ? fieldValue : null) ?? GetFallbackReloadClip()); if ((Object)(object)val != (Object)null && (Object)(object)weapon.audio != (Object)null) { weapon.audio.PlayOneShot(val); } float num = 1.75f; bool num2 = TriggerReloadAnimation(weapon); weapon.OnReload(); if (num2) { yield return (object)new WaitForSeconds(num); } else { yield return AnimateFallbackReload(weapon, num); } if (memory.Reloading) { int num3 = (memory.UnlimitedAmmo ? memory.MagazineSize : Mathf.Min(memory.MagazineSize, memory.SpareRounds)); if (!memory.UnlimitedAmmo) { memory.SpareRounds -= num3; } weapon.currentAmmo = num3; weapon.cantTakeSafeBool = false; weapon.noAmmoClicks = 0; memory.Reloading = false; weapon.isReloading = false; SetFieldValue(weapon, "inHandDespawn", memory.OriginalInHandDespawn); } } private static bool TriggerReloadAnimation(Weapon weapon) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 if ((Object)(object)weapon.animator == (Object)null) { return false; } AnimatorControllerParameter[] parameters = weapon.animator.parameters; foreach (AnimatorControllerParameter val in parameters) { if (val.name == "Reload" && (int)val.type == 9) { weapon.animator.SetTrigger(val.name); return true; } } return false; } private static IEnumerator AnimateFallbackReload(Weapon weapon, float reloadTime) { Transform transform = ((Component)weapon).transform; Vector3 initialPosition = transform.localPosition; Quaternion initialRotation = transform.localRotation; float elapsed = 0f; while (elapsed < reloadTime) { float num = Mathf.Sin(Mathf.Clamp01(elapsed / reloadTime) * MathF.PI); transform.localPosition = initialPosition + Vector3.down * (0.08f * num); transform.localRotation = initialRotation * Quaternion.Euler(25f * num, 0f, 0f); elapsed += Time.deltaTime; yield return null; } transform.localPosition = initialPosition; transform.localRotation = initialRotation; } private static AudioClip? GetFallbackReloadClip() { if (fallbackReloadClipResolved) { return fallbackReloadClip; } fallbackReloadClipResolved = true; GameObject? obj = WeaponService.FindPrefab("QCW05"); Weapon val = ((obj != null) ? obj.GetComponent() : null); fallbackReloadClip = (AudioClip?)(((Object)(object)val == (Object)null) ? null : /*isinst with value type is only supported in some contexts*/); return fallbackReloadClip; } } internal static class WeaponPolicy { internal static bool PrepareItemSpawn(ItemSpawner spawner) { if (GameModeManager.ShouldIgnoreGlobalWeaponSettings) { return false; } if (!WeaponSettingsState.Enabled || WeaponSettingsState.Allowed.Count == 0) { return true; } if (WeaponSettingsState.Cycle) { return false; } GameObject val = WeaponService.FindPrefab(WeaponSettingsState.Allowed[Random.Range(0, WeaponSettingsState.Allowed.Count)]); if ((Object)(object)val != (Object)null) { spawner.itemToSpawn = val; } return true; } internal static bool CanEquip(PlayerPickup pickup, GameObject obj, bool rightHand) { InitializeGlobalPickupAmmo(obj); if ((Object)(object)obj == (Object)null) { return true; } Weapon component = obj.GetComponent(); if ((Object)(object)component == (Object)null) { return true; } PlayerHealth component2 = ((Component)pickup).GetComponent(); switch (GameModeManager.ActiveMode) { case GameMode.Juggernaut: if (!((Object)(object)component2 == (Object)null) && JuggernautState.IsCurrentJuggernaut(component2)) { return ((Object)component).name.StartsWith("Minigun", StringComparison.Ordinal); } return true; case GameMode.KillTheRat: if ((Object)(object)component2 == (Object)null) { return true; } if (!KillTheRatState.IsRat(component2)) { return KillTheRatState.IsHumanWeapon(component); } return KillTheRatState.IsRatWeapon(component); case GameMode.MichaelMeyers: if ((Object)(object)component2 != (Object)null) { if (!MichaelMeyersState.CanHoldCouperet(component2) || !MichaelMeyersState.IsCouperet(component)) { if (MichaelMeyersState.CanHoldSurvivorWeapon(component2)) { return ((Object)component).name.StartsWith("Couperet", StringComparison.Ordinal); } return false; } return true; } return false; case GameMode.Infidel: { int num = component2?.playerValues?.playerClient?.PlayerId ?? (-1); if (num >= 0) { return InfidelState.IsAllowedWeapon(component, num); } return true; } case GameMode.HotPotato: { int num2 = component2?.playerValues?.playerClient?.PlayerId ?? (-1); if (num2 >= 0) { return HotPotatoState.IsAllowedWeapon(component, num2); } return true; } case GameMode.OneInTheChamber: if (!(OneInTheChamberState.IsPistol(component) && rightHand)) { if (OneInTheChamberState.IsCouperet(component)) { return !rightHand; } return false; } return true; case GameMode.SniperBattle: return SniperBattleState.IsSniperWeapon(component); default: return true; } } private static void InitializeGlobalPickupAmmo(GameObject obj) { if (!GameModeManager.ShouldIgnoreGlobalWeaponSettings && WeaponSettingsState.Enabled && !((Object)(object)obj == (Object)null)) { Weapon component = obj.GetComponent(); if (!((Object)(object)component == (Object)null) && !((Object)(object)((Component)obj.transform).GetComponentInParent() == (Object)null)) { WeaponAmmoTuning.InitializeFromSpawnerPickup(component, WeaponSettingsState.SpareMagazines); } } } } [HarmonyPatch(typeof(ItemSpawner), "Spawn")] [HarmonyPriority(800)] internal static class ItemSpawner_WeaponPolicy_Patch { private static bool Prefix(ItemSpawner __instance) { return WeaponPolicy.PrepareItemSpawn(__instance); } } [HarmonyPatch(typeof(PlayerPickup), "SetObjectInHandServer")] [HarmonyPriority(800)] internal static class PlayerPickup_WeaponPolicy_Patch { private static bool Prefix(PlayerPickup __instance, GameObject obj, bool rightHand) { return WeaponPolicy.CanEquip(__instance, obj, rightHand); } } internal static class WeaponReloadGuards { internal static bool CanFire(Weapon weapon) { return !WeaponAmmoTuning.IsReloading(weapon); } } [HarmonyPatch(typeof(BeamGun), "FireBlast")] internal static class BeamGun_FireBlast_Reload_Patch { private static bool Prefix(BeamGun __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } [HarmonyPatch(typeof(BeamGun), "Fire")] internal static class BeamGun_Fire_Reload_Patch { private static bool Prefix(BeamGun __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } [HarmonyPatch(typeof(Gun), "Fire")] internal static class Gun_Fire_Reload_Patch { private static bool Prefix(Gun __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } [HarmonyPatch(typeof(Shotgun), "Fire")] internal static class Shotgun_Fire_Reload_Patch { private static bool Prefix(Shotgun __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } [HarmonyPatch(typeof(LargeRaycastGun), "Fire")] internal static class LargeRaycastGun_Fire_Reload_Patch { private static bool Prefix(LargeRaycastGun __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } [HarmonyPatch(typeof(Minigun), "Fire")] internal static class Minigun_Fire_Reload_Patch { private static bool Prefix(Minigun __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } [HarmonyPatch(typeof(RepulsiveGun), "Fire")] internal static class RepulsiveGun_Fire_Reload_Patch { private static bool Prefix(RepulsiveGun __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } [HarmonyPatch(typeof(FlashLight), "Fire")] internal static class FlashLight_Fire_Reload_Patch { private static bool Prefix(FlashLight __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } [HarmonyPatch(typeof(ChargeGun), "Fire")] internal static class ChargeGun_Fire_Reload_Patch { private static bool Prefix(ChargeGun __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } [HarmonyPatch(typeof(BumpGun), "Fire")] internal static class BumpGun_Fire_Reload_Patch { private static bool Prefix(BumpGun __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } [HarmonyPatch(typeof(DualLauncher), "Fire")] internal static class DualLauncher_Fire_Reload_Patch { private static bool Prefix(DualLauncher __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } [HarmonyPatch(typeof(Taser), "Fire")] internal static class Taser_Fire_Reload_Patch { private static bool Prefix(Taser __instance) { return WeaponReloadGuards.CanFire((Weapon)(object)__instance); } } internal static class WeaponService { private static readonly Dictionary Prefabs = new Dictionary(StringComparer.Ordinal); private static readonly BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static MethodInfo? SetObjectInHandLogic; private static MethodInfo? SetObjectInHandObserverLogic; private static bool _attachmentMethodsResolved; private static bool _attachmentMethodsAvailable; private static readonly RequestVersionTracker RequestVersions = new RequestVersionTracker(); private static readonly RequestVersionTracker LeftHandRequestVersions = new RequestVersionTracker(); private static readonly HashSet PendingOwnerAttachments = new HashSet(); internal static bool IsFinalGameScreen { get { //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) if ((Object)(object)PauseManager.Instance != (Object)null && PauseManager.Instance.inVictoryMenu) { return true; } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (!(name == "VictoryScene")) { return name == "EndGame"; } return true; } } internal static void Initialize() { ResolveAttachmentMethods(); } internal static void ResetPendingRequests() { RequestVersions.Clear(); LeftHandRequestVersions.Clear(); PendingOwnerAttachments.Clear(); } internal static void CachePrefabs() { if (Prefabs.Count != 0) { return; } GameObject[] array = Resources.LoadAll("RandomWeapons"); foreach (GameObject val in array) { if ((Object)(object)val != (Object)null && !Prefabs.ContainsKey(((Object)val).name)) { Prefabs[((Object)val).name] = val; } } } internal static GameObject? FindPrefab(string weaponName) { CachePrefabs(); if (!Prefabs.TryGetValue(weaponName.Trim(), out GameObject value)) { return null; } return value; } internal static List ParseWeaponList(string value) { CachePrefabs(); return WeaponListParser.Parse(value, Prefabs.Keys); } internal static void GiveWeapon(int playerId, string weaponName, int? spareMagazines = null, bool unlimitedAmmo = false) { if ((Object)(object)Plugin.Instance != (Object)null && !IsFinalGameScreen) { int requestVersion = RequestVersions.Next(playerId); ((MonoBehaviour)Plugin.Instance).StartCoroutine(GiveWeaponCoroutine(playerId, weaponName, spareMagazines, unlimitedAmmo, SessionState.Generation, GameModeManager.RoundId, requestVersion, RequestVersions, rightHand: true, clearBothHands: true)); } } internal static void GiveWeaponToLeftHand(int playerId, string weaponName) { if ((Object)(object)Plugin.Instance != (Object)null && !IsFinalGameScreen) { int requestVersion = LeftHandRequestVersions.Next(playerId); ((MonoBehaviour)Plugin.Instance).StartCoroutine(GiveWeaponCoroutine(playerId, weaponName, null, unlimitedAmmo: false, SessionState.Generation, GameModeManager.RoundId, requestVersion, LeftHandRequestVersions, rightHand: false, clearBothHands: false)); } } private static IEnumerator GiveWeaponCoroutine(int playerId, string weaponName, int? spareMagazines, bool unlimitedAmmo, int sessionGeneration, int roundId, int requestVersion, RequestVersionTracker requestVersions, bool rightHand, bool clearBothHands) { NetworkManager networkManager = InstanceFinder.NetworkManager; GameObject prefab = FindPrefab(weaponName); if (!requestVersions.IsCurrent(playerId, requestVersion) || !SessionState.IsCurrent(sessionGeneration) || GameModeManager.RoundId != roundId || (Object)(object)networkManager == (Object)null || !networkManager.IsServer || (Object)(object)prefab == (Object)null || !ResolveAttachmentMethods()) { yield break; } PlayerPickup pickup = null; PlayerManager manager = null; for (int attempt = 0; attempt < 40; attempt++) { if (!requestVersions.IsCurrent(playerId, requestVersion) || !SessionState.IsCurrent(sessionGeneration) || GameModeManager.RoundId != roundId || IsFinalGameScreen) { yield break; } if (ClientInstance.playerInstances.TryGetValue(playerId, out var value)) { manager = value.PlayerSpawner; PlayerManager obj = manager; object obj2; if (obj == null) { obj2 = null; } else { FirstPersonController player = obj.player; obj2 = ((player != null) ? ((Component)player).GetComponent() : null); } pickup = (PlayerPickup)obj2; } if ((Object)(object)pickup != (Object)null && (Object)(object)manager?.player != (Object)null) { break; } yield return (object)new WaitForSeconds(0.25f); } if ((Object)(object)pickup == (Object)null || (Object)(object)manager?.player == (Object)null || !requestVersions.IsCurrent(playerId, requestVersion) || !SessionState.IsCurrent(sessionGeneration) || GameModeManager.RoundId != roundId || IsFinalGameScreen) { yield break; } if (clearBothHands) { DespawnHeldWeapon(networkManager, pickup.objInHand); DespawnHeldWeapon(networkManager, pickup.objInLeftHand); pickup.sync___set_value_hasObjectInHand(false, true); pickup.sync___set_value_hasObjectInLeftHand(false, true); pickup.sync___set_value_objInHand((GameObject)null, true); pickup.sync___set_value_objInLeftHand((GameObject)null, true); } else { DespawnHeldWeapon(networkManager, pickup.objInLeftHand); pickup.sync___set_value_hasObjectInLeftHand(false, true); pickup.sync___set_value_objInLeftHand((GameObject)null, true); } yield return (object)new WaitForSeconds(0.15f); if (!requestVersions.IsCurrent(playerId, requestVersion) || !SessionState.IsCurrent(sessionGeneration) || GameModeManager.RoundId != roundId || IsFinalGameScreen) { yield break; } GameObject weapon = Object.Instantiate(prefab, ((Component)manager.player).transform.position, ((Component)manager.player).transform.rotation); ItemBehaviour item = weapon.GetComponent(); if ((Object)(object)item != (Object)null) { item.dispenserStart = true; } Rigidbody component = weapon.GetComponent(); if ((Object)(object)component != (Object)null) { component.isKinematic = true; component.useGravity = false; } NotifyOwnerWeaponAttached(playerId); networkManager.ServerManager.Spawn(weapon, (NetworkConnection)null); yield return (object)new WaitForSeconds(0.1f); if (!requestVersions.IsCurrent(playerId, requestVersion) || !SessionState.IsCurrent(sessionGeneration) || GameModeManager.RoundId != roundId || IsFinalGameScreen) { DespawnHeldWeapon(networkManager, weapon); yield break; } Weapon component2 = weapon.GetComponent(); if ((Object)(object)item == (Object)null || (Object)(object)component2 == (Object)null) { DespawnHeldWeapon(networkManager, weapon); yield break; } if (unlimitedAmmo) { WeaponAmmoTuning.InitializeUnlimited(component2); } else if (spareMagazines.HasValue) { WeaponAmmoTuning.InitializeFromSpawnerPickup(component2, spareMagazines.Value); } Transform val = ((!rightHand) ? pickup.pickupPositionLeftHand[item.camChildIndexLeftHand] : (component2.requireBothHands ? pickup.pickupPositionBothHand[item.camChildIndex] : pickup.pickupPositionRightHand[item.camChildIndex])); weapon.transform.SetPositionAndRotation(val.position, val.rotation); object[] parameters = new object[5] { weapon, val.position, val.rotation, ((Component)manager.player).gameObject, rightHand }; SetObjectInHandLogic.Invoke(pickup, parameters); if (rightHand) { pickup.sync___set_value_hasObjectInHand(true, true); pickup.sync___set_value_objInHand(weapon, true); } else { pickup.sync___set_value_hasObjectInLeftHand(true, true); pickup.sync___set_value_objInLeftHand(weapon, true); } SetObjectInHandObserverLogic.Invoke(pickup, parameters); pickup.HandsReconstruct(); if (!rightHand) { pickup.SetLeftIKTarget(item.gripLeft); } pickup.UpdateIKPoistion(); item.InstantComeBackOnFire(); if ((Object)(object)item != (Object)null) { item.dispenserStart = false; } } internal static void AttachUnparentedWeapon(PlayerPickup pickup) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if (!((NetworkBehaviour)pickup).IsOwner || !pickup.hasObjectInHand || (Object)(object)pickup.objInHand == (Object)null) { return; } GameObject objInHand = pickup.objInHand; ItemBehaviour component = objInHand.GetComponent(); Weapon component2 = objInHand.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null) { return; } Transform val = (component2.requireBothHands ? pickup.pickupPositionBothHand[component.camChildIndex] : pickup.pickupPositionRightHand[component.camChildIndex]); if (!((Object)(object)val == (Object)null) && (!((Object)(object)objInHand.transform.parent == (Object)(object)val) || !component2.inRightHand || !((Object)(object)component.playerPickup == (Object)(object)pickup) || !((Object)(object)component.rootObject == (Object)(object)((Component)pickup).gameObject)) && ResolveAttachmentMethods()) { object[] parameters = new object[5] { objInHand, val.position, val.rotation, ((Component)pickup).gameObject, true }; SetObjectInHandObserverLogic.Invoke(pickup, parameters); pickup.HandsReconstruct(); pickup.SetRightIKTarget(component.gripRight); if (component2.requireBothHands) { pickup.SetLeftIKTarget(component.gripLeft); } pickup.UpdateIKPoistion(); component.InstantComeBackOnFire(); component.dispenserStart = false; } } internal static void ClearHeldWeapons(PlayerPickup pickup) { NetworkManager networkManager = InstanceFinder.NetworkManager; if (!((Object)(object)networkManager == (Object)null) && networkManager.IsServer) { DespawnHeldWeapon(networkManager, pickup.objInHand); DespawnHeldWeapon(networkManager, pickup.objInLeftHand); pickup.sync___set_value_hasObjectInHand(false, true); pickup.sync___set_value_hasObjectInLeftHand(false, true); pickup.sync___set_value_objInHand((GameObject)null, true); pickup.sync___set_value_objInLeftHand((GameObject)null, true); } } internal static void AttachGrantedWeaponForOwner(int playerId) { if (!((Object)(object)Plugin.Instance == (Object)null) && PendingOwnerAttachments.Add(playerId)) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(AttachGrantedWeaponAfterSync(playerId, SessionState.Generation)); } } internal static bool IsOwnerAttachmentPending(PlayerPickup pickup) { if (((NetworkBehaviour)pickup).IsOwner && (Object)(object)ClientInstance.Instance != (Object)null) { return PendingOwnerAttachments.Contains(ClientInstance.Instance.PlayerId); } return false; } private static IEnumerator AttachGrantedWeaponAfterSync(int playerId, int sessionGeneration) { for (int attempt = 0; attempt < 20; attempt++) { if (!SessionState.IsCurrent(sessionGeneration)) { PendingOwnerAttachments.Remove(playerId); yield break; } if ((Object)(object)ClientInstance.Instance == (Object)null || ClientInstance.Instance.PlayerId != playerId) { yield return (object)new WaitForSeconds(0.1f); continue; } PlayerPickup val = ClientInstance.Instance.PlayerSpawner?.player?.playerPickupScript; GameObject val2 = val?.objInHand; GameObject val3 = val?.objInLeftHand; if ((Object)(object)val != (Object)null && (((Object)(object)val2 != (Object)null && Object.op_Implicit((Object)(object)val2)) || ((Object)(object)val3 != (Object)null && Object.op_Implicit((Object)(object)val3)))) { val.hasObjectInHand = true; bool num = (Object)(object)val2 == (Object)null || !Object.op_Implicit((Object)(object)val2) || AttachWeaponLocally(val, val2, rightHand: true); bool flag = (Object)(object)val3 == (Object)null || !Object.op_Implicit((Object)(object)val3) || AttachWeaponLocally(val, val3, rightHand: false); if (num && flag) { PendingOwnerAttachments.Remove(playerId); yield break; } } yield return (object)new WaitForSeconds(0.1f); } PendingOwnerAttachments.Remove(playerId); } private static bool AttachWeaponLocally(PlayerPickup pickup, GameObject weapon, bool rightHand) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) Weapon component = weapon.GetComponent(); ItemBehaviour component2 = weapon.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !ResolveAttachmentMethods()) { return false; } try { Transform val = ((!rightHand) ? pickup.pickupPositionLeftHand[component2.camChildIndexLeftHand] : (component.requireBothHands ? pickup.pickupPositionBothHand[component2.camChildIndex] : pickup.pickupPositionRightHand[component2.camChildIndex])); object[] parameters = new object[5] { weapon, val.position, val.rotation, ((Component)pickup).gameObject, rightHand }; SetObjectInHandObserverLogic.Invoke(pickup, parameters); pickup.HandsReconstruct(); if (!rightHand) { pickup.SetLeftIKTarget(component2.gripLeft); } pickup.UpdateIKPoistion(); component2.InstantComeBackOnFire(); component2.dispenserStart = false; return weapon.layer == 8; } catch (Exception ex) { Plugin.Logger.LogDebug((object)("[WeaponService] Owner attachment retry: " + ex.GetBaseException().Message)); return false; } } internal static void NotifyOwnerWeaponAttached(int playerId) { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { if ((Object)(object)ClientInstance.Instance != (Object)null && ClientInstance.Instance.PlayerId == playerId) { AttachGrantedWeaponForOwner(playerId); } MyceliumNetwork.RPC(1618033991u, "AttachServerGrantedWeapon", (ReliableType)1, new object[1] { playerId }); } } private static void DespawnHeldWeapon(NetworkManager networkManager, GameObject? heldWeapon) { if (!((Object)(object)heldWeapon == (Object)null)) { NetworkObject componentInParent = heldWeapon.GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null && componentInParent.IsSpawned) { networkManager.ServerManager.Despawn(componentInParent, (DespawnType?)null); } else { Object.Destroy((Object)(object)heldWeapon); } } } private static bool ResolveAttachmentMethods() { if (_attachmentMethodsResolved) { return _attachmentMethodsAvailable; } _attachmentMethodsResolved = true; SetObjectInHandLogic = FindAttachmentMethod("RpcLogic___SetObjectInHandServer_"); SetObjectInHandObserverLogic = FindAttachmentMethod("RpcLogic___SetObjectInHandObserver_"); _attachmentMethodsAvailable = SetObjectInHandLogic != null && SetObjectInHandObserverLogic != null; if (!_attachmentMethodsAvailable) { Plugin.Logger.LogError((object)"[WeaponService] Could not resolve FishNet weapon attachment methods. Weapon grants are disabled until the game assembly is updated."); } return _attachmentMethodsAvailable; } private static MethodInfo? FindAttachmentMethod(string namePrefix) { MethodInfo[] array = (from method in typeof(PlayerPickup).GetMethods(Flags) where method.Name.StartsWith(namePrefix, StringComparison.Ordinal) && HasAttachmentSignature(method) select method).ToArray(); if (array.Length > 1) { Plugin.Logger.LogWarning((object)("[WeaponService] Multiple attachment methods match '" + namePrefix + "'. Using '" + array[0].Name + "'.")); } return array.FirstOrDefault(); } private static bool HasAttachmentSignature(MethodInfo method) { ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length == 5 && parameters[0].ParameterType == typeof(GameObject) && parameters[1].ParameterType == typeof(Vector3) && parameters[2].ParameterType == typeof(Quaternion) && parameters[3].ParameterType == typeof(GameObject)) { return parameters[4].ParameterType == typeof(bool); } return false; } } [HarmonyPatch(typeof(Weapon), "WeaponUpdate")] internal static class Weapon_UpdatePolicy_Patch { private static void Prefix(Weapon __instance) { if (!GameModeManager.ShouldIgnoreGlobalWeaponSettingsFor(__instance)) { WeaponAmmoTuning.ApplyToWeapon(__instance, WeaponSettingsState.Enabled, WeaponSettingsState.SpareMagazines); } if (GameModeManager.IsActive(GameMode.Juggernaut) && (Object)(object)__instance != (Object)null && ((Object)__instance).name.StartsWith("Minigun", StringComparison.Ordinal) && __instance.needsAmmo) { __instance.currentAmmo = 1; __instance.chargedBullets = Mathf.Max(1, __instance.ammoCharge); } if (GameModeManager.IsActive(GameMode.SniperBattle) && (Object)(object)__instance != (Object)null && SniperBattleState.IsSniperWeapon(__instance) && __instance.needsAmmo && ((Component)__instance).gameObject.layer == 8 && __instance.currentAmmo <= 0) { __instance.currentAmmo = 1; __instance.cantTakeSafeBool = false; __instance.noAmmoClicks = 0; } } private static void Postfix(Weapon __instance) { if (GameModeManager.IsActive(GameMode.GunGame) && GunGameState.Enabled) { WeaponAmmoTuning.ApplyUnlimitedToWeapon(__instance); WeaponAmmoTuning.TryStartManualReload(__instance, enabled: true, 0); } else if (GameModeManager.IsActive(GameMode.KillTheRat) && KillTheRatState.IsHumanWeapon(__instance)) { WeaponAmmoTuning.ApplyUnlimitedToWeapon(__instance); WeaponAmmoTuning.TryStartManualReload(__instance, enabled: true, 0); } else if (GameModeManager.IsActive(GameMode.OneInTheChamber) && OneInTheChamberState.IsPistol(__instance)) { int num = (((Object)(object)__instance.playerController == (Object)null) ? null : ((Component)__instance.playerController).GetComponent())?.playerValues?.playerClient?.PlayerId ?? (-1); if (num >= 0) { OneInTheChamberState.EnforcePistolAmmo(__instance, num); } } if (!GameModeManager.ShouldIgnoreGlobalWeaponSettingsFor(__instance)) { WeaponAmmoTuning.TryStartManualReload(__instance, WeaponSettingsState.Enabled, WeaponSettingsState.SpareMagazines); bool flag = (Object)(object)__instance != (Object)null && WeaponAmmoTuning.IsReloading(__instance); if (WeaponSettingsState.Enabled && (Object)(object)__instance != (Object)null && ((NetworkBehaviour)__instance).IsOwner && __instance.needsAmmo && ((Component)__instance).gameObject.layer == 8 && (flag || !__instance.reloadWeapon)) { int spareRounds = WeaponAmmoTuning.GetSpareRounds(__instance); int num2 = ((!flag) ? Mathf.Max(0, __instance.currentAmmo) : 0); PauseManager.Instance.ChangeAmmoText(spareRounds.ToString(), num2 + " / ", __instance.inRightHand); } } if ((Object)(object)__instance != (Object)null && __instance.inRightHand && (Object)(object)__instance.playerController != (Object)null) { MovementPolicy.Apply(__instance.playerController); } } } [HarmonyPatch(typeof(PlayerManager), "SpawnPlayer", new Type[] { typeof(int), typeof(int), typeof(Vector3), typeof(Quaternion) })] internal static class PlayerManager_SniperBattleSpawn_Patch { private static void Postfix(PlayerManager __instance) { if (GameModeManager.IsActive(GameMode.SniperBattle) && !WeaponService.IsFinalGameScreen && !((Object)(object)__instance.player == (Object)null)) { ClientInstance component = ((Component)__instance).GetComponent(); if ((Object)(object)component != (Object)null) { SniperBattleState.GiveStartingWeapon(component.PlayerId); } } } } internal static class SniperBattleState { internal const string WeaponName = "M2000"; internal const float PlayerHealth = 10f; internal static bool Enabled; internal static int WinnerId = -1; internal static readonly Dictionary Points = new Dictionary(); private static readonly Dictionary PendingLoadouts = new Dictionary(); private static float _nextLoadoutCheckTime; private static readonly ModeSyncState Sync = new ModeSyncState(); internal static int PointsToWin => GameModeManager.EffectivePointsToWin; internal static void ApplySettings(bool enabled) { bool num = Enabled != enabled; Enabled = enabled; if (num) { ResetMatchState(); } } private static void ApplyFromConfig() { ApplySettings(Plugin.SniperBattleEnabled.Value); } internal static void PushSettingsIfHost() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { ApplyFromConfig(); MyceliumNetwork.RPC(1618033992u, "SyncSniperBattleSettings", (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.NextSettingsRevision(), Plugin.SniperBattleEnabled.Value }); } } internal static void PeriodicPushSettingsIfHost() { if (Sync.IsSettingsPushDue()) { PushSettingsIfHost(); } } internal static void PeriodicPushIfHost() { PeriodicPushSettingsIfHost(); if (Sync.IsLivePushDue()) { BroadcastLiveState(); } } internal static void OnLobbyEntered() { Sync.ResetForLobby(); if (MyceliumNetwork.IsHost) { ApplyFromConfig(); ResetMatchState(); } } internal static void OnPlayerEntered(CSteamID player) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.IsHost) { MyceliumNetwork.RPCTarget(1618033992u, "SyncSniperBattleSettings", player, (ReliableType)1, new object[4] { MyceliumNetwork.LobbyHost, GameModeManager.RoundId, Sync.SettingsRevision, Plugin.SniperBattleEnabled.Value }); MyceliumNetwork.RPCTarget(1618033992u, "SyncSniperBattleLiveState", player, (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, SerializePoints(), WinnerId, GameModeManager.RoundId, Sync.LiveRevision }); } } internal static bool TryAcceptSettingsSnapshot(CSteamID hostId, int roundId, int revision) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Sync.TryAcceptSettingsSnapshot(hostId, roundId, revision); } internal static void ResetMatchState() { Sync.ResetLiveState(); WinnerId = -1; Points.Clear(); PendingLoadouts.Clear(); } internal static void ApplyLiveState(CSteamID hostId, string pointsData, int winnerId, int roundId, int revision) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (winnerId < -1 || !Sync.TryAcceptLiveSnapshot(hostId, roundId, revision)) { return; } WinnerId = winnerId; Points.Clear(); foreach (KeyValuePair item in ScoreCodec.Parse(pointsData, PointsToWin)) { Points[item.Key] = item.Value; } } internal static void OnServerKill(int deadPlayerId, int killerId) { if (Enabled && WinnerId < 0 && killerId >= 0 && killerId != deadPlayerId) { Points.TryGetValue(killerId, out var value); int num = value + 10; Points[killerId] = num; GameModeHud.ShowScorePopupForPlayer(killerId, 10); if (num >= PointsToWin) { WinnerId = killerId; Announce(PlayerLookup.GetPlayerNameTag(killerId) + " reached " + PointsToWin + " points and won the round!"); GameModeManager.CompleteCustomRound(ScoreManager.Instance.GetTeamId(killerId)); } BroadcastLiveState(); } } internal static void ApplyHealth(PlayerHealth health) { health.fullHealth = 10f; if (((NetworkBehaviour)health).IsServer) { float num = health.sync___get_value_health(); if (num > 10f) { FishNetCompatibility.TryRemoveHealth(health, num - 10f); } } } internal static bool IsSniperWeapon(Weapon weapon) { if ((Object)(object)weapon != (Object)null) { return ((Object)weapon).name.StartsWith("M2000", StringComparison.Ordinal); } return false; } internal static void GiveStartingWeapon(int playerId) { if (Enabled && GameModeManager.IsActive(GameMode.SniperBattle)) { PendingLoadouts[playerId] = Time.unscaledTime + 5f; WeaponService.GiveWeapon(playerId, "M2000"); } } internal static void EnsureLoadouts() { if (!Enabled || !GameModeManager.IsActive(GameMode.SniperBattle) || WeaponService.IsFinalGameScreen || !MyceliumNetwork.InLobby || !MyceliumNetwork.IsHost || Time.unscaledTime < _nextLoadoutCheckTime) { return; } _nextLoadoutCheckTime = Time.unscaledTime + 1f; foreach (ClientInstance value2 in ClientInstance.playerInstances.Values) { if (!((Object)(object)value2 == (Object)null) && Object.op_Implicit((Object)(object)value2) && !((Object)(object)value2.PlayerSpawner == (Object)null) && Object.op_Implicit((Object)(object)value2.PlayerSpawner) && !((Object)(object)value2.PlayerSpawner.player == (Object)null) && Object.op_Implicit((Object)(object)value2.PlayerSpawner.player)) { GameObject val = value2.PlayerSpawner.player.playerPickupScript?.objInHand; Weapon val2 = (((Object)(object)val == (Object)null || !Object.op_Implicit((Object)(object)val)) ? null : val.GetComponent()); float value; if ((Object)(object)val2 != (Object)null && IsSniperWeapon(val2)) { PendingLoadouts.Remove(value2.PlayerId); } else if (!PendingLoadouts.TryGetValue(value2.PlayerId, out value) || Time.unscaledTime >= value) { GiveStartingWeapon(value2.PlayerId); } } } } private static string SerializePoints() { return ScoreCodec.Serialize(Points); } private static void BroadcastLiveState() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(1618033992u, "SyncSniperBattleLiveState", (ReliableType)1, new object[5] { MyceliumNetwork.LobbyHost, SerializePoints(), WinnerId, GameModeManager.RoundId, Sync.NextLiveRevision() }); } } private static void Announce(string text) { if (MyceliumNetwork.InLobby && MyceliumNetwork.IsHost) { MyceliumNetwork.RPC(1618033992u, "SniperBattleAnnounce", (ReliableType)1, new object[1] { text }); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "StraftatEightsPlugin"; public const string PLUGIN_NAME = "Straftat Eights"; public const string PLUGIN_VERSION = "0.1.0"; } }