Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SoloHost v1.2.1
BepInEx/plugins/SoloHost.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using SoloHost.Rules; using SoloHost.Secure; using SoloShared; using Steamworks; using Steamworks.Data; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Utilities; using UnityEngine.Localization; using UnityEngine.Localization.Settings; using UnityEngine.Profiling; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("HappyTheLegend")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) 2026 HappyTheLegend")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+9aa58e96d80619b89d8c2741314ca206bd368d36")] [assembly: AssemblyProduct("SoloHost")] [assembly: AssemblyTitle("SoloHost")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SoloShared { internal static class Loc { private static Dictionary<string, string> _table = new Dictionary<string, string>(StringComparer.Ordinal); private static string _folder; private static string _mod = "Solo"; private static string _loaded = ""; private static float _nextCheck; private static Action<string> _log = delegate { }; private static Action<string> _warn = delegate { }; private static readonly SortedSet<string> _seen = new SortedSet<string>(StringComparer.Ordinal); private static bool _collect; internal static string Language { get { if (_loaded.Length != 0) { return _loaded; } return "en"; } } internal static void Init(string modName, string dllPath, Action<string> log, Action<string> warn, bool collectStrings = false) { _mod = modName ?? "Solo"; _log = log ?? ((Action<string>)delegate { }); _warn = warn ?? ((Action<string>)delegate { }); _collect = collectStrings; try { _folder = Path.GetDirectoryName(dllPath); } catch { _folder = null; } Reload(CurrentLanguage()); } internal static string T(string english) { if (string.IsNullOrEmpty(english)) { return english; } if (_collect) { lock (_seen) { _seen.Add(english); } } if (!_table.TryGetValue(english, out var value) || value.Length <= 0) { return english; } return value; } internal static string F(string english, params object[] args) { string format = T(english); try { return string.Format(format, args); } catch { try { return string.Format(english, args); } catch { return english; } } } internal static void Tick(float unscaledTime) { if (!(unscaledTime < _nextCheck)) { _nextCheck = unscaledTime + 2f; string text = CurrentLanguage(); if (text != _loaded) { Reload(text); } } } private static string CurrentLanguage() { //IL_0017: 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) try { Locale selectedLocale = LocalizationSettings.SelectedLocale; object obj; if (!((Object)(object)selectedLocale != (Object)null)) { obj = ""; } else { LocaleIdentifier identifier = selectedLocale.Identifier; obj = ((LocaleIdentifier)(ref identifier)).Code; } string text = (string)obj; return string.IsNullOrEmpty(text) ? "en" : text; } catch { return "en"; } } private static void Reload(string code) { _loaded = code ?? "en"; Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); if (!string.IsNullOrEmpty(_folder) && !_loaded.StartsWith("en", StringComparison.OrdinalIgnoreCase)) { string text = Path.Combine(_folder, "lang"); foreach (string item in Candidates(_loaded, text)) { string path = Path.Combine(text, item + ".txt"); if (File.Exists(path)) { int num = Read(path, dictionary); _log($"[{_mod}] Language {item}: {num} line(s) from {Path.GetFileName(path)}."); break; } } } _table = dictionary; if (dictionary.Count > 0) { _log("[" + _mod + "] Following the game's language: " + _loaded + "."); } if (dictionary.Count == 0 && !_loaded.StartsWith("en", StringComparison.OrdinalIgnoreCase)) { _log("[" + _mod + "] No translation file for '" + _loaded + "' — showing English. Add lang/" + _loaded + ".txt beside the mod to change that."); } } private static IEnumerable<string> Candidates(string code, string dir) { yield return code; string language = code; int num = code.IndexOf('-'); if (num > 0) { language = code.Substring(0, num); yield return language; } string[] array; try { array = (Directory.Exists(dir) ? Directory.GetFiles(dir, "*.txt") : new string[0]); } catch { yield break; } string[] array2 = array; for (int i = 0; i < array2.Length; i++) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(array2[i]); if (fileNameWithoutExtension.StartsWith(language + "-", StringComparison.OrdinalIgnoreCase)) { yield return fileNameWithoutExtension; } } } private static int Read(string path, Dictionary<string, string> into) { int num = 0; try { string text = null; string[] array = File.ReadAllLines(path, Encoding.UTF8); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2[0] == '#') { continue; } if (text2[0] == '=') { text = Unescape(text2.Substring(1).Trim()); } else if (text2[0] == '>' && text != null) { string text3 = Unescape(text2.Substring(1).Trim()); if (text3.Length > 0) { into[text] = text3; num++; } text = null; } } } catch (Exception ex) { _warn("[" + _mod + "] Couldn't read " + Path.GetFileName(path) + ": " + ex.Message); } return num; } private static string Unescape(string s) { return s.Replace("\\n", "\n"); } private static string Escape(string s) { return s.Replace("\n", "\\n"); } internal static void DumpSeen() { if (!_collect || string.IsNullOrEmpty(_folder)) { return; } try { string text = Path.Combine(_folder, "lang"); Directory.CreateDirectory(text); string path = Path.Combine(text, "_english.txt"); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# Every line " + _mod + " has shown this session."); stringBuilder.AppendLine("# Copy this file to <language code>.txt and fill in the > lines."); stringBuilder.AppendLine("# Leave a > line empty to keep the English."); stringBuilder.AppendLine(); lock (_seen) { foreach (string item in _seen) { stringBuilder.Append("= ").AppendLine(Escape(item)); stringBuilder.AppendLine("> "); } File.WriteAllText(path, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); _log($"[{_mod}] Wrote {_seen.Count} line(s) to lang/_english.txt."); } } catch (Exception ex) { _warn("[" + _mod + "] Couldn't write the English list: " + ex.Message); } } } } namespace SoloHost { internal static class AntiCheat { internal sealed class Flag { public string SteamId; public string Name; public string Reason; public float Time; public int Count = 1; } private static readonly List<Flag> _flags = new List<Flag>(); private static readonly Dictionary<string, Vector3> _lastPos = new Dictionary<string, Vector3>(); private static readonly Dictionary<string, Dictionary<string, int>> _lastUpgrades = new Dictionary<string, Dictionary<string, int>>(); private static float _nextPoll; private static float _lastPollTime; private static readonly Dictionary<string, float> _excused = new Dictionary<string, float>(); public static IEnumerable<Flag> Flags => _flags.OrderByDescending((Flag f) => f.Time); public static int FlagCount => _flags.Count; public static void Excuse(string steamId, float seconds = 5f) { if (!string.IsNullOrEmpty(steamId)) { _excused[steamId] = Time.time + seconds; } } public static void Excuse(PlayerAvatar p, float seconds = 5f) { if ((Object)(object)p != (Object)null) { Excuse(SemiFunc.PlayerGetSteamID(p), seconds); } } public static void ExcuseEveryone(float seconds = 5f) { foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { Excuse(item, seconds); } } private static bool IsExcused(string id) { if (_excused.TryGetValue(id, out var value)) { return Time.time < value; } return false; } public static void Clear() { _flags.Clear(); } public static void ClearFor(string steamId) { _flags.RemoveAll((Flag f) => f.SteamId == steamId); } public static void Poll() { if (!Plugin.Ready || !Plugin.AntiCheatEnabled.Value || !SemiFunc.IsMultiplayer() || !SemiFunc.IsMasterClientOrSingleplayer() || Time.time < _nextPoll) { return; } float dt = ((_lastPollTime > 0f) ? (Time.time - _lastPollTime) : 0.5f); _lastPollTime = Time.time; _nextPoll = Time.time + 0.5f; foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { if (!((Object)(object)item == (Object)null) && !item.isLocal) { string text = SemiFunc.PlayerGetSteamID(item); if (!string.IsNullOrEmpty(text)) { string name = SemiFunc.PlayerGetName(item) ?? text; CheckSpeed(item, text, name, dt); CheckUpgrades(item, text, name); } } } } private static void CheckSpeed(PlayerAvatar p, string id, string name, float dt) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)p).transform.position; if (!FlightController.IsTowing(p) && !FlightController.HasFlight(p) && !FlightController.HasNoclip(p) && !IsExcused(id) && _lastPos.TryGetValue(id, out var value) && dt > 0.01f) { float num = Vector3.Distance(position, value) / dt; if (num > Plugin.AntiCheatTeleportSpeed.Value) { Raise(id, name, $"moved {num:0} m/s (teleport?)"); } else if (num > Plugin.AntiCheatMaxSpeed.Value) { Raise(id, name, $"moving {num:0} m/s (speed hack?)"); } } _lastPos[id] = position; } private static void CheckUpgrades(PlayerAvatar p, string id, string name) { string[] upgradeNames; if (!_lastUpgrades.TryGetValue(id, out var value)) { value = new Dictionary<string, int>(); _lastUpgrades[id] = value; upgradeNames = SharedUpgrades.UpgradeNames; foreach (string text in upgradeNames) { value[text] = DevActions.GetUpgradeLevel(p, text); } return; } bool flag = IsExcused(id); upgradeNames = SharedUpgrades.UpgradeNames; foreach (string text2 in upgradeNames) { int upgradeLevel = DevActions.GetUpgradeLevel(p, text2); value.TryGetValue(text2, out var value2); int num = upgradeLevel - value2; if (!flag && num >= Plugin.AntiCheatUpgradeJump.Value) { Raise(id, name, $"{text2} jumped +{num} at once"); } value[text2] = upgradeLevel; } } internal static void Raise(string id, string name, string reason) { Flag flag = _flags.FirstOrDefault((Flag f) => f.SteamId == id && f.Reason == reason); if (flag != null) { flag.Count++; flag.Time = Time.time; return; } _flags.Add(new Flag { SteamId = id, Name = name, Reason = reason, Time = Time.time }); Plugin.Log.LogWarning((object)("[SoloHost/AntiCheat] " + name + ": " + reason)); if (Plugin.AntiCheatNotify.Value) { DevActions.Notify("Flagged " + name + ": " + reason); } } public static void ResetTracking() { _lastPos.Clear(); _lastUpgrades.Clear(); _lastPollTime = 0f; } } internal static class ArenaRules { [HarmonyPatch(typeof(Arena), "StateSet")] internal static class ArenaGameOverGuard { private static bool _holding; private static bool Prefix(States state) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 if (!Plugin.Ready || (int)state != 8) { return true; } if (!RespawnPending()) { _holding = false; return true; } if (!_holding) { _holding = true; Plugin.Log.LogInfo((object)"[SoloHost] Holding the arena open — somebody is still owed a life."); } return false; } } internal static readonly string[] Upgrades = new string[13] { "Health", "Stamina", "ExtraJump", "Launch", "MapPlayerCount", "Range", "Speed", "Strength", "Throw", "TumbleWings", "CrouchRest", "TumbleClimb", "DeathHeadBattery" }; private static Dictionary<string, Dictionary<string, int>> _snapshot; private static float _nextPass; private static readonly Dictionary<string, int> _livesLeft = new Dictionary<string, int>(); private static readonly Dictionary<string, float> _dueBack = new Dictionary<string, float>(); private static Arena _livesArena; private static int _spawnTurn; private static bool _endgame; private static bool _arrived; private const float HoldGrace = 5f; public static bool Sumo { get; private set; } public static void Begin(string spec, bool sumo) { Sumo = sumo; _endgame = false; if (_snapshot == null) { _snapshot = Capture(); } ApplyTargets(ParseTargets(spec)); Plugin.Log.LogInfo((object)("[SoloHost] Mini-game loadout applied (" + (sumo ? "sumo" : "arena") + "): " + spec)); } public static void End() { Sumo = false; ForgetLives(); if (_snapshot == null) { return; } foreach (KeyValuePair<string, Dictionary<string, int>> item in _snapshot) { Dictionary<string, int> value = item.Value; string[] upgrades = Upgrades; foreach (string text in upgrades) { int value2; int level = (value.TryGetValue(text, out value2) ? value2 : 0); SetTarget(item.Key, text, level); } } Plugin.Log.LogInfo((object)$"[SoloHost] Upgrades restored for {_snapshot.Count} player(s)."); _snapshot = null; } public static void BeginEndgame(string spec, bool sumo) { Sumo = sumo; _endgame = true; _arrived = false; _snapshot = null; ApplyTargets(ParseTargets(spec)); Plugin.Log.LogInfo((object)("[SoloHost] Endgame " + (sumo ? "sumo" : "arena") + " loadout: " + spec)); } public static void Discard() { Sumo = false; ForgetLives(); _arrived = false; _snapshot = null; } public static void Tick() { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } bool flag = SemiFunc.RunIsArena(); if (Sumo) { if (flag) { _arrived = true; } else if (_arrived) { Sumo = false; _arrived = false; } } LivesTick(); if (Sumo && flag && !(Time.time < _nextPass)) { _nextPass = Time.time + 0.5f; KeepEveryoneDown(); if (Plugin.SumoNoItems.Value) { ClearItems(); } } } private static void KeepEveryoneDown() { foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { PlayerTumble val = (((Object)(object)item != (Object)null) ? item.tumble : null); if ((Object)(object)val == (Object)null || item.isDisabled || item.deadSet) { continue; } try { if (!item.isTumbling) { val.TumbleRequest(true, false); } val.TumbleOverrideTime(3f); } catch { } } } private static void ClearItems() { int num = 0; PhysGrabObject[] array = SceneScan.All<PhysGrabObject>(2f); foreach (PhysGrabObject val in array) { if ((Object)(object)val == (Object)null) { continue; } GameObject gameObject = ((Component)val).gameObject; if ((Object)(object)gameObject.GetComponentInChildren<PlayerTumble>(true) != (Object)null || (Object)(object)gameObject.GetComponentInChildren<PlayerDeathHead>(true) != (Object)null || (Object)(object)gameObject.GetComponentInChildren<PhysGrabHinge>(true) != (Object)null || (Object)(object)gameObject.GetComponentInChildren<ItemAttributes>(true) == (Object)null) { continue; } try { PhotonView component = ((Component)val).GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient) { PhotonNetwork.Destroy(component); } else { Object.Destroy((Object)(object)gameObject); } num++; } catch { } } if (num > 0) { Plugin.Log.LogInfo((object)$"[SoloHost] Sumo: cleared {num} item(s)."); } } private static void ForgetLives() { _endgame = false; _livesArena = null; _livesLeft.Clear(); _dueBack.Clear(); } internal static int Allowance() { if (_endgame) { return 0; } if (IsRaceLevel()) { return 0; } return Mathf.Max(0, Sumo ? Plugin.SumoExtraLives.Value : Plugin.BrawlExtraLives.Value); } private static bool IsRaceLevel() { try { RunManager instance = RunManager.instance; return (Object)(object)instance != (Object)null && MiniGames.IsRace(instance.levelCurrent); } catch { return false; } } internal static int LivesLeft(PlayerAvatar p) { string text = (((Object)(object)p != (Object)null) ? SemiFunc.PlayerGetSteamID(p) : null); if (string.IsNullOrEmpty(text)) { return -1; } if (!_livesLeft.TryGetValue(text, out var value)) { return -1; } return value; } internal static bool RespawnPending() { if (Allowance() <= 0) { return false; } float time = Time.time; foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { if (!((Object)(object)item == (Object)null) && (item.isDisabled || item.deadSet)) { string text = SemiFunc.PlayerGetSteamID(item); if (!string.IsNullOrEmpty(text) && _livesLeft.TryGetValue(text, out var value) && value > 0 && _dueBack.TryGetValue(text, out var value2) && time < value2 + 5f) { return true; } } } return false; } private static void LivesTick() { //IL_0173: Unknown result type (might be due to invalid IL or missing references) Arena instance = Arena.instance; if ((Object)(object)instance == (Object)null) { if ((Object)(object)_livesArena != (Object)null) { ForgetLives(); } return; } if (instance != _livesArena) { _livesArena = instance; _livesLeft.Clear(); _dueBack.Clear(); _spawnTurn = 0; } int num = Allowance(); if (num <= 0) { return; } foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { if ((Object)(object)item == (Object)null) { continue; } string text = SemiFunc.PlayerGetSteamID(item); if (string.IsNullOrEmpty(text)) { continue; } if (!_livesLeft.ContainsKey(text)) { _livesLeft[text] = num; } if (!item.isDisabled && !item.deadSet) { _dueBack.Remove(text); } else if (_livesLeft[text] > 0) { Vector3 spot; if (!_dueBack.TryGetValue(text, out var value)) { _dueBack[text] = Time.time + Mathf.Max(0f, Plugin.ArenaRespawnDelay.Value); } else if (!(Time.time < value) && !((Object)(object)item.playerDeathHead == (Object)null) && !((Object)(object)item.playerDeathHead.physGrabObject == (Object)null) && TrySpawnSpot(out spot)) { _dueBack.Remove(text); _livesLeft[text]--; Respawn(item, spot, _livesLeft[text]); } } } } private static void Respawn(PlayerAvatar p, Vector3 where, int left) { //IL_000d: 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) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) try { PhysGrabObject physGrabObject = p.playerDeathHead.physGrabObject; physGrabObject.Teleport(where, ((Component)physGrabObject).transform.rotation); if ((Object)(object)physGrabObject.rb != (Object)null) { physGrabObject.rb.velocity = Vector3.zero; physGrabObject.rb.angularVelocity = Vector3.zero; } AntiCheat.Excuse(p); p.Revive(false); DevActions.TeleportPlayer(p, where, ((Component)p).transform.rotation); DevActions.HealPlayer(p); if (Sumo && (Object)(object)p.tumble != (Object)null) { p.tumble.TumbleRequest(true, false); p.tumble.TumbleOverrideTime(3f); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SoloHost] Couldn't put " + SemiFunc.PlayerGetName(p) + " back in: " + ex.Message)); return; } DevActions.Notify((left > 0) ? $"{SemiFunc.PlayerGetName(p)} is back in — {left} live(s) left." : (SemiFunc.PlayerGetName(p) + " is back in on their last life.")); } private static bool TrySpawnSpot(out Vector3 spot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) spot = Vector3.zero; List<ArenaPlatform> list = (from pl in Object.FindObjectsOfType<ArenaPlatform>() where (Object)(object)pl != (Object)null && (Object)(object)pl.meshRenderer != (Object)null && ((int)pl.currentState == 0 || (int)pl.currentState == 1) select pl).ToList(); if (list.Count == 0) { return false; } Bounds bounds = ((Renderer)list[Mathf.Abs(_spawnTurn++) % list.Count].meshRenderer).bounds; spot = new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).max.y + Mathf.Max(1f, Plugin.ArenaRespawnHeight.Value), ((Bounds)(ref bounds)).center.z); return true; } private static Dictionary<string, Dictionary<string, int>> Capture() { Dictionary<string, Dictionary<string, int>> dictionary = new Dictionary<string, Dictionary<string, int>>(); StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null) { return dictionary; } foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { string text = SemiFunc.PlayerGetSteamID(item); if (!string.IsNullOrEmpty(text) && !dictionary.ContainsKey(text)) { Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); string[] upgrades = Upgrades; foreach (string text2 in upgrades) { dictionary2[text2] = Level(instance, text2, text); } dictionary[text] = dictionary2; } } return dictionary; } internal static Dictionary<string, int> ParseTargets(string spec) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); string[] upgrades = Upgrades; foreach (string key in upgrades) { dictionary[key] = 0; } if (string.IsNullOrWhiteSpace(spec)) { return dictionary; } upgrades = spec.Split(','); foreach (string obj in upgrades) { string text = obj.Trim(); int num = text.IndexOf('='); if (num > 0) { string name = text.Substring(0, num).Trim(); string text2 = Upgrades.FirstOrDefault((string u) => u.Equals(name, StringComparison.OrdinalIgnoreCase)); int result; if (text2 == null) { Plugin.Log.LogWarning((object)("[SoloHost] Mini-game loadout: no upgrade called '" + name + "'.")); } else if (int.TryParse(text.Substring(num + 1).Trim(), out result)) { dictionary[text2] = Mathf.Max(0, result); } } } return dictionary; } private static void ApplyTargets(Dictionary<string, int> targets) { foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { string text = SemiFunc.PlayerGetSteamID(item); if (string.IsNullOrEmpty(text)) { continue; } foreach (KeyValuePair<string, int> target in targets) { SetTarget(text, target.Key, target.Value); } } } private static void SetTarget(string steamId, string token, int level) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) StatsManager instance = StatsManager.instance; PunManager instance2 = PunManager.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null) { return; } int num = level - Level(instance, token, steamId); if (num == 0) { return; } try { if (SemiFunc.IsMultiplayer() && (Object)(object)instance2.photonView != (Object)null) { instance2.photonView.RPC("TesterUpgradeCommandRPC", (RpcTarget)0, new object[3] { steamId, token, num }); } else { instance2.TesterUpgradeCommandRPC(steamId, token, num, default(PhotonMessageInfo)); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SoloHost] Couldn't set " + token + " for " + steamId + ": " + ex.Message)); } } private static int Level(StatsManager sm, string token, string steamId) { if (sm.dictionaryOfDictionaries == null) { return 0; } if (!sm.dictionaryOfDictionaries.TryGetValue("playerUpgrade" + token, out var value) || value == null || !value.TryGetValue(steamId, out var value2)) { return 0; } return value2; } } internal static class BackgroundRun { private static bool _reported; public static void Tick() { if (!Plugin.Ready) { return; } bool value = Plugin.RunInBackground.Value; if (Application.runInBackground != value) { Application.runInBackground = value; if (value && !_reported) { _reported = true; Plugin.Log.LogInfo((object)"[SoloHost] Keeping the game running while unfocused. Without this, alt-tabbing stops the loop that drains Photon's send queue and the connection dies with WinSock 10055."); } } } } internal static class Banishment { private sealed class State { public Vector3 Spot; public bool HasSpot; public float NextAllowed; } private const float MinDistance = 600f; private const float MaxDistance = 900f; private const float Height = 250f; private const float Drop = 120f; private const float Cooldown = 0.6f; private static readonly Dictionary<string, State> _banished = new Dictionary<string, State>(); private static float _nextCheck; public static int Count => _banished.Count; public static bool IsBanished(PlayerAvatar p) { if ((Object)(object)p != (Object)null) { return _banished.ContainsKey(SemiFunc.PlayerGetSteamID(p) ?? ""); } return false; } public static void ToggleBanish(PlayerAvatar target) { if (!DevActions.Allowed() || (Object)(object)target == (Object)null) { return; } string text = SemiFunc.PlayerGetSteamID(target); if (!string.IsNullOrEmpty(text)) { string text2 = SemiFunc.PlayerGetName(target); if (_banished.Remove(text)) { BringBack(target); DevActions.Notify(text2 + " is no longer banished."); return; } State state = new State(); _banished[text] = state; Send(target, state); DevActions.Notify("Banished " + text2 + "."); } } public static void ReleaseAll() { if (!DevActions.Allowed()) { return; } int num = 0; foreach (string item in new List<string>(_banished.Keys)) { _banished.Remove(item); PlayerAvatar val = SemiFunc.PlayerAvatarGetFromSteamID(item); if ((Object)(object)val != (Object)null) { BringBack(val); num++; } } DevActions.Notify($"Released {num} banished player(s)."); } public static List<string> BanishedIds() { return new List<string>(_banished.Keys); } public static int ReleaseAllQuietly() { int count = _banished.Count; _banished.Clear(); return count; } public static void OnLevelChanged() { foreach (State value in _banished.Values) { value.HasSpot = false; } } public static void Tick() { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) if (_banished.Count == 0 || !SemiFunc.IsMasterClientOrSingleplayer() || Time.time < _nextCheck) { return; } _nextCheck = Time.time + 0.2f; float time = Time.time; foreach (KeyValuePair<string, State> item in _banished) { PlayerAvatar val = SemiFunc.PlayerAvatarGetFromSteamID(item.Key); if (!((Object)(object)val == (Object)null) && !val.isDisabled && !val.deadSet) { State value = item.Value; if (!(time < value.NextAllowed) && (!value.HasSpot || Where(val).y < value.Spot.y - 120f || HorizontalDistance(Where(val), value.Spot) > 60f)) { Send(val, value); } } } } private static void Send(PlayerAvatar p, State s) { //IL_0027: 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_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!s.HasSpot) { s.Spot = PickSpot(); s.HasSpot = true; } AntiCheat.Excuse(p, 10f); DevActions.TeleportPlayer(p, s.Spot, ((Component)p).transform.rotation); s.NextAllowed = Time.time + 0.6f; } private static void BringBack(PlayerAvatar p) { //IL_0015: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) AntiCheat.Excuse(p); if (DevActions.TryGetTruck(out var where)) { DevActions.TeleportPlayer(p, where, ((Component)p).transform.rotation); } else if ((Object)(object)Relay.Me != (Object)null) { DevActions.TeleportPlayer(p, ((Component)Relay.Me).transform.position + Vector3.up, ((Component)p).transform.rotation); } } private static Vector3 PickSpot() { //IL_002e: 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_0069: 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_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 where; Vector3 val = (DevActions.TryGetTruck(out where) ? where : (((Object)(object)Relay.Me != (Object)null) ? ((Component)Relay.Me).transform.position : Vector3.zero)); float num = Random.Range(0f, MathF.PI * 2f); float num2 = Random.Range(600f, 900f); return val + new Vector3(Mathf.Cos(num) * num2, 250f, Mathf.Sin(num) * num2); } private static Vector3 Where(PlayerAvatar p) { //IL_002d: 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 (p.isTumbling && (Object)(object)p.tumble != (Object)null) { return ((Component)p.tumble).transform.position; } return ((Component)p).transform.position; } private static float HorizontalDistance(Vector3 a, Vector3 b) { //IL_0018: 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) a.y = 0f; b.y = 0f; return Vector3.Distance(a, b); } } internal static class BanManager { private static readonly Dictionary<string, string> _bans = new Dictionary<string, string>(); private static bool _loaded; private static float _nextPoll; private static string FilePath => Path.Combine(Paths.ConfigPath, "solohost_bans.txt"); public static IEnumerable<KeyValuePair<string, string>> Bans { get { EnsureLoaded(); return _bans; } } public static bool IsBanned(string steamId) { EnsureLoaded(); if (!string.IsNullOrEmpty(steamId)) { return _bans.ContainsKey(steamId); } return false; } public static void Ban(PlayerAvatar avatar) { if (!((Object)(object)avatar == (Object)null) && DevActions.Allowed()) { string text = SemiFunc.PlayerGetSteamID(avatar); if (!string.IsNullOrEmpty(text)) { EnsureLoaded(); _bans[text] = SemiFunc.PlayerGetName(avatar) ?? text; Save(); Kick(avatar); DevActions.Notify("Banned " + _bans[text] + " — they'll be auto-kicked if they rejoin."); } } } public static bool Remember(string steamId, string name) { if (string.IsNullOrEmpty(steamId)) { return false; } EnsureLoaded(); if (_bans.ContainsKey(steamId)) { return false; } _bans[steamId] = (string.IsNullOrEmpty(name) ? steamId : name); Save(); return true; } public static void Unban(string steamId) { EnsureLoaded(); if (_bans.Remove(steamId)) { Save(); DevActions.Notify("Un-banned " + steamId + "."); } } public static void UnbanAll() { EnsureLoaded(); int count = _bans.Count; _bans.Clear(); Save(); DevActions.Notify($"Un-banned everyone ({count})."); Plugin.Log.LogInfo((object)$"[SoloHost] Ban list cleared ({count} entries)."); } public static void Kick(PlayerAvatar avatar) { if ((Object)(object)avatar == (Object)null) { return; } try { if ((Object)(object)NetworkManager.instance != (Object)null && SemiFunc.IsMasterClient()) { NetworkManager.instance.KickPlayer(avatar); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SoloHost] Kick failed: " + ex.Message)); } } public static void PollAutoKick() { if (!Plugin.Ready || !SemiFunc.IsMultiplayer() || !SemiFunc.IsMasterClient() || Time.time < _nextPoll) { return; } _nextPoll = Time.time + 1f; EnsureLoaded(); if (_bans.Count == 0) { return; } foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { if (!((Object)(object)item == (Object)null) && !item.isLocal && IsBanned(SemiFunc.PlayerGetSteamID(item))) { Plugin.Log.LogInfo((object)("[SoloHost] Auto-kicking banned player " + SemiFunc.PlayerGetName(item) + ".")); Kick(item); } } } public static void EnsureLoaded() { if (_loaded) { return; } _loaded = true; try { if (!File.Exists(FilePath)) { return; } string[] array = File.ReadAllLines(FilePath); foreach (string text in array) { if (!string.IsNullOrWhiteSpace(text)) { string[] array2 = text.Split('\t'); string text2 = array2[0].Trim(); if (text2.Length > 0) { _bans[text2] = ((array2.Length > 1) ? array2[1] : text2); } } } Plugin.Log.LogInfo((object)$"[SoloHost] Loaded {_bans.Count} ban(s)."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SoloHost] Ban list load failed: " + ex.Message)); } } private static void Save() { try { List<string> list = new List<string>(); foreach (KeyValuePair<string, string> ban in _bans) { list.Add(ban.Key + "\t" + ban.Value); } File.WriteAllLines(FilePath, list); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SoloHost] Ban list save failed: " + ex.Message)); } } } internal static class CartDimension { private sealed class CartVault { public ValuableObject Valuable; public PhysGrabObjectImpactDetector Impact; public float Total; } private static readonly Dictionary<int, CartVault> _vaults = new Dictionary<int, CartVault>(); private static float _nextScan; public static float BankedTotalMirror; public static float TotalBanked { get { float num = 0f; foreach (CartVault value in _vaults.Values) { num += value.Total; } return num; } } public static void Reset() { _vaults.Clear(); BankedTotalMirror = 0f; } public static float BankedFor(PhysGrabCart cart) { if ((Object)(object)cart == (Object)null) { return 0f; } PhotonView component = ((Component)cart).GetComponent<PhotonView>(); if ((Object)(object)component == (Object)null) { return 0f; } if (!_vaults.TryGetValue(component.ViewID, out var value) || !((Object)(object)value.Valuable != (Object)null)) { return 0f; } return value.Total; } public static void Tick() { if (!Plugin.Ready || !Plugin.CartDimensionEnabled.Value || !SemiFunc.IsMasterClientOrSingleplayer()) { return; } foreach (KeyValuePair<int, CartVault> vault in _vaults) { PhotonView val = PhotonView.Find(vault.Key); PhysGrabCart val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<PhysGrabCart>() : null); if ((Object)(object)val2 != (Object)null) { MaintainVault(val2, vault.Key); } } if (Time.time < _nextScan) { return; } _nextScan = Time.time + 0.5f; PhysGrabCart[] array = SceneScan.All<PhysGrabCart>(5f); foreach (PhysGrabCart val3 in array) { if (!((Object)(object)val3 == (Object)null)) { PhotonView component = ((Component)val3).GetComponent<PhotonView>(); if (!((Object)(object)component == (Object)null)) { GetVault(val3, component.ViewID); AbsorbInto(val3, component.ViewID); } } } DropOrphanedVaults(); } private static void AbsorbInto(PhysGrabCart cart, int cartId) { List<PhysGrabObject> itemsInCart = cart.itemsInCart; if (itemsInCart == null || itemsInCart.Count == 0) { return; } for (int num = itemsInCart.Count - 1; num >= 0; num--) { PhysGrabObject val = itemsInCart[num]; if (!((Object)(object)val == (Object)null) && ShouldAbsorb(val)) { ValuableObject val2 = ((Component)val).GetComponent<ValuableObject>() ?? ((Component)val).GetComponentInChildren<ValuableObject>(true); if (!((Object)(object)val2 == (Object)null)) { CartVault vault = GetVault(cart, cartId); if (vault != null && !((Object)(object)val2 == (Object)(object)vault.Valuable)) { float dollarValueCurrent = val2.dollarValueCurrent; if (!(dollarValueCurrent <= 0f)) { vault.Total += dollarValueCurrent; SetValue(vault.Valuable, vault.Total); itemsInCart.RemoveAt(num); Destroy(val); Plugin.Log.LogInfo((object)($"[SoloHost/CART] Absorbed ${dollarValueCurrent:0} from '{((Object)val).name}' " + $"(cart {cartId} contents now ${vault.Total:0}).")); } } } } } } private static CartVault GetVault(PhysGrabCart cart, int cartId) { //IL_005a: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if (_vaults.TryGetValue(cartId, out var value) && (Object)(object)value.Valuable != (Object)null) { return value; } PrefabRef val = PickContentsPrefab(); if (val == null) { Plugin.Log.LogWarning((object)"[SoloHost/CART] No valuable prefab available for cart contents."); return null; } Vector3 val2 = (((Object)(object)cart.inCart != (Object)null) ? cart.inCart.position : ((Component)cart).transform.position); GameObject val3 = (((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0) ? PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)val).ResourcePath, val2, Quaternion.identity, (byte)0, (object[])null) : Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)val).Prefab, val2, Quaternion.identity)); ValuableObject val4 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent<ValuableObject>() : null); if ((Object)(object)val4 == (Object)null) { Plugin.Log.LogWarning((object)"[SoloHost/CART] Cart contents valuable failed to spawn."); return null; } ((Object)val3).name = "Cart Contents"; ItemAttributes component = val3.GetComponent<ItemAttributes>(); if ((Object)(object)component != (Object)null) { component.instanceName = "Cart Contents"; component.itemName = "Cart Contents"; } CartVault cartVault = new CartVault { Valuable = val4, Impact = val3.GetComponent<PhysGrabObjectImpactDetector>(), Total = 0f }; _vaults[cartId] = cartVault; StripInteraction(val3); SetValue(val4, 0f); Plugin.Log.LogInfo((object)$"[SoloHost/CART] Created contents valuable for cart {cartId}."); return cartVault; } private static PrefabRef PickContentsPrefab() { List<SpawnCatalog.Entry> valuables = SpawnCatalog.Valuables; string[] array = Plugin.CartContentsPrefab.Value.Split(','); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0) { continue; } foreach (SpawnCatalog.Entry item in valuables) { if (item.Display.Equals(text, StringComparison.OrdinalIgnoreCase) || ("Valuable " + item.Display).Equals(text, StringComparison.OrdinalIgnoreCase)) { object payload = item.Payload; PrefabRef val = (PrefabRef)((payload is PrefabRef) ? payload : null); if (val != null && (Object)(object)((PrefabRef<GameObject>)(object)val).Prefab != (Object)null) { Plugin.Log.LogInfo((object)("[SoloHost/CART] Cart contents will ride in '" + item.Display + "'.")); return val; } } } } Plugin.Log.LogWarning((object)"[SoloHost/CART] None of the configured contents props are in this level's presets — falling back to its cheapest valuable."); for (int num = valuables.Count - 1; num >= 0; num--) { object payload2 = valuables[num].Payload; PrefabRef val2 = (PrefabRef)((payload2 is PrefabRef) ? payload2 : null); if (val2 != null && (Object)(object)((PrefabRef<GameObject>)(object)val2).Prefab != (Object)null) { return val2; } } return null; } private static void StripInteraction(GameObject go) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) Collider[] componentsInChildren = go.GetComponentsInChildren<Collider>(true); foreach (Collider val in componentsInChildren) { if ((Object)(object)val != (Object)null) { val.enabled = false; } } Rigidbody[] componentsInChildren2 = go.GetComponentsInChildren<Rigidbody>(true); foreach (Rigidbody val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null)) { val2.velocity = Vector3.zero; val2.angularVelocity = Vector3.zero; val2.isKinematic = true; val2.useGravity = false; val2.interpolation = (RigidbodyInterpolation)0; val2.Sleep(); } } } private static void MaintainVault(PhysGrabCart cart, int cartId) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (_vaults.TryGetValue(cartId, out var value) && !((Object)(object)value.Valuable == (Object)null)) { GameObject gameObject = ((Component)value.Valuable).gameObject; gameObject.transform.position = InsideCart(cart, gameObject); StripInteraction(gameObject); if ((Object)(object)value.Impact != (Object)null) { value.Impact.indestructibleSpawnTimer = 1f; } } } private static Vector3 InsideCart(PhysGrabCart cart, GameObject contents) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Transform val = (((Object)(object)cart.inCart != (Object)null) ? cart.inCart : ((Component)cart).transform); if ((Object)(object)cart.inCart == (Object)null) { return val.position; } float num = val.localScale.y * 0.5f; float num2 = Mathf.Min(HalfHeight(contents) + 0.02f, num); return val.position - val.up * (num - num2); } private static Bounds? WorldBounds(GameObject go) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)go == (Object)null) { return null; } Renderer[] componentsInChildren = go.GetComponentsInChildren<Renderer>(true); if (componentsInChildren.Length == 0) { return null; } Bounds bounds = componentsInChildren[0].bounds; for (int i = 1; i < componentsInChildren.Length; i++) { ((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds); } return bounds; } private static float HalfHeight(GameObject go) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)go == (Object)null) { return 0f; } Renderer[] componentsInChildren = go.GetComponentsInChildren<Renderer>(true); if (componentsInChildren.Length == 0) { return 0f; } Bounds bounds = componentsInChildren[0].bounds; for (int i = 1; i < componentsInChildren.Length; i++) { ((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds); } return ((Bounds)(ref bounds)).extents.y; } private static void DropOrphanedVaults() { foreach (int item in _vaults.Keys.ToList()) { PhotonView val = PhotonView.Find(item); if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)null) { continue; } if ((Object)(object)_vaults[item].Valuable != (Object)null) { GameObject gameObject = ((Component)_vaults[item].Valuable).gameObject; Collider[] componentsInChildren = gameObject.GetComponentsInChildren<Collider>(true); foreach (Collider val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null) { val2.enabled = true; } } Rigidbody component = gameObject.GetComponent<Rigidbody>(); if ((Object)(object)component != (Object)null) { component.isKinematic = false; } Plugin.Log.LogInfo((object)$"[SoloHost/CART] Cart {item} is gone — its contents are loose."); } _vaults.Remove(item); } } private static bool ShouldAbsorb(PhysGrabObject pgo) { return LootFilter.IsLooseCargo(pgo); } private static void SetValue(ValuableObject vo, float total) { if ((Object)(object)vo == (Object)null) { return; } vo.dollarValueOriginal = total; vo.dollarValueCurrent = total; vo.dollarValueSet = true; try { PhotonView component = ((Component)vo).GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient) { component.RPC("DollarValueSetRPC", (RpcTarget)1, new object[1] { total }); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SoloHost/CART] Value sync failed: " + ex.Message)); } } private static void Destroy(PhysGrabObject pgo) { try { PhotonView component = ((Component)pgo).GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient) { PhotonNetwork.Destroy(component); } else { Object.Destroy((Object)(object)((Component)pgo).gameObject); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SoloHost/CART] Absorb destroy failed: " + ex.Message)); } } } internal static class ClownExtraction { private sealed class ClownState { public int PointViewId; public ExtractionPoint Point; public GameObject ClownGo; public PhysGrabObject Pgo; public Rigidbody Rb; public bool WasGrabbed; public bool Released; public Vector3 LastPos; public Quaternion LastRot; public float RespawnAt; public float NextReport; } internal const string Marker = "SOLOHOST_CLOWN"; private static readonly Dictionary<int, ClownState> _byPoint = new Dictionary<int, ClownState>(); private static readonly HashSet<ClownTrap> _disarmed = new HashSet<ClownTrap>(); private static bool Enabled { get { if (Plugin.Ready) { return Plugin.ClownExtractionEnabled.Value; } return false; } } internal static Vector3 SavedOffset => new Vector3(Plugin.ClownSpotX.Value, Plugin.ClownSpotY.Value, Plugin.ClownSpotZ.Value); internal static Vector3 SavedEuler => new Vector3(Plugin.ClownSpotRotX.Value, Plugin.ClownSpotRotY.Value, Plugin.ClownSpotRotZ.Value); public static string SpotSummary { get { if (Plugin.ClownSpotSaved.Value) { if (!IsDefaultSpot) { return Loc.F("your spot ({0}, {1}, {2})", Plugin.ClownSpotX.Value.ToString("0.00"), Plugin.ClownSpotY.Value.ToString("0.00"), Plugin.ClownSpotZ.Value.ToString("0.00")); } return Loc.T("the default spot"); } return Loc.T("placed automatically"); } } public static bool IsDefaultSpot { get { if (Mathf.Approximately(Plugin.ClownSpotX.Value, -2.114112f) && Mathf.Approximately(Plugin.ClownSpotY.Value, -0.02088949f)) { return Mathf.Approximately(Plugin.ClownSpotZ.Value, 1.469957f); } return false; } } public static void OnPointActivated(ExtractionPoint ep) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0172: 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_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || (Object)(object)ep == (Object)null || ep.isShop || !SemiFunc.IsMasterClientOrSingleplayer() || (Object)(object)ep.photonView == (Object)null) { return; } int viewID = ep.photonView.ViewID; if (_byPoint.ContainsKey(viewID)) { return; } if ((Object)(object)SpawnCatalog.TryGetClownPrefab(out var _) == (Object)null) { Plugin.Log.LogWarning((object)"[SoloHost] Clown finish: no clown prefab found in the item/valuable pools."); return; } _byPoint[viewID] = new ClownState { PointViewId = viewID, Point = ep }; Quaternion val = ((Component)ep).transform.rotation * Quaternion.Euler(SavedEuler); if (!Plugin.ClownSpotSaved.Value) { val *= Quaternion.Euler(0f, Plugin.ClownYawOffset.Value, 0f); } Vector3 val2; string text; if (Plugin.ClownSpotSaved.Value) { val2 = ((Component)ep).transform.TransformPoint(SavedOffset); text = "saved"; } else { Vector3 val3 = (((Object)(object)ep.safetySpawn != (Object)null) ? ep.safetySpawn.position : ((Component)ep).transform.position) + Vector3.up * 1f; RaycastHit val4 = default(RaycastHit); if (Physics.Raycast(val3, Vector3.down, ref val4, 12f, -5, (QueryTriggerInteraction)1)) { val2 = ((RaycastHit)(ref val4)).point + Vector3.up * 0.3f; text = "floor"; } else { val2 = val3; text = "device-fallback"; } } GameObject val5 = Instantiate(viewID, val2, val); Plugin.Log.LogInfo((object)string.Format("[SoloHost] Clown finish: spawned '{0}' at {1} (anchor={2}) for point {3}.", ((Object)(object)val5 != (Object)null) ? ((Object)val5).name : "null", val2, text, viewID)); } private static GameObject Instantiate(int pointId, Vector3 pos, Quaternion rot) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) string resourcePath; GameObject val = SpawnCatalog.TryGetClownPrefab(out resourcePath); if ((Object)(object)val == (Object)null) { return null; } GameObject val2 = ((!((Object)(object)GameManager.instance != (Object)null) || GameManager.instance.gameMode == 0) ? Object.Instantiate<GameObject>(val, pos, rot) : PhotonNetwork.InstantiateRoomObject(resourcePath, pos, rot, (byte)0, new object[2] { "SOLOHOST_CLOWN", pointId })); Register(val2, pointId); if (_byPoint.TryGetValue(pointId, out var value)) { value.LastPos = pos; value.LastRot = rot; } return val2; } public static void RegisterFromTrap(ClownTrap trap) { if (!((Object)(object)trap == (Object)null)) { PhotonView component = ((Component)trap).GetComponent<PhotonView>(); object[] array = (((Object)(object)component != (Object)null) ? component.InstantiationData : null); if (array != null && array.Length >= 2 && array[0] is string text && !(text != "SOLOHOST_CLOWN")) { int pointViewId = ((array[1] is int num) ? num : (-1)); Register(((Component)trap).gameObject, pointViewId); } } } private static void Register(GameObject clownGo, int pointViewId) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)clownGo == (Object)null) { return; } ClownTrap componentInChildren = clownGo.GetComponentInChildren<ClownTrap>(true); PhysGrabObject pgo = clownGo.GetComponent<PhysGrabObject>() ?? clownGo.GetComponentInChildren<PhysGrabObject>(true); Rigidbody rb = clownGo.GetComponent<Rigidbody>() ?? clownGo.GetComponentInChildren<Rigidbody>(true); Disarm(componentInChildren); Pin(rb); ZeroValue(clownGo); if (!_byPoint.TryGetValue(pointViewId, out var value)) { value = new ClownState { PointViewId = pointViewId }; _byPoint[pointViewId] = value; } value.ClownGo = clownGo; value.Pgo = pgo; value.Rb = rb; if (value.LastPos == Vector3.zero) { value.LastPos = clownGo.transform.position; value.LastRot = clownGo.transform.rotation; } if ((Object)(object)value.Point == (Object)null && pointViewId >= 0) { PhotonView val = PhotonView.Find(pointViewId); if ((Object)(object)val != (Object)null) { value.Point = ((Component)val).GetComponent<ExtractionPoint>(); } } } private static void Disarm(ClownTrap trap) { if (!((Object)(object)trap == (Object)null)) { _disarmed.Add(trap); ((Trap)trap).trapActive = false; ((Trap)trap).trapTriggered = true; ((Behaviour)trap).enabled = false; } } private static void Pin(Rigidbody rb) { if ((Object)(object)rb != (Object)null) { rb.constraints = (RigidbodyConstraints)126; } } private static void Unpin(Rigidbody rb) { if ((Object)(object)rb != (Object)null) { rb.constraints = (RigidbodyConstraints)0; } } private static void ZeroValue(GameObject go) { ValuableObject val = go.GetComponent<ValuableObject>() ?? go.GetComponentInChildren<ValuableObject>(true); if ((Object)(object)val == (Object)null) { return; } val.dollarValueOverride = 0; val.dollarValueOriginal = 0f; val.dollarValueCurrent = 0f; val.dollarValueSet = true; try { PhotonView component = ((Component)val).GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient) { component.RPC("DollarValueSetRPC", (RpcTarget)1, new object[1] { 0f }); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SoloHost] Clown value zero-sync failed: " + ex.Message)); } } public static bool IsDisarmed(ClownTrap trap) { if ((Object)(object)trap != (Object)null) { return _disarmed.Contains(trap); } return false; } public static bool TryGetCurrentOffset(out Vector3 local, out float distance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) local = Vector3.zero; distance = 0f; foreach (ClownState value in _byPoint.Values) { if (!((Object)(object)value.ClownGo == (Object)null) && !((Object)(object)value.Point == (Object)null)) { Vector3 position = value.ClownGo.transform.position; local = ((Component)value.Point).transform.InverseTransformPoint(position); distance = Vector3.Distance(position, ((Component)value.Point).transform.position); return true; } } return false; } public static void SaveCurrentSpot() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0138: 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_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) ClownState clownState = null; foreach (ClownState value in _byPoint.Values) { if ((Object)(object)value.ClownGo != (Object)null && (Object)(object)value.Point != (Object)null) { clownState = value; break; } } if (clownState == null) { DevActions.Notify("No clown out right now — activate an extraction first."); return; } Transform transform = ((Component)clownState.Point).transform; Vector3 val = transform.InverseTransformPoint(clownState.ClownGo.transform.position); Quaternion val2 = Quaternion.Inverse(transform.rotation) * clownState.ClownGo.transform.rotation; Vector3 eulerAngles = ((Quaternion)(ref val2)).eulerAngles; Plugin.ClownSpotX.Value = val.x; Plugin.ClownSpotY.Value = val.y; Plugin.ClownSpotZ.Value = val.z; Plugin.ClownSpotRotX.Value = eulerAngles.x; Plugin.ClownSpotRotY.Value = eulerAngles.y; Plugin.ClownSpotRotZ.Value = eulerAngles.z; Plugin.ClownSpotSaved.Value = true; DevActions.Notify($"Saved clown spot ({val.x:0.00}, {val.y:0.00}, {val.z:0.00}) " + $"facing {eulerAngles.y:0}° — future clowns spawn here."); Plugin.Log.LogInfo((object)$"[SoloHost] Clown spot saved: pos {val}, euler {eulerAngles}"); } internal static void AdoptDefaultSpot() { if (!Plugin.ClownDefaultApplied.Value) { Plugin.ClownDefaultApplied.Value = true; if (Plugin.ClownSpotSaved.Value && (Plugin.ClownSpotX.Value != 0f || Plugin.ClownSpotY.Value != 0f || Plugin.ClownSpotZ.Value != 0f)) { Plugin.Log.LogInfo((object)"[SoloHost] Keeping your saved clown spot; the shipped one is not applied."); return; } Plugin.ClownSpotX.Value = -2.114112f; Plugin.ClownSpotY.Value = -0.02088949f; Plugin.ClownSpotZ.Value = 1.469957f; Plugin.ClownSpotRotX.Value = 0f; Plugin.ClownSpotRotY.Value = 0f; Plugin.ClownSpotRotZ.Value = 0f; Plugin.ClownSpotSaved.Value = true; Plugin.Log.LogInfo((object)"[SoloHost] Using the clown spot SoloHost ships with. Save your own from the menu to replace it, or clear it for automatic placement."); } } public static void UseDefaultSpot() { if (DevActions.Allowed()) { Plugin.ClownSpotX.Value = -2.114112f; Plugin.ClownSpotY.Value = -0.02088949f; Plugin.ClownSpotZ.Value = 1.469957f; Plugin.ClownSpotRotX.Value = 0f; Plugin.ClownSpotRotY.Value = 0f; Plugin.ClownSpotRotZ.Value = 0f; Plugin.ClownSpotSaved.Value = true; DevActions.Notify("Clown spot back to the default."); } } public static void ClearSavedSpot() { Plugin.ClownSpotSaved.Value = false; DevActions.Notify("Cleared saved clown spot — back to auto-placement."); } public static bool ShouldBlockCompletion(ExtractionPoint ep) { if (!Enabled || (Object)(object)ep == (Object)null || (Object)(object)ep.photonView == (Object)null) { return false; } if (!_byPoint.TryGetValue(ep.photonView.ViewID, out var value)) { return false; } if (value.Released) { return false; } return (Object)(object)value.ClownGo != (Object)null; } public static void Tick() { //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || _byPoint.Count == 0) { return; } RoundDirector instance = RoundDirector.instance; bool flag = SemiFunc.IsMasterClientOrSingleplayer(); if ((Object)(object)instance != (Object)null) { if (instance.extractionPoints <= 0) { _ = instance.haulGoal; } else { _ = instance.haulGoal / instance.extractionPoints; } } foreach (ClownState value in _byPoint.Values) { if ((Object)(object)value.ClownGo == (Object)null) { if (!value.Released && flag && !((Object)(object)value.Point == (Object)null)) { if (value.RespawnAt <= 0f) { value.RespawnAt = Time.time + 1.5f; Plugin.Log.LogInfo((object)$"[SoloHost] Clown for point {value.PointViewId} was destroyed — respawning."); } else if (!(Time.time < value.RespawnAt)) { value.RespawnAt = 0f; value.WasGrabbed = false; Instantiate(value.PointViewId, value.LastPos, value.LastRot); } } } else { if ((Object)(object)value.Pgo == (Object)null) { continue; } bool flag2 = value.Pgo.grabbed || value.Pgo.grabbedLocal || (value.Pgo.playerGrabbing != null && value.Pgo.playerGrabbing.Count > 0); if (Plugin.ClownLocked.Value) { Pin(value.Rb); if (flag) { value.ClownGo.transform.SetPositionAndRotation(value.LastPos, value.LastRot); } value.WasGrabbed = flag2; } else { if (!flag2) { value.LastPos = value.ClownGo.transform.position; value.LastRot = value.ClownGo.transform.rotation; } if (flag2 && !value.WasGrabbed) { Unpin(value.Rb); } else if (!flag2 && value.WasGrabbed) { Pin(value.Rb); } value.WasGrabbed = flag2; } if (!flag || value.Released || (Object)(object)value.Point == (Object)null || (Object)(object)instance == (Object)null) { continue; } if (Time.time >= value.NextReport) { value.NextReport = Time.time + 5f; Plugin.Log.LogInfo((object)($"[SoloHost] Extraction {value.PointViewId} held by its clown: " + $"grabbed={flag2} haul={value.Point.haulCurrent}/{value.Point.haulGoal} " + $"goalFetched={value.Point.haulGoalFetched} " + $"requiresEnough={Plugin.ClownRequiresEnough.Value}")); } if (flag2) { ExtractionPoint point = value.Point; if (!Plugin.ClownRequiresEnough.Value || (point.haulGoalFetched && point.haulGoal > 0 && point.haulCurrent >= point.haulGoal)) { value.Released = true; DespawnClown(value); point.StateSet((State)3); Plugin.Log.LogInfo((object)("[SoloHost] Clown grabbed with enough value " + $"({point.haulCurrent}/{point.haulGoal}) — finishing extraction {value.PointViewId}.")); } } } } } public static void OnPointFailed(ExtractionPoint ep) { //IL_006f: 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) if (Enabled && !((Object)(object)ep == (Object)null) && !((Object)(object)ep.photonView == (Object)null) && SemiFunc.IsMasterClientOrSingleplayer() && _byPoint.TryGetValue(ep.photonView.ViewID, out var value)) { value.Released = false; value.WasGrabbed = false; if ((Object)(object)value.ClownGo == (Object)null) { value.RespawnAt = 0f; Instantiate(value.PointViewId, value.LastPos, value.LastRot); Plugin.Log.LogInfo((object)$"[SoloHost] Extraction {value.PointViewId} failed — clown restored."); } } } private static void DespawnClown(ClownState st) { if (st == null || (Object)(object)st.ClownGo == (Object)null) { return; } try { if ((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0) { PhotonView component = st.ClownGo.GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && PhotonNetwork.IsMasterClient) { PhotonNetwork.Destroy(component); } else { Object.Destroy((Object)(object)st.ClownGo); } } else { Object.Destroy((Object)(object)st.ClownGo); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SoloHost] Clown despawn failed: " + ex.Message)); } st.ClownGo = null; st.Rb = null; st.Pgo = null; } public static void ForceRelease(ExtractionPoint ep) { if (!((Object)(object)ep == (Object)null) && !((Object)(object)ep.photonView == (Object)null) && _byPoint.TryGetValue(ep.photonView.ViewID, out var value) && !value.Released) { value.Released = true; DespawnClown(value); } } public static void Reset() { _byPoint.Clear(); _disarmed.Clear(); } } internal static class Cosmetics { private static readonly Dictionary<string, Color> Palette = new Dictionary<string, Color>(StringComparer.OrdinalIgnoreCase) { { "Default", Color.clear }, { "White", Color.white }, { "Red", new Color(1f, 0.25f, 0.2f) }, { "Green", new Color(0.35f, 1f, 0.4f) }, { "Blue", new Color(0.35f, 0.6f, 1f) }, { "Purple", new Color(0.7f, 0.4f, 1f) }, { "Orange", new Color(1f, 0.6f, 0.15f) }, { "Pink", new Color(1f, 0.45f, 0.8f) }, { "Cyan", new Color(0.3f, 1f, 1f) }, { "Yellow", new Color(1f, 0.95f, 0.3f) } }; private static Color? _originalFlashlight; private static float _nextEyeRefresh; internal static IEnumerable<string> ColorNames => Palette.Keys; public static void Tick() { if (Plugin.Ready) { ApplyFlashlight(); ApplyEyes(); ApplyWings(); } } public static void LateTick() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Ready && Plugin.ThirdPerson.Value) { PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.playerAvatarVisuals != (Object)null) { instance.playerAvatarVisuals.showSelfOverrideTimer = 0.5f; } Camera main = Camera.main; if (!((Object)(object)main == (Object)null)) { Transform transform = ((Component)main).transform; transform.position += -transform.forward * Plugin.ThirdPersonDistance.Value + Vector3.up * Plugin.ThirdPersonHeight.Value; } } } private static void ApplyFlashlight() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) FlashlightController instance = FlashlightController.Instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.spotlight == (Object)null)) { if (!_originalFlashlight.HasValue) { _originalFlashlight = instance.spotlight.color; } if (!Palette.TryGetValue(Plugin.FlashlightColor.Value ?? "Default", out var value) || value == Color.clear) { instance.spotlight.color = _originalFlashlight.Value; } else { instance.spotlight.color = value; } } } private static void ApplyEyes() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) string value = Plugin.EyeEffect.Value; if (!string.IsNullOrEmpty(value) && !value.Equals("None", StringComparison.OrdinalIgnoreCase) && !(Time.time < _nextEyeRefresh)) { _nextEyeRefresh = Time.time + 1f; PlayerAvatar instance = PlayerAvatar.instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.playerHealth == (Object)null) && Enum.TryParse<EyeOverrideState>(value, ignoreCase: true, out EyeOverrideState result)) { instance.playerHealth.EyeMaterialOverride(result, 1.5f, 0); } } } private static void ApplyWings() { if (!Plugin.WingsAlwaysShow.Value) { return; } PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance == (Object)null) { return; } ItemUpgradePlayerTumbleWingsLogic componentInChildren = ((Component)instance).GetComponentInChildren<ItemUpgradePlayerTumbleWingsLogic>(true); if (!((Object)(object)componentInChildren == (Object)null) && !((Object)(object)componentInChildren.transformWings == (Object)null)) { if (!((Component)componentInChildren.transformWings).gameObject.activeSelf) { ((Component)componentInChildren.transformWings).gameObject.SetActive(true); } if ((Object)(object)componentInChildren.lightWings != (Object)null && !((Behaviour)componentInChildren.lightWings).enabled) { ((Behaviour)componentInChildren.lightWings).enabled = true; } } } public static void Restore() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) FlashlightController instance = FlashlightController.Instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.spotlight != (Object)null && _originalFlashlight.HasValue) { instance.spotlight.color = _originalFlashlight.Value; } } } internal static class DevActions { private sealed class PendingCompletion { public ExtractionPoint Point; public int GoalPer; public float GiveUpAt; } private sealed class FillPacker { private readonly Vector3 _origin; private readonly float _footX; private readonly float _footZ; private readonly float _height; private readonly float _gap; private readonly int _cap; private float _y; private int _placed; public bool Full { get { if (_placed < _cap) { return _y >= _height; } return true; } } public FillPacker(Vector3 origin, float footX, float footZ, float height, float gap, int cap) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) _origin = origin; _footX = footX; _footZ = footZ; _height = height; _gap = gap; _cap = cap; } public int Pack(PrefabRef pf, int wanted, Action<Vector3, Quaternion> place) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) if (pf == null || (Object)(object)((PrefabRef<GameObject>)(object)pf).Prefab == (Object)null || wanted <= 0 || Full) { return 0; } Vector3 val = PrefabSize(((PrefabRef<GameObject>)(object)pf).Prefab); float num = Mathf.Max(0.05f, val.x) + _gap; float num2 = Mathf.Max(0.05f, val.z) + _gap; float num3 = Mathf.Max(0.05f, val.y) + _gap; int num4 = Mathf.FloorToInt(_footX / num); int num5 = Mathf.FloorToInt(_footZ / num2); int num6 = Mathf.FloorToInt(_footX / num2); int num7 = Mathf.FloorToInt(_footZ / num); bool flag = num6 * num7 > num4 * num5; int num8 = Mathf.Max(1, flag ? num6 : num4); int num9 = Mathf.Max(1, flag ? num7 : num5); float num10 = (flag ? num2 : num); float num11 = (flag ? num : num2); Quaternion arg = (flag ? Quaternion.Euler(0f, 90f, 0f) : Quaternion.identity); int num12 = 0; Vector3 arg2 = default(Vector3); while (num12 < wanted && !Full && !(_y + num3 > _height)) { for (int i = 0; i < num9; i++) { if (num12 >= wanted) { break; } if (_placed >= _cap) { break; } for (int j = 0; j < num8; j++) { if (num12 >= wanted) { break; } if (_placed >= _cap) { break; } ((Vector3)(ref arg2))..ctor(_origin.x + (float)j * num10 + num10 * 0.5f, _origin.y + _y + num3 * 0.5f, _origin.z + (float)i * num11 + num11 * 0.5f); place(arg2, arg); num12++; _placed++; } } _y += num3; } return num12; } } private sealed class QuietScope : IDisposable { public void Dispose() { _quiet--; } } public static readonly HashSet<string> InfiniteEnergy = new HashSet<string>(); private static readonly List<PhysGrabHinge> _doorQueue = new List<PhysGrabHinge>(); private static int _doorsTornOut; private const int DoorsPerFrame = 4; private static readonly List<EnemyParent> _tempEnemies = new List<EnemyParent>(); private static PendingCompletion _pending; private static readonly List<GameObject> _filled = new List<GameObject>(); private static readonly Dictionary<string, int> _sprintGranted = new Dictionary<string, int>(); public static readonly HashSet<string> ProtectedPlayers = new HashSet<string>(); private static int _quiet; internal static bool OneShotEnabled { get; private set; } public static int DoorsPending => _doorQueue.Count; public static int FilledCount => _filled.Count((GameObject g) => (Object)(object)g != (Object)null); public static bool Allowed(bool notify = true) { if (!HostGate.Active) { if (notify) { Notify("SoloHost is host-only — you aren't hosting this lobby."); } return false; } if (HostLockout.LockedByHost) { if (notify) { Notify("The host has disabled this mod's shared features here."); } return false; } try { if ((Object)(object)RunManager.instance != (Object)null && SemiFunc.IsMasterClientOrSingleplayer()) { return true; } } catch { } if (notify) { Notify("Host / singleplayer only."); } return false; } public static Vector3 FacingFlat() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) Vector3 lookForward = Relay.LookForward; lookForward.y = 0f; if (!(((Vector3)(ref lookForward)).sqrMagnitude < 0.01f)) { return ((Vector3)(ref lookForward)).normalized; } return Vector3.forward; } public static Vector3 InFrontOfPlayer(float dist = 2f) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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) Vector3 mePosition = Relay.MePosition; Vector3 val = Relay.LookForward; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.01f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); Vector3 val2 = mePosition + val * dist; RaycastHit val3 = default(RaycastHit); if (Physics.Raycast(val2 + Vector3.up * 1.5f, Vector3.down, ref val3, 8f, -5, (QueryTriggerInteraction)1)) { val2 = ((RaycastHit)(ref val3)).point; } return val2 + Vector3.up * 0.3f; } public static Quaternion SpawnRotation() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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_0020: Unknown result type (might be due to invalid IL or missing references) Vector3 lookForward = Relay.LookForward; lookForward.y = 0f; if (((Vector3)(ref lookForward)).sqrMagnitude < 0.01f) { return Quaternion.identity; } return Quaternion.LookRotation(((Vector3)(ref lookForward)).normalized, Vector3.up); } public static void SpawnEnemy(EnemySetup setup) { if (Allowed() && !((Object)(object)setup == (Object)null) && setup.spawnObjects != null) { int num = Mathf.Clamp(Plugin.SpawnCount.Value, 1, 100); for (int i = 0; i < num; i++) { SpawnEnemyOnce(setup, i, num); } Notify(Plugin.SpawnEnemiesPermanent.Value ? $"Spawned {num}x '{((Object)setup).name}' — permanent (respawns when killed)." : $"Spawned {num}x '{((Object)setup).name}' — temporary (stays until killed, then gone)."); } } private static Vector3 BatchOffset(int index, int total) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) if (total <= 1) { return Vector3.zero; } float num = (float)index / (float)total * MathF.PI * 2f; float num2 = 0.8f + (float)index * 0.05f; return new Vector3(Mathf.Cos(num), 0f, Mathf.Sin(num)) * num2; } private static void SpawnEnemyOnce(EnemySetup setup, int index, int total) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) Vector3 val = InFrontOfPlayer() + BatchOffset(index, total); int num = 0; foreach (PrefabRef spawnObject in setup.spawnObjects) { if (spawnObject == null) { continue; } GameObject val2 = (((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0) ? PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)spawnObject).ResourcePath, val, SpawnRotation(), (byte)0, (object[])null) : Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)spawnObject).Prefab, val, SpawnRotation())); EnemyParent component = val2.GetComponent<EnemyParent>(); if ((Object)(object)component != (Object)null) { component.SetupDone = true; Enemy componentInChildren = val2.GetComponentInChildren<Enemy>(); if (componentInChildren != null) { componentInChildren.EnemyTeleported(val); } component.firstSpawnPointUsed = true; if (!Plugin.SpawnEnemiesPermanent.Value) { TrackTempSpawn(component); } } num++; } } public static void SpawnItem(Item item) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) if (!Allowed() || (Object)(object)item == (Object)null || item.prefab == null) { return; } int num = Mathf.Clamp(Plugin.SpawnCount.Value, 1, 100); for (int i = 0; i < num; i++) { Vector3 val = InFrontOfPlayer() + BatchOffset(i, num); if ((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0) { PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)item.prefab).ResourcePath, val, SpawnRotation(), (byte)0, (object[])null); } else { Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)item.prefab).Prefab, val, SpawnRotation()); } } Notify($"Spawned {num}x '{((Object)item).name}' in front of you."); } public static void SpawnValuable(PrefabRef pf) { //IL_002b: 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) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_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) if (!Allowed() || pf == null) { return; } int num = Mathf.Clamp(Plugin.SpawnCount.Value, 1, 100); for (int i = 0; i < num; i++) { Vector3 val = InFrontOfPlayer() + BatchOffset(i, num); GameObject val2 = (((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0) ? PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)pf).ResourcePath, val, SpawnRotation(), (byte)0, (object[])null) : Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)pf).Prefab, val, SpawnRotation())); ValuableObject val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<ValuableObject>() : null); if ((Object)(object)val3 != (Object)null) { val3.DollarValueSetLogic(); } } Notify($"Spawned {num}x valuable in front of you."); } public static void MaxUpgrades(int amount) { if (!Allowed()) { return; } if ((Object)(object)Relay.Requester != (Object)null) { PlayerAvatar requester = Relay.Requester; int num = 0; using (Quiet()) { string[] upgradeNames = SharedUpgrades.UpgradeNames; foreach (string upgradeName in upgradeNames) { GiveUpgrade(requester, upgradeName, amount); num++; } } Notify($"Gave {SemiFunc.PlayerGetName(requester)} +{amount} of {num} upgrades."); return; } StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null || instance.dictionaryOfDictionaries == null) { Notify("No StatsManager yet."); return; } List<string> list = (from k in instance.dictionaryOfDictionaries.Keys where k.StartsWith("playerUpgrade") select k.Substring("playerUpgrade".Length)).ToList(); AntiCheat.ExcuseEveryone(); int num2 = list.Count((string n) => RunCommand($"upgrade {n} {amount}")); Notify($"Gave +{amount} of {num2}/{list.Count} upgrades."); } public static void SetLevel(int n) { if (Allowed() && RunCommand($"levelnum {n}")) { Notify($"Jumping to level {n}."); } } public static void GiveCash(long n) { if (!Allowed()) { return; } if (Plugin.BigMoney.Value) { Wealth.SetCurrency(n); Notify("Balance set to $" + Wealth.Format(n) + "K."); return; } int num = (int)((n > int.MaxValue) ? int.MaxValue : ((n >= 0) ? n : 0)); if (RunCommand($"cash {num}")) { Notify($"Cash set to {num}."); } } public static void ToggleGodMode() { if (Allowed()) { if ((Object)(object)Relay.Requester != (Object)null) { SetPlayerGod(Relay.Requester, !IsPlayerGod(Relay.Requester)); } else { RunCommand("godmode"); } } } public static void ToggleInfiniteEnergy() { if (Allowed()) { if ((Object)(object)Relay.Requester != (Object)null) { ToggleInfiniteSprint(Relay.Requester); } else { RunCommand("energy"); } } } public static void Heal() { if (Allowed()) { PlayerAvatar me = Relay.Me; if ((Object)(object)me != (Object)null && (Object)(object)me.playerHealth != (Object)null) { HealPlayer(me); } else { RunCommand("health 100"); } } } public static void HealPlayer(PlayerAvatar target, int amount = 0) { if (Allowed() && !((Object)(object)target == (Object)null) && !((Object)(object)target.playerHealth == (Object)null)) { AntiCheat.Excuse(target); int num = ((amount > 0) ? amount : Mathf.Max(1, target.playerHealth.maxHealth)); if (target.isLocal || !SemiFunc.IsMultiplayer()) { target.playerHealth.Heal(num, true); } else { target.playerHealth.HealOther(num, true); } Notify((amount > 0) ? $"Healed {SemiFunc.PlayerGetName(target)} +{num}." : $"Healed {SemiFunc.PlayerGetName(target)} to full ({num})."); } } public static void HealEveryone(int amount = 0) { if (!Allowed()) { return; } foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { if ((Object)(object)item != (Object)null) { HealPlayer(item, amount); } } } public static void RevivePlayer(PlayerAvatar target) { if (Allowed() && !((Object)(object)target == (Object)null)) { if (!target.isDisabled && !target.deadSet) { Notify(SemiFunc.PlayerGetName(target) + " isn't dead."); return; } AntiCheat.Excuse(target); target.Revive(false); Notify("Revived " + SemiFunc.PlayerGetName(target) + "."); } } public static void Revive() { if (!Allowed()) { return; } int num = 0; foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { if (!((Object)(object)item == (Object)null) && (item.isDisabled || item.deadSet)) { AntiCheat.Excuse(item); item.Revive(false); num++; } } Notify((num > 0) ? $"Revived {num} player(s)." : "Nobody needs reviving."); } public static void ToggleOneShot() { if (OneShotEnabled || Allowed()) { OneShotEnabled = !OneShotEnabled; Notify(OneShotEnabled ? "One-shot: ON — any hit kills." : "One-shot: OFF."); } } public static void SpawnOneShotGun() { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) if (!Allowed()) { return; } StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null || instance.itemDictionary == null) { Notify("No item dictionary yet."); return; } string want = Plugin.GunItemName.Value.ToLowerInvariant(); Item val = ((IEnumerable<Item>)instance.itemDictionary.Values).FirstOrDefault((Func<Item, bool>)((Item x) => (Object)(object)x != (Object)null && ((Object)x).name.ToLowerInvariant().Contains(want) && x.prefab != null && (Object)(object)((PrefabRef<GameObject>)(object)x.prefab).Prefab != (Object)null && (Object)(object)((PrefabRef<GameObject>)(object)x.prefab).Prefab.GetComponentInChildren<ItemGun>(true) != (Object)null)) ?? ((IEnumerable<Item>)instance.itemDictionary.Values).FirstOrDefault((Func<Item, bool>)((Item x) => (Object)(object)x != (Object)null && ((Object)x).name.ToLowerInvariant().Contains(want))); if ((Object)(object)val == (Object)null || val.prefab == null) { Notify("No item matching '" + want + "'. Try a different Gun/GunItemName."); return; } Vector3 val2 = InFrontOfPlayer(); RenameGun(((Object)(object)GameManager.instance != (Object)null && GameManager.instance.gameMode != 0) ? PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)val.prefab).ResourcePath, val2, SpawnRotation(), (byte)0, (object[])null) : Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)val.prefab).Prefab, val2, SpawnRotation()), val); if (!OneShotEnabled) { ToggleOneShot(); } Notify("Spawned '" + Plugin.GunDisplayName.Value + "'. One-shot is ON."); } private static void RenameGun(GameObject go, Item item) { if ((Object)(object)go == (Object)null) { return; } string value = Plugin.GunDisplayName.Value; try { ItemAttributes component = go.GetComponent<ItemAttributes>(); if ((Object)(object)component != (Object)null) { component.itemName = value; component.instanceName = value; } if ((Object)(object)item != (Object)null) { item.itemName = value; item.itemNameLocalized = null; } ((Object)go).name = "SoloHost " + value; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[So