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 RepoGamblingConfigure v0.2.0
RepoGamblingConfigure.dll
Decompiled 4 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MenuLib; using MenuLib.MonoBehaviors; using Microsoft.CodeAnalysis; using Photon.Pun; using REPOGambling; using REPOLib.Modules; using TMPro; using UnityEngine; using UnityEngine.Events; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace repo_gambling_configure_nezzy { [HarmonyPatch(typeof(EnvironmentPlacementPatch), "ApplyUpgradeDirect")] internal static class ApplyUpgradeDirectPatch { private static bool Prefix(int upgradeIndex, string steamID) { if (upgradeIndex >= 8) { return !UpgradeDropConfig.Apply(upgradeIndex, steamID); } return true; } } [HarmonyPatch] internal static class RepoConfigMenuPatch { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ScrollViewBuilderDelegate <>9__2_0; internal RectTransform <AddSectionSpacer>b__2_0(Transform parent) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0f, 30f); return ((REPOElement)MenuAPI.CreateREPOSpacer(parent, default(Vector2), val)).rectTransform; } } private static MethodBase? TargetMethod() { return AccessTools.Method("REPOConfig.ConfigMenu:CreateModEntries", new Type[2] { typeof(REPOPopupPage), typeof(ConfigEntryBase[]) }, (Type[])null); } private static bool Prefix(REPOPopupPage modPage, ConfigEntryBase[] configEntryBases) { bool flag = false; foreach (ConfigEntryBase val in configEntryBases) { if (val.Definition.Section == "QOL" && val.Definition.Key == "Remove Player Count Repeat") { flag = true; break; } } if (!flag) { return true; } AddHeader(modPage, "QOL"); AddToggle(modPage, "Remove Player\nCount Repeat", UpgradeDropConfig.SetRemovePlayerCountRepeat, UpgradeDropConfig.RemovePlayerCountRepeat); AddToggle(modPage, "Replace Range At\nMax Range Upgrade", UpgradeDropConfig.SetRemoveRangeAtTen, UpgradeDropConfig.RemoveRangeAtTen); AddToggle(modPage, "Try To Spawn Shop\nSecret (Host Controlled)", UpgradeDropConfig.SetTrySpawnShopSecret, UpgradeDropConfig.TrySpawnShopSecret); AddSectionSpacer(modPage); AddUpgrades(modPage, "Yellow Upgrade"); AddUpgrades(modPage, "Orange Upgrade"); AddUpgrades(modPage, "Blue Upgrade"); AddUpgrades(modPage, "Green Upgrade"); AddUpgrades(modPage, "Death"); AddUpgrades(modPage, "Custom"); AddHeader(modPage, "Developer"); AddToggle(modPage, "Extended Logging", UpgradeDropConfig.SetExtendedLogging, UpgradeDropConfig.ExtendedLogging); UpgradeDropConfig.LogDetailed("Configure menu section spacing applied (30 px)."); return false; } private static void AddSectionSpacer(REPOPopupPage page) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown object obj = <>c.<>9__2_0; if (obj == null) { ScrollViewBuilderDelegate val = delegate(Transform parent) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0f, 30f); return ((REPOElement)MenuAPI.CreateREPOSpacer(parent, default(Vector2), val2)).rectTransform; }; <>c.<>9__2_0 = val; obj = (object)val; } page.AddElementToScrollView((ScrollViewBuilderDelegate)obj, 0f, 0f); } private static void AddHeader(REPOPopupPage page, string text) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown page.AddElementToScrollView((ScrollViewBuilderDelegate)((Transform parent) => (RectTransform)((Component)MenuAPI.CreateREPOLabel(text, parent, default(Vector2))).transform), 0f, 4f); } private static void AddUpgrades(REPOPopupPage page, string section) { AddHeader(page, section); for (int i = 0; i < UpgradeDropConfig.Upgrades.Count; i++) { UpgradeDropConfig.Upgrade upgrade = UpgradeDropConfig.Upgrades[i]; if (upgrade.Section == section) { int index = i; AddToggle(page, upgrade.Label, delegate(bool enabled) { UpgradeDropConfig.SetAllowed(index, enabled); }, UpgradeDropConfig.IsAllowed(index)); } } AddSectionSpacer(page); } private static void AddToggle(REPOPopupPage page, string text, Action<bool> onToggle, bool value) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform parent) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown string text2 = text; Action<bool> action = onToggle; bool flag = value; return (RectTransform)((Component)MenuAPI.CreateREPOToggle(text2, action, parent, default(Vector2), "ON", "OFF", flag)).transform; }, 0f, 2f); } } [BepInPlugin("nezzy.RepoGamblingConfigure", "REPO Gambling Configure", "0.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class RepoGamblingConfigurePlugin : BaseUnityPlugin { internal static RepoGamblingConfigurePlugin Instance { get; private set; } internal static ManualLogSource Logger => Instance._logger; private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger; private void Awake() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) Instance = this; ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID).PatchAll(); Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} loaded."); } private void Start() { UpgradeDropConfig.Bind(((BaseUnityPlugin)this).Config); UpgradeDropRollTranspiler.LogPatchCounts(); } } [HarmonyPatch(typeof(WheelMachineRPCHandler), "RPC_ApplyUpgrade")] internal static class RpcApplyUpgradePatch { private static bool Prefix(int upgradeIndex, string steamID) { if (upgradeIndex >= 8) { return !UpgradeDropConfig.Apply(upgradeIndex, steamID); } return true; } } [HarmonyPatch(typeof(LevelGenerator), "Start")] internal static class ShopDeadEndCapturePatch { internal static List<PrefabRef>? OriginalDeadEnds; internal static PrefabRef? GamblingRoom; [HarmonyPrefix] [HarmonyPriority(800)] private static void CaptureOriginalDeadEnds() { OriginalDeadEnds = null; GamblingRoom = null; if ((Object)(object)RunManager.instance != (Object)null && RunManager.instance.levelCurrent.NarrativeName == "Service Station") { OriginalDeadEnds = new List<PrefabRef>(RunManager.instance.levelCurrent.ModulesDeadEnd1); } } } [HarmonyPatch(typeof(LevelGenerator), "Start")] internal static class ShopGamblingRoomPatch { [HarmonyPrefix] [HarmonyPriority(0)] private static void GuaranteeGamblingRoom() { List<PrefabRef> original = ShopDeadEndCapturePatch.OriginalDeadEnds; if (original == null || (Object)(object)RunManager.instance == (Object)null || RunManager.instance.levelCurrent.NarrativeName != "Service Station") { return; } List<PrefabRef> modulesDeadEnd = RunManager.instance.levelCurrent.ModulesDeadEnd1; PrefabRef val = ((IEnumerable<PrefabRef>)modulesDeadEnd).FirstOrDefault((Func<PrefabRef, bool>)((PrefabRef prefab) => !original.Any((PrefabRef originalPrefab) => ((PrefabRef<GameObject>)(object)originalPrefab).ResourcePath == ((PrefabRef<GameObject>)(object)prefab).ResourcePath))); string text = "REPOGambling default selection"; if (val == null) { REPOGambling val2 = Object.FindObjectOfType<REPOGambling>(); if ((Object)(object)val2 == (Object)null) { RepoGamblingConfigurePlugin.Logger.LogWarning((object)"Guaranteed Gambling room skipped: REPOGambling was unavailable."); return; } List<GameObject> list = val2.LoadModulePrefabsFromBundle(); if (list.Count == 0) { RepoGamblingConfigurePlugin.Logger.LogWarning((object)"Guaranteed Gambling room skipped: no Gambling room prefab was available."); return; } GameObject val3 = list[Random.Range(0, list.Count)]; val = NetworkPrefabs.RegisterNetworkPrefab("Level/Shop/Modules/" + ((Object)val3).name, val3); text = "guarantee fallback"; } modulesDeadEnd.Clear(); modulesDeadEnd.Add(val); ShopDeadEndCapturePatch.GamblingRoom = val; UpgradeDropConfig.LogShopInfo("Guaranteed Gambling room selected via " + text + ": " + ((PrefabRef<GameObject>)(object)val).ResourcePath); } } [HarmonyPatch(typeof(LevelGenerator), "GenerateConnectObjects")] internal static class ShopSecretPlacementPatch { private static readonly FieldInfo? LevelGrid = AccessTools.Field(typeof(LevelGenerator), "LevelGrid"); [HarmonyPrefix] private static void TryAddSecretDeadEnd(LevelGenerator __instance) { //IL_00e1: Unknown result type (might be due to invalid IL or missing references) List<PrefabRef> originalDeadEnds = ShopDeadEndCapturePatch.OriginalDeadEnds; if (!UpgradeDropConfig.TrySpawnShopSecret || originalDeadEnds == null || originalDeadEnds.Count == 0 || ShopDeadEndCapturePatch.GamblingRoom == null || !SemiFunc.IsMasterClientOrSingleplayer()) { return; } if (!(LevelGrid?.GetValue(__instance) is Tile[,] array)) { UpgradeDropConfig.LogShopInfo("Shop secret skipped: the store grid was unavailable."); return; } List<Tile> list = new List<Tile>(); for (int i = 0; i < array.GetLength(0); i++) { for (int j = 0; j < array.GetLength(1); j++) { Tile val = array[i, j]; if (!val.active && HasOneOpenConnection(array, i, j) && !TouchesReservedTile(array, i, j)) { list.Add(val); } } } if (list.Count == 0) { UpgradeDropConfig.LogShopInfo("Shop secret skipped: no eligible second dead-end socket."); return; } Tile obj = list[Random.Range(0, list.Count)]; obj.active = true; obj.type = (Type)2; PrefabRef val2 = originalDeadEnds[Random.Range(0, originalDeadEnds.Count)]; RunManager.instance.levelCurrent.ModulesDeadEnd1.Add(val2); UpgradeDropConfig.LogShopInfo("Shop secret spawned: " + ((PrefabRef<GameObject>)(object)val2).ResourcePath); } private static bool HasOneOpenConnection(Tile[,] grid, int x, int y) { int num = 0; (int, int)[] array = new(int, int)[4] { (x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1) }; for (int i = 0; i < array.Length; i++) { var (num2, num3) = array[i]; if (num2 >= 0 && num2 < grid.GetLength(0) && num3 >= 0 && num3 < grid.GetLength(1) && grid[num2, num3].active) { num++; } } return num == 1; } private static bool TouchesReservedTile(Tile[,] grid, int x, int y) { //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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Invalid comparison between Unknown and I4 //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Invalid comparison between Unknown and I4 //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Invalid comparison between Unknown and I4 (int, int)[] array = new(int, int)[4] { (x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1) }; for (int i = 0; i < array.Length; i++) { var (num, num2) = array[i]; if (num >= 0 && num < grid.GetLength(0) && num2 >= 0 && num2 < grid.GetLength(1)) { Type type = grid[num, num2].type; if ((int)type == 2 || (int)type == 3 || (int)type == 4) { return true; } } } return false; } } [HarmonyPatch(typeof(EnvironmentPlacementPatch), "SetupSlotMachines")] internal static class SlotBetTextFontPatch { [HarmonyPostfix] private static void RepairBetTextFont() { GameObject[] array = Object.FindObjectsByType<GameObject>((FindObjectsSortMode)0); foreach (GameObject val in array) { if (!((Object)val).name.Contains("Slot Machine")) { continue; } Transform val2 = val.transform.Find("Bet Text"); if ((Object)(object)val2 == (Object)null) { continue; } TextMeshPro component = ((Component)val2).GetComponent<TextMeshPro>(); if ((Object)(object)component == (Object)null) { continue; } TMP_FontAsset font = ((TMP_Text)component).font; Material fontSharedMaterial = ((TMP_Text)component).fontSharedMaterial; Texture val3 = (Texture)(object)(((Object)(object)font != (Object)null) ? font.atlasTexture : null); TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset; string[] obj = new string[13] { "Slot bet text before font repair: font=", ((font != null) ? ((Object)font).name : null) ?? "null", ", material=", ((fontSharedMaterial != null) ? ((Object)fontSharedMaterial).name : null) ?? "null", ", shader=", null, null, null, null, null, null, null, null }; object obj2; if (fontSharedMaterial == null) { obj2 = null; } else { Shader shader = fontSharedMaterial.shader; obj2 = ((shader != null) ? ((Object)shader).name : null); } if (obj2 == null) { obj2 = "null"; } obj[5] = (string)obj2; obj[6] = ", "; bool? obj3; if (fontSharedMaterial == null) { obj3 = null; } else { Shader shader2 = fontSharedMaterial.shader; obj3 = ((shader2 != null) ? new bool?(shader2.isSupported) : ((bool?)null)); } obj[7] = string.Format("supported={0}, atlas={1}, ", obj3, ((val3 != null) ? ((Object)val3).name : null) ?? "null"); obj[8] = string.Format("size={0}x{1}, gameFont={2}, ", (val3 != null) ? new int?(val3.width) : ((int?)null), (val3 != null) ? new int?(val3.height) : ((int?)null), ((defaultFontAsset != null) ? ((Object)defaultFontAsset).name : null) ?? "null"); obj[9] = "gameShader="; object obj4; if (defaultFontAsset == null) { obj4 = null; } else { Material material = ((TMP_Asset)defaultFontAsset).material; if (material == null) { obj4 = null; } else { Shader shader3 = material.shader; obj4 = ((shader3 != null) ? ((Object)shader3).name : null); } } if (obj4 == null) { obj4 = "null"; } obj[10] = (string)obj4; obj[11] = ", text="; obj[12] = ((TMP_Text)component).text; UpgradeDropConfig.LogDetailed(string.Concat(obj)); if ((Object)(object)defaultFontAsset == (Object)null || (Object)(object)((TMP_Asset)defaultFontAsset).material == (Object)null || !defaultFontAsset.HasCharacter('$', false, false)) { RepoGamblingConfigurePlugin.Logger.LogError((object)"Slot bet text font repair failed: the game font, material, or dollar-sign glyph is unavailable."); continue; } ((TMP_Text)component).font = defaultFontAsset; ((TMP_Text)component).fontSharedMaterial = ((TMP_Asset)defaultFontAsset).material; ((TMP_Text)component).ForceMeshUpdate(false, false); UpgradeDropConfig.LogDetailed("Slot bet text font replaced: font=" + ((Object)((TMP_Text)component).font).name + ", shader=" + ((Object)((TMP_Text)component).fontSharedMaterial.shader).name + ", atlas=" + ((Object)((TMP_Text)component).font.atlasTexture).name + ", " + $"characters={((TMP_Text)component).textInfo.characterCount}."); } } } [HarmonyPatch(typeof(EnvironmentPlacementPatch), "SpinAndStop")] internal static class SlotSpinPatch { private static readonly PropertyInfo GamblingInstance = AccessTools.Property(typeof(REPOGambling), "Instance") ?? throw new MissingMemberException(typeof(REPOGambling).FullName, "Instance"); private static readonly MethodInfo SmoothSpinRoll = AccessTools.Method(typeof(EnvironmentPlacementPatch), "SmoothSpinRoll", new Type[3] { typeof(Transform), typeof(float), typeof(float) }, (Type[])null) ?? throw new MissingMethodException(typeof(EnvironmentPlacementPatch).FullName, "SmoothSpinRoll"); private static readonly MethodInfo SpecialPrize = AccessTools.Method(typeof(EnvironmentPlacementPatch), "SpecialPrize", new Type[1] { typeof(Transform) }, (Type[])null) ?? throw new MissingMethodException(typeof(EnvironmentPlacementPatch).FullName, "SpecialPrize"); private static bool Prefix(Transform[] rolls, int[] results, float[] fruitAngles, string[] fruitNames, AudioSource audio, SlotMachineState state, Transform nadeOrigin, ref IEnumerator __result) { __result = SpinAndStopCompatible(rolls, results, fruitAngles, fruitNames, audio, state, nadeOrigin); return false; } private static IEnumerator SpinAndStopCompatible(Transform[] rolls, int[] results, float[] fruitAngles, string[] fruitNames, AudioSource audio, SlotMachineState state, Transform nadeOrigin) { object? value = GamblingInstance.GetValue(null); REPOGambling gambling = (REPOGambling)(((value is REPOGambling) ? value : null) ?? throw new InvalidOperationException("REPOGambling instance was unavailable for slot spin.")); float elapsed = 0f; bool specialCombo = results.Count((int r) => fruitNames[r] == "Cherry") == 1 && results.Count((int r) => fruitNames[r] == "Spade") == 2; UpgradeDropConfig.LogDetailed("Sunstone slot spin coroutine started."); PlaySlotSound(rolls, audio, gambling, (GamblingSound)0, 0); while (elapsed < 6.38f) { foreach (Transform val in rolls) { if ((Object)(object)val != (Object)null) { float num2 = Random.Range(0f, 360f); val.localEulerAngles = new Vector3(num2, val.localEulerAngles.y, val.localEulerAngles.z); } } elapsed += Time.deltaTime; yield return null; } IEnumerator[] array = new IEnumerator[rolls.Length]; for (int num3 = 0; num3 < rolls.Length; num3++) { if ((Object)(object)rolls[num3] != (Object)null && results[num3] != -1) { array[num3] = (SmoothSpinRoll.Invoke(null, new object[3] { rolls[num3], fruitAngles[results[num3]], 0.1f }) as IEnumerator) ?? throw new InvalidOperationException("Base SmoothSpinRoll did not return a coroutine."); } } IEnumerator[] array2 = array; foreach (IEnumerator enumerator in array2) { if (enumerator != null) { ((MonoBehaviour)gambling).StartCoroutine(enumerator); } } yield return (object)new WaitForSeconds(0.1f); bool num4 = results.All((int r) => r == results[0]) && results[0] != -1; bool flag = num4 && fruitNames[results[0]] == "7"; int num5 = Mathf.RoundToInt((num4 ? MatchMultiplier(gambling, fruitNames[results[0]]) : 0f) * (float)gambling.slotBet); if (specialCombo) { SpecialPrize.Invoke(null, new object[1] { nadeOrigin }); } if (num5 > 0) { SemiFunc.StatSetRunCurrency(SemiFunc.StatGetRunCurrency() + num5); if (flag) { ChatManager.instance.PossessChatScheduleStart(-1); ChatManager.instance.PossessChat((PossessChatID)3, $"Jackpot {num5}k from the slots!", 2f, Color.yellow, 0f, true, 1, (UnityEvent)null, (string)null); ChatManager.instance.PossessChatScheduleEnd(); PlaySlotSound(rolls, audio, gambling, (GamblingSound)5, 5); } else { PlaySlotSound(rolls, audio, gambling, (GamblingSound)3, 3); } } else { PlaySlotSound(rolls, audio, gambling, (GamblingSound)4, 4); } state.IsSpinning = false; UpgradeDropConfig.LogDetailed($"Sunstone slot spin completed: payout={num5}, jackpot={flag}, special={specialCombo}."); } private static float MatchMultiplier(REPOGambling gambling, string fruit) { return fruit switch { "7" => gambling.slotRewardJackpotMultiplier.Value, "Cherry" => gambling.slotRewardCherryMatchMultiplier.Value, "Strawberry" => gambling.slotRewardBerryMatchMultiplier.Value, "Spade" => gambling.slotRewardSpadeMatchMultiplier.Value, "Clove" => gambling.slotRewardCloveMatchMultiplier.Value, _ => 0f, }; } private static void PlaySlotSound(Transform[] rolls, AudioSource audio, REPOGambling gambling, GamblingSound sound, int rpcSound) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.IsMultiplayer()) { ((Component)rolls[0].parent).GetComponent<PhotonView>().RPC("RPC_PlaySound", (RpcTarget)0, new object[1] { rpcSound }); } else if ((Object)(object)audio != (Object)null) { audio.PlayOneShot(gambling.GetClip(sound)); } } } internal static class UpgradeAnnouncePatch { private static readonly FieldInfo IsDisabledField = AccessTools.Field(typeof(PlayerAvatar), "isDisabled") ?? throw new MissingFieldException(typeof(PlayerAvatar).FullName, "isDisabled"); internal static void Announce(int index, PlayerAvatar avatar) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)avatar == (Object)null) { return; } object value = IsDisabledField.GetValue(avatar); if ((value is bool && (bool)value) || index < 0 || index >= UpgradeDropConfig.Upgrades.Count) { return; } ChatManager instance = ChatManager.instance; if ((Object)(object)instance == (Object)null) { return; } try { string text = ((Random.value > 0.5f) ? "Upgraded" : "Just what I wanted, a"); instance.PossessChatScheduleStart(-1); instance.PossessChat((PossessChatID)3, text + " " + UpgradeDropConfig.Upgrades[index].Label, 2f, Color.blue, 0f, false, 0, (UnityEvent)null, (string)null); instance.PossessChatScheduleEnd(); UpgradeDropConfig.LogDetailed("Sunstone upgrade announcement returned successfully."); } catch (Exception arg) { RepoGamblingConfigurePlugin.Logger.LogWarning((object)$"Failed to show upgrade announcement: {arg}"); } } } internal static class UpgradeDropConfig { internal sealed class Upgrade { internal string Key { get; } internal string Label { get; } internal string Section { get; } internal Action<string> Apply { get; } internal Upgrade(string key, string label, string section, Action<string> apply) { Key = key; Label = label; Section = section; Apply = apply; } } internal static readonly List<Upgrade> Upgrades = new List<Upgrade> { new Upgrade("MapPlayerCount", "Map Player Count", "Green Upgrade", delegate(string steamId) { PunManager.instance.UpgradeMapPlayerCount(steamId, 1); }), new Upgrade("PlayerStamina", "Player Stamina", "Yellow Upgrade", delegate(string steamId) { PunManager.instance.UpgradePlayerEnergy(steamId, 1); }), new Upgrade("ExtraJump", "Extra Jump", "Blue Upgrade", delegate(string steamId) { PunManager.instance.UpgradePlayerExtraJump(steamId, 1); }), new Upgrade("GrabRange", "Grab Range", "Orange Upgrade", delegate(string steamId) { PunManager.instance.UpgradePlayerGrabRange(steamId, 1); }), new Upgrade("GrabStrength", "Grab Strength", "Orange Upgrade", delegate(string steamId) { PunManager.instance.UpgradePlayerGrabStrength(steamId, 1); }), new Upgrade("Health", "Health", "Green Upgrade", delegate(string steamId) { PunManager.instance.UpgradePlayerHealth(steamId, 1); }), new Upgrade("SprintSpeed", "Sprint Speed", "Yellow Upgrade", delegate(string steamId) { PunManager.instance.UpgradePlayerSprintSpeed(steamId, 1); }), new Upgrade("TumbleLaunch", "Tumble Launch", "Blue Upgrade", delegate(string steamId) { PunManager.instance.UpgradePlayerTumbleLaunch(steamId, 1); }), new Upgrade("TumbleClimb", "Tumble Climb", "Blue Upgrade", delegate(string steamId) { PunManager.instance.UpgradePlayerTumbleClimb(steamId, 1); }), new Upgrade("DeathHeadBattery", "Death Head Battery", "Death", delegate(string steamId) { PunManager.instance.UpgradeDeathHeadBattery(steamId, 1); }), new Upgrade("TumbleWings", "Tumble Wings", "Blue Upgrade", delegate(string steamId) { PunManager.instance.UpgradePlayerTumbleWings(steamId, 1); }), new Upgrade("CrouchRest", "Crouch Rest", "Yellow Upgrade", delegate(string steamId) { PunManager.instance.UpgradePlayerCrouchRest(steamId, 1); }) }; private static readonly Dictionary<string, ConfigEntry<bool>> Entries = new Dictionary<string, ConfigEntry<bool>>(); private static readonly FieldInfo SteamIdField = AccessTools.Field(typeof(PlayerAvatar), "steamID") ?? throw new MissingFieldException(typeof(PlayerAvatar).FullName, "steamID"); private static ConfigEntry<bool> removePlayerCountRepeat = null; private static ConfigEntry<bool> removeRangeAtTen = null; private static ConfigEntry<bool> trySpawnShopSecret = null; private static ConfigEntry<bool> extendedLogging = null; internal static bool RemovePlayerCountRepeat => removePlayerCountRepeat.Value; internal static bool RemoveRangeAtTen => removeRangeAtTen.Value; internal static bool TrySpawnShopSecret => trySpawnShopSecret.Value; internal static bool ExtendedLogging => extendedLogging?.Value ?? false; internal static void BindLogging(ConfigFile config) { extendedLogging = config.Bind<bool>("Developer", "Extended Shop Logging", false, "Log detailed Configure patch, wheel, slot, font, and Shop events."); } internal static void Bind(ConfigFile config) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Expected O, but got Unknown //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Expected O, but got Unknown removePlayerCountRepeat = config.Bind<bool>("QOL", "Remove Player Count Repeat", true, "Remove Map Player Count when the player already has it."); ConfigDefinition val = new ConfigDefinition("QOL", "Remove Range At 10"); bool flag = true; ConfigEntry<bool> val2 = null; if (config.TryGetEntry<bool>(val, ref val2)) { flag = val2.Value; } config.Remove(val); removeRangeAtTen = config.Bind<bool>("QOL", "Replace Range At Max Range Upgrade", flag, "Remove Grab Range when the player has more than 9 levels."); trySpawnShopSecret = config.Bind<bool>("QOL", "Try To Spawn Shop Secret (Host Controlled)", true, "Try to add a second Shop dead-end for the original secret-room pool. The host controls this setting."); foreach (PlayerUpgrade upgrade in Upgrades.PlayerUpgrades) { string key = "REPOLib." + upgrade.UpgradeId; if (!Upgrades.Exists((Upgrade existing) => existing.Key == key)) { Upgrades.Add(new Upgrade(key, upgrade.UpgradeId, "Custom", delegate(string steamId) { upgrade.AddLevel(steamId, 1); })); } } string[] obj = new string[6] { "Yellow Upgrade", "Orange Upgrade", "Blue Upgrade", "Green Upgrade", "Death", "Custom" }; ConfigEntry<bool> val3 = null; ConfigEntry<bool> val4 = null; string[] array = obj; foreach (string text in array) { foreach (Upgrade upgrade2 in Upgrades) { if (!(upgrade2.Section != text)) { ConfigDefinition val5 = new ConfigDefinition("Upgrade Drops", upgrade2.Key); bool flag2 = true; if (config.TryGetEntry<bool>(val5, ref val3)) { flag2 = val3.Value; } config.Remove(val5); ConfigDefinition val6 = new ConfigDefinition(upgrade2.Section.Replace(" Upgrade", string.Empty), upgrade2.Key); if (config.TryGetEntry<bool>(val6, ref val4)) { flag2 = val4.Value; } config.Remove(val6); Entries[upgrade2.Key] = config.Bind<bool>(upgrade2.Section, upgrade2.Key, flag2, "Allow REPOGambling to award " + upgrade2.Label + "."); } } } BindLogging(config); LogDetailed($"Configured {Upgrades.Count} upgrade drops ({Upgrades.Count - 12} REPOLib custom)."); } internal static bool IsAllowed(int index) { if (index >= 0 && index < Upgrades.Count) { return Entries[Upgrades[index].Key].Value; } return false; } internal static void SetAllowed(int index, bool allowed) { if (index >= 0 && index < Upgrades.Count) { Entries[Upgrades[index].Key].Value = allowed; ((BaseUnityPlugin)RepoGamblingConfigurePlugin.Instance).Config.Save(); } } internal static int PickUpgradeIndex(PlayerAvatar avatar) { string text = SteamIdField.GetValue(avatar) as string; if (string.IsNullOrEmpty(text)) { throw new InvalidOperationException("PlayerAvatar steamID was unavailable."); } List<int> list = new List<int>(); for (int i = 0; i < Upgrades.Count; i++) { if (IsAllowed(i) && IsEligible(Upgrades[i], text)) { list.Add(i); } } if (list.Count == 0) { RepoGamblingConfigurePlugin.Logger.LogWarning((object)"All eligible upgrade drops disabled; falling back to eligible upgrades."); for (int j = 0; j < Upgrades.Count; j++) { if (IsEligible(Upgrades[j], text)) { list.Add(j); } } } return list[Random.Range(0, list.Count)]; } private static bool IsEligible(Upgrade upgrade, string steamId) { if ((Object)(object)StatsManager.instance == (Object)null) { return true; } if (upgrade.Key == "MapPlayerCount" && removePlayerCountRepeat.Value && StatsManager.instance.playerUpgradeMapPlayerCount.TryGetValue(steamId, out var value) && value > 0) { return false; } if (!(upgrade.Key != "GrabRange") && removeRangeAtTen.Value && StatsManager.instance.playerUpgradeRange.TryGetValue(steamId, out var value2)) { return value2 <= 9; } return true; } internal static void SetRemovePlayerCountRepeat(bool enabled) { removePlayerCountRepeat.Value = enabled; ((BaseUnityPlugin)RepoGamblingConfigurePlugin.Instance).Config.Save(); } internal static void SetRemoveRangeAtTen(bool enabled) { removeRangeAtTen.Value = enabled; ((BaseUnityPlugin)RepoGamblingConfigurePlugin.Instance).Config.Save(); } internal static void SetTrySpawnShopSecret(bool enabled) { trySpawnShopSecret.Value = enabled; ((BaseUnityPlugin)RepoGamblingConfigurePlugin.Instance).Config.Save(); } internal static void SetExtendedLogging(bool enabled) { extendedLogging.Value = enabled; ((BaseUnityPlugin)RepoGamblingConfigurePlugin.Instance).Config.Save(); } internal static void LogShopInfo(string message) { LogDetailed(message); } internal static void LogDetailed(string message) { if (ExtendedLogging) { RepoGamblingConfigurePlugin.Logger.LogInfo((object)message); } } internal static bool Apply(int index, string steamId) { if (index < 0 || index >= Upgrades.Count) { RepoGamblingConfigurePlugin.Logger.LogWarning((object)$"Invalid upgrade index: {index}"); return false; } Upgrades[index].Apply(steamId); return true; } } [HarmonyPatch(typeof(EnvironmentPlacementPatch), "HandleWheelPrize")] internal static class UpgradeDropRollTranspiler { private static int upgradeRollCount; private static int announcementCount; private static int wheelDeathCount; private static readonly MethodInfo RandomRangeInt = AccessTools.Method(typeof(Random), "Range", new Type[2] { typeof(int), typeof(int) }, (Type[])null); private static readonly MethodInfo PickUpgradeIndex = AccessTools.Method(typeof(UpgradeDropConfig), "PickUpgradeIndex", new Type[1] { typeof(PlayerAvatar) }, (Type[])null); private static readonly MethodInfo UpgradeAnnounce = AccessTools.Method(typeof(EnvironmentPlacementPatch), "UpgradeAnnounce", new Type[2] { typeof(int), typeof(PlayerAvatar) }, (Type[])null); private static readonly MethodInfo CompatibleAnnounce = AccessTools.Method(typeof(UpgradeAnnouncePatch), "Announce", new Type[2] { typeof(int), typeof(PlayerAvatar) }, (Type[])null); private static readonly MethodInfo WheelSelfDestruction = AccessTools.Method(typeof(EnvironmentPlacementPatch), "WheelSelfDestruction", new Type[1] { typeof(PlayerAvatar) }, (Type[])null); private static readonly MethodInfo CompatibleWheelDeath = AccessTools.Method(typeof(WheelDeathPatch), "AnnounceAndKill", new Type[1] { typeof(PlayerAvatar) }, (Type[])null); private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.ToList(); int num = 0; for (int i = 0; i <= list.Count - 3; i++) { if (CodeInstructionExtensions.LoadsConstant(list[i], 0L) && CodeInstructionExtensions.LoadsConstant(list[i + 1], 8L) && CodeInstructionExtensions.Calls(list[i + 2], RandomRangeInt)) { list[i].opcode = OpCodes.Ldarg_3; list[i + 1].opcode = OpCodes.Nop; list[i + 1].operand = null; list[i + 2].opcode = OpCodes.Call; list[i + 2].operand = PickUpgradeIndex; num++; } } int num2 = 0; int num3 = 0; foreach (CodeInstruction item in list) { if (CodeInstructionExtensions.Calls(item, UpgradeAnnounce)) { item.opcode = OpCodes.Call; item.operand = CompatibleAnnounce; num2++; } else if (CodeInstructionExtensions.Calls(item, WheelSelfDestruction)) { item.opcode = OpCodes.Call; item.operand = CompatibleWheelDeath; num3++; } } if (num != 2 || num2 != 2 || num3 != 2) { throw new InvalidOperationException($"Unsupported REPOGambling 1.7.1 wheel IL: {num} upgrade rolls, {num2} announcement calls, {num3} wheel death calls."); } upgradeRollCount = num; announcementCount = num2; wheelDeathCount = num3; return list; } internal static void LogPatchCounts() { UpgradeDropConfig.LogDetailed($"Patched {upgradeRollCount} REPOGambling upgrade roll(s)."); UpgradeDropConfig.LogDetailed($"Replaced {announcementCount} obsolete upgrade announcement call(s)."); UpgradeDropConfig.LogDetailed($"Replaced {wheelDeathCount} wheel death call(s)."); } private static void Postfix(PrizeEntry prize) { UpgradeDropConfig.LogDetailed("REPOGambling wheel prize handler returned: " + prize.name + "."); } } internal static class WheelDeathPatch { private static readonly FieldInfo IsDisabledField = AccessTools.Field(typeof(PlayerAvatar), "isDisabled") ?? throw new MissingFieldException(typeof(PlayerAvatar).FullName, "isDisabled"); private static readonly PropertyInfo GamblingInstance = AccessTools.Property(typeof(REPOGambling), "Instance") ?? throw new MissingMemberException(typeof(REPOGambling).FullName, "Instance"); private static readonly MethodInfo DelayedDeath = AccessTools.Method(typeof(EnvironmentPlacementPatch), "DelayedDeath", new Type[3] { typeof(PlayerAvatar), typeof(int), typeof(float) }, (Type[])null) ?? throw new MissingMethodException(typeof(EnvironmentPlacementPatch).FullName, "DelayedDeath"); internal static void AnnounceAndKill(PlayerAvatar avatar) { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)avatar == (Object)null) { return; } object value = IsDisabledField.GetValue(avatar); if (value is bool && (bool)value) { return; } ChatManager val = ChatManager.instance ?? throw new InvalidOperationException("ChatManager was unavailable for wheel death."); object? value2 = GamblingInstance.GetValue(null); REPOGambling val2 = (REPOGambling)(((value2 is REPOGambling) ? value2 : null) ?? throw new InvalidOperationException("REPOGambling instance was unavailable for wheel death.")); if (val2.deathLines == null || val2.deathLines.Count == 0) { throw new InvalidOperationException("REPOGambling death lines were unavailable."); } string text = val2.deathLines[Random.Range(0, val2.deathLines.Count)]; try { val.PossessChatScheduleStart(-1); val.PossessChat((PossessChatID)3, text, 2f, Color.red, 0f, true, 2, (UnityEvent)null, (string)null); val.PossessChatScheduleEnd(); if (!(DelayedDeath.Invoke(null, new object[3] { avatar, text.Length, 2f }) is IEnumerator enumerator)) { throw new InvalidOperationException("Wheel death coroutine was unavailable."); } ((MonoBehaviour)val2).StartCoroutine(enumerator); UpgradeDropConfig.LogDetailed("Sunstone wheel death voice line played and original delayed death scheduled."); } catch (Exception arg) { RepoGamblingConfigurePlugin.Logger.LogError((object)$"Wheel death failed: {arg}"); throw; } } } }