using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using QueenOfTheLosers.Modifiers; using REPOLib.Modules; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("QueenOfTheLosers")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+57482133f365a4a9ddb7a08bf665b8b072be83cd")] [assembly: AssemblyProduct("QueenOfTheLosers")] [assembly: AssemblyTitle("QueenOfTheLosers")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace QueenOfTheLosers { public struct PlayerID : IEquatable { private static PlayerID local; private string steamID; public static PlayerID Local { get { ref string reference = ref local.steamID; if (reference == null) { reference = SemiFunc.PlayerAvatarLocal()?.steamID; } return local; } } public static bool LocalSteamIDFound => Local.HasSteamID; public string SteamID => steamID ?? (steamID = Local.steamID); public PlayerAvatar? Avatar => ((Object)(object)GameDirector.instance == (Object)null) ? null : SemiFunc.PlayerAvatarGetFromSteamID(SteamID); public bool IsLocal => SteamID == Local.SteamID; public bool HasSteamID => SteamID != null; [return: NotNullIfNotNull("avatar")] public static PlayerID? FromAvatar(PlayerAvatar avatar) { PlayerID? result; if (!((Object)(object)avatar == (Object)null)) { PlayerID value = default(PlayerID); value.steamID = avatar.steamID; result = value; } else { result = null; } return result; } [return: NotNullIfNotNull("avatar")] public static implicit operator PlayerID?(PlayerAvatar avatar) { return FromAvatar(avatar); } public override string ToString() { return "PlayerID: " + (SteamID?.ToString() ?? "Local Unknown"); } public override int GetHashCode() { return (!(SteamID == Local.SteamID)) ? SteamID.GetHashCode() : 0; } public override readonly bool Equals(object? obj) { return obj is PlayerID other && Equals(other); } public readonly bool Equals(PlayerID other) { return this == other; } public static bool operator ==(PlayerID left, PlayerID right) { return left.SteamID == right.SteamID; } public static bool operator !=(PlayerID left, PlayerID right) { return left.SteamID != right.SteamID; } } public static class PlayerSettingSyncer { private static NetworkedEvent SyncEvent = null; private static long SequenceNumber; private static bool isDirty; public static Dictionary Settings = new Dictionary(); public static void Init() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown SyncEvent = new NetworkedEvent("RT0405.QueenOfTheLosers.Sync", (Action)delegate(EventData e) { if (!RecieveMessage(e)) { QueenOfTheLosers.Logger.LogWarning((object)"invalid message recieved in PlayerSettingSyncer"); } }); } public static void SendIfDirty() { if (isDirty) { SendPlayerSettings(); } } public static void SetDirty() { isDirty = true; } public static void RegisterValue(string id, NetworkedPlayerSetting value) { Settings.Add(id, value); } public static void SendPlayerSettings() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) if (!PlayerID.Local.HasSteamID) { return; } Hashtable val = new Hashtable(); foreach (KeyValuePair setting in Settings) { NetworkedPlayerSetting value = setting.Value; if (value.TryGetPayload(out object payload)) { ((Dictionary)(object)val).Add((object)setting.Key, payload); } } NetworkedEvent syncEvent = SyncEvent; if (syncEvent != null) { syncEvent.RaiseEvent((object)new object[3] { PlayerID.Local.SteamID, SequenceNumber++, val }, NetworkingEvents.RaiseOthers, SendOptions.SendReliable); } isDirty = false; } public static bool RecieveMessage(EventData e) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if (!(e.CustomData is object[] array)) { return false; } if (array.Length != 3) { return false; } if (!(array[0] is string text)) { return false; } PlayerID? playerID = SemiFunc.PlayerAvatarGetFromSteamID(text); if (!playerID.HasValue) { return false; } if (!(array[1] is long seqNum) || 1 == 0) { return false; } object obj = array[2]; Hashtable val = (Hashtable)((obj is Hashtable) ? obj : null); if (val == null) { return false; } DictionaryEntryEnumerator enumerator = val.GetEnumerator(); try { while (((DictionaryEntryEnumerator)(ref enumerator)).MoveNext()) { DictionaryEntry current = ((DictionaryEntryEnumerator)(ref enumerator)).Current; if (current.Key is string key && Settings.TryGetValue(key, out NetworkedPlayerSetting value)) { value.ProcessPayload(playerID.Value, current.Value, seqNum); } } } finally { ((IDisposable)(DictionaryEntryEnumerator)(ref enumerator)).Dispose(); } return true; } } public abstract class NetworkedPlayerSetting { public abstract bool TryGetPayload(out object payload); public abstract bool ProcessPayload(PlayerID playerID, object payload, long seqNum); } public class NetworkedPlayerSetting : NetworkedPlayerSetting { public delegate void OnValueChangedDelegate(PlayerID playerID, T value); protected readonly Dictionary Values = new Dictionary(); public event OnValueChangedDelegate? OnValueChanged; public bool TryGetValue(PlayerAvatar avatar, out T value) { value = default(T); PlayerID? playerID = PlayerID.FromAvatar(avatar); if (!playerID.HasValue) { return false; } return TryGetValue(playerID.Value, out value); } public bool TryGetValue(PlayerID playerID, out T value) { if (!Values.TryGetValue(playerID, out (T, long) value2)) { value = default(T); return false; } (value, _) = value2; return true; } private void Set(PlayerID playerID, T value, long seqNum) { Values[playerID] = (value, seqNum); this.OnValueChanged?.Invoke(playerID, value); } public void SetLocalValue(T value) { Set(PlayerID.Local, value, 0L); PlayerSettingSyncer.SetDirty(); } public void BindConfig(ConfigEntry config) { ConfigEntry config2 = config; config2.SettingChanged += delegate { SetLocalValue(config2.Value); }; SetLocalValue(config2.Value); } public override bool TryGetPayload(out object payload) { if (TryGetValue(PlayerID.Local, out var value)) { payload = value; return true; } payload = null; return false; } public override bool ProcessPayload(PlayerID playerID, object payload, long seqNum) { if (Values.TryGetValue(playerID, out (T, long) value) && value.Item2 >= seqNum) { return true; } if (TryDeserializePayload(payload, out var value2)) { Set(playerID, value2, seqNum); return true; } return false; } public virtual bool TryDeserializePayload(object obj, out T value) { if (obj is T val) { value = val; return true; } value = default(T); return false; } } [HarmonyPatch(typeof(PlayerAvatar))] internal static class PlayerAvatarPatches { [HarmonyPostfix] [HarmonyPatch("Awake")] private static void Awake_PostFix() { PlayerSettingSyncer.SetDirty(); } [HarmonyPostfix] [HarmonyPatch("AddToStatsManagerRPC")] private static void AddToStatsManagerRPC_PostFix() { PlayerSettingSyncer.SetDirty(); } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("QueenOfTheLosers", "Queen Of The Losers", "1.1.1")] public class QueenOfTheLosers : BaseUnityPlugin { internal ConfigEntry? TitleConfig; internal ConfigEntry? DeathHeadPitchConfig; internal ConfigEntry? DeathHeadInflectionConfig; internal ConfigEntry? TTSPitchConfig; internal static QueenOfTheLosers Instance { get; private set; } internal static ManualLogSource Logger => Instance._logger; private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger; internal Harmony? Harmony { get; set; } internal void Awake() { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Expected O, but got Unknown Instance = this; PlayerSettingSyncer.Init(); ((Component)this).transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Patch(); TitleConfig = ((BaseUnityPlugin)this).Config.Bind("Title", "Title Selection", "RULER", new ConfigDescription("A replacement for the title of KING in the \"King of the Losers\" screen", (AcceptableValueBase)(object)new AcceptableValueList(new string[4] { "QUEEN", "RULER", "KING", "MONARCH" }), Array.Empty())); TitleChange.Init(TitleConfig); DeathHeadPitchConfig = ((BaseUnityPlugin)this).Config.Bind("Death Head Voice", "Pitch", 1f, new ConfigDescription("A modifier for the pitch of your voice while you're possesing your head, Vanilla is 1.0. Note: extreme values can make your voice unintelligible", (AcceptableValueBase)(object)new AcceptableValueRange(0.8f, 1.8f), Array.Empty())); DeathHeadInflectionConfig = ((BaseUnityPlugin)this).Config.Bind("Death Head Voice", "Low Battery Inflection", 0.5f, new ConfigDescription("Changes the inflection that is applied when you're running out of battery, Vanilla is 0.5. Note: extreme values can make your voice even more unintelligible", (AcceptableValueBase)(object)new AcceptableValueRange(0.4f, 1.8f), Array.Empty())); DeathHeadVoice.Init(DeathHeadPitchConfig, DeathHeadInflectionConfig); TTSPitchConfig = ((BaseUnityPlugin)this).Config.Bind("TTS", "Pitch", 0f, new ConfigDescription("A modifier for the pitch of your TTS voice, Vanilla is 0", (AcceptableValueBase)(object)new AcceptableValueRange(-0.5f, 1f), Array.Empty())); TTSVoicePitch.Init(TTSPitchConfig); Logger.LogMessage((object)TitleChange.ReplaceTitle("KING OF THE LOSERS!", "QUEEN")); } internal void Update() { PlayerSettingSyncer.SendIfDirty(); } internal void Patch() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0026: Expected O, but got Unknown if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } Harmony.PatchAll(); } internal void Unpatch() { Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace QueenOfTheLosers.Modifiers { [HarmonyPatch] public static class DeathHeadVoice { [HarmonyPatch] private static class Patches { private static bool CallOverride; [HarmonyPrefix] [HarmonyPatch(typeof(PlayerDeathHead), "Update")] private static void PlayerDeathHead_Update_Prefix(PlayerDeathHead __instance) { CallOverride = __instance.spectated; } [HarmonyPrefix] [HarmonyPatch(typeof(PlayerVoiceChat), "OverridePitch")] private static void PlayerVoiceChat_OverridePitch_Prefix(PlayerVoiceChat __instance, ref float _multiplier) { if (CallOverride && Pitch.TryGetValue(__instance.playerAvatar, out var value) && Inflection.TryGetValue(__instance.playerAvatar, out var value2)) { _multiplier = value * value2; } CallOverride = false; } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerDeathHead), "Update")] private static void PlayerDeathHead_Update_Postfix(PlayerDeathHead __instance) { if (CallOverride && Pitch.TryGetValue(__instance.playerAvatar, out var value)) { CallOverride = false; __instance.playerAvatar.voiceChat.OverridePitch(value, 0.25f, 0.25f, 0.1f, 0f, 0f); } CallOverride = false; } } private static NetworkedPlayerSetting? Pitch; private static NetworkedPlayerSetting? Inflection; public static void Init(ConfigEntry pitch, ConfigEntry inflection) { Pitch = new NetworkedPlayerSetting(); Inflection = new NetworkedPlayerSetting(); PlayerSettingSyncer.RegisterValue("QueenOfTheLosers.DeathHeadVoicePitchSetting", Pitch); PlayerSettingSyncer.RegisterValue("QueenOfTheLosers.DeathHeadVoiceInflectionSetting", Inflection); Pitch.BindConfig(pitch); Inflection.BindConfig(inflection); } } [HarmonyPatch] public class TitleChange : NetworkedPlayerSetting { [HarmonyPatch] private static class Patches { [HarmonyPrefix] [HarmonyPatch(typeof(ArenaMessageWinUI), "ArenaText")] private static void ArenaMessageWinUI_ArenaText_Prefix(ref string? __0, bool __1) { if (__1 && Instance.TryGetValue(SessionManager.instance.CrownedPlayerGet(), out string value)) { __0 = ReplaceTitle(__0, value); } } } public static TitleChange? Instance; public static string ReplaceText = "KING"; public static void Init(ConfigEntry config) { Instance = new TitleChange(); PlayerSettingSyncer.RegisterValue("QueenOfTheLosers.TitleChange", Instance); Instance.BindConfig(config); } public override bool TryDeserializePayload(object obj, out string value) { if (!base.TryDeserializePayload(obj, out value)) { return false; } if (value.Length > 16) { return false; } for (int i = 0; i < value.Length; i++) { if (!char.IsLetterOrDigit(value[i]) && value[i] != ' ') { return false; } } return true; } public static string? ReplaceTitle(string? text, string title) { return text?.Replace(ReplaceText, title, StringComparison.OrdinalIgnoreCase); } } [HarmonyPatch] public static class TTSVoicePitch { [HarmonyPatch] private static class Patches { [HarmonyPostfix] [HarmonyPatch(typeof(TTSVoice), "Start")] private static void TTS_Start_Postfix(TTSVoice __instance) { NetworkedPlayerSetting? pitch = Pitch; if (pitch != null && pitch.TryGetValue(__instance.playerAvatar, out var value)) { __instance.playerAvatar.voiceChat.TTSPitchChange = value; } } } private static NetworkedPlayerSetting? Pitch; public static void Init(ConfigEntry pitch) { Pitch = new NetworkedPlayerSetting(); PlayerSettingSyncer.RegisterValue("QueenOfTheLosers.TTSVoicePitchSetting", Pitch); Pitch.OnValueChanged += delegate(PlayerID playerID, float value) { if ((Object)(object)playerID.Avatar?.voiceChat != (Object)null) { playerID.Avatar.voiceChat.TTSPitchChange = value; } }; Pitch.BindConfig(pitch); } } }