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 ShuffleUpgrade v1.0.2
ShuffleUpgrade.dll
Decompiled 2 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; 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: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("zabu")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("zabumod")] [assembly: AssemblyTitle("zabumod")] [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 REPOJP.ShuffleUpgrade { [BepInPlugin("jp.repo.shuffleupgrade", "ShuffleUpgrade", "1.4.0")] public sealed class ShuffleUpgradePlugin : BaseUnityPlugin { private enum UpgradeKind { Health, Stamina, ExtraJump, TumbleLaunch, TumbleClimb, MapPlayerCount, DeathHeadBattery, TumbleWings, SprintSpeed, CrouchRest, GrabStrength, Throw, GrabRange } private enum ShuffleMode { PreserveTypeTotalsRandom, FullRandomKeepGrandTotal, ReassignWholeBuilds, RandomPattern } private sealed class PlayerUpgradeBuild { public string SteamId; public string DisplayName; public readonly Dictionary<UpgradeKind, int> Values = new Dictionary<UpgradeKind, int>(); } private sealed class ShuffleSnapshot { public readonly List<string> SteamIds = new List<string>(); public readonly Dictionary<string, Dictionary<UpgradeKind, int>> Before = new Dictionary<string, Dictionary<UpgradeKind, int>>(); public readonly Dictionary<string, Dictionary<UpgradeKind, int>> After = new Dictionary<string, Dictionary<UpgradeKind, int>>(); public string ModeName = ""; public int ShopVisitCount; } private sealed class PlayerBiasProfile { public readonly Dictionary<string, float> PlayerTotalWeight = new Dictionary<string, float>(); public readonly Dictionary<string, Dictionary<UpgradeKind, float>> KindWeightByPlayer = new Dictionary<string, Dictionary<UpgradeKind, float>>(); } [HarmonyPatch(typeof(PlayerAvatar), "ChatMessageSend")] private static class Patch_PlayerAvatar_ChatMessageSend { private static bool Prefix(PlayerAvatar __instance, string _message) { if ((Object)(object)Instance == (Object)null) { return true; } if (string.IsNullOrEmpty(_message)) { return true; } if (!string.Equals(_message.Trim(), "/undo", StringComparison.OrdinalIgnoreCase)) { return true; } try { if ((Object)(object)__instance == (Object)null || !__instance.isLocal) { return true; } } catch { return true; } Instance.TryUndo(); return false; } } public const string PluginGuid = "jp.repo.shuffleupgrade"; public const string PluginName = "ShuffleUpgrade"; public const string PluginVersion = "1.4.0"; internal static ShuffleUpgradePlugin Instance; private Harmony _harmony; private bool _wasInShop; private bool _ranThisShopVisit; private int _shopVisitCount; private int _lastAppliedFrame = -1; private readonly Queue<ShuffleSnapshot> _undoHistory = new Queue<ShuffleSnapshot>(); private Random _random; private ConfigEntry<bool> CfgEnabled; private ConfigEntry<bool> CfgHostOnlyInMultiplayer; private ConfigEntry<bool> CfgTriggerOnShopEnter; private ConfigEntry<bool> CfgRunOncePerShopVisit; private ConfigEntry<int> CfgShuffleEveryNLevels; private ConfigEntry<ShuffleMode> CfgShuffleMode; private ConfigEntry<bool> CfgEnableRandomPatternMode3; private ConfigEntry<bool> CfgCallStatSyncAll; private ConfigEntry<bool> CfgUseFixedSeed; private ConfigEntry<int> CfgFixedSeed; private ConfigEntry<int> CfgChaosRandom; private ConfigEntry<bool> CfgLogSummary; private ConfigEntry<bool> CfgLogDetails; private ConfigEntry<bool> CfgLogExcludedUpgrades; private ConfigEntry<bool> CfgEnableUndoCommand; private ConfigEntry<bool> CfgUndoRequireHost; private ConfigEntry<int> CfgUndoHistoryKeepCount; private ConfigEntry<bool> CfgEnableMaxClampSafety; private ConfigEntry<bool> CfgEnableMode0; private ConfigEntry<bool> CfgEnableMode1; private ConfigEntry<bool> CfgEnableMode2; private ConfigEntry<bool> CfgIncludeHealth; private ConfigEntry<bool> CfgIncludeStamina; private ConfigEntry<bool> CfgIncludeExtraJump; private ConfigEntry<bool> CfgIncludeTumbleLaunch; private ConfigEntry<bool> CfgIncludeTumbleClimb; private ConfigEntry<bool> CfgIncludeMapPlayerCount; private ConfigEntry<bool> CfgIncludeDeathHeadBattery; private ConfigEntry<bool> CfgIncludeTumbleWings; private ConfigEntry<bool> CfgIncludeSprintSpeed; private ConfigEntry<bool> CfgIncludeCrouchRest; private ConfigEntry<bool> CfgIncludeGrabStrength; private ConfigEntry<bool> CfgIncludeThrow; private ConfigEntry<bool> CfgIncludeGrabRange; private ConfigEntry<int> CfgMaxHealth; private ConfigEntry<int> CfgMaxStamina; private ConfigEntry<int> CfgMaxExtraJump; private ConfigEntry<int> CfgMaxTumbleLaunch; private ConfigEntry<int> CfgMaxTumbleClimb; private ConfigEntry<int> CfgMaxMapPlayerCount; private ConfigEntry<int> CfgMaxDeathHeadBattery; private ConfigEntry<int> CfgMaxTumbleWings; private ConfigEntry<int> CfgMaxSprintSpeed; private ConfigEntry<int> CfgMaxCrouchRest; private ConfigEntry<int> CfgMaxGrabStrength; private ConfigEntry<int> CfgMaxThrow; private ConfigEntry<int> CfgMaxGrabRange; private void Awake() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown Instance = this; BindConfigs(); ResetRandom(); _harmony = new Harmony("jp.repo.shuffleupgrade"); _harmony.PatchAll(typeof(ShuffleUpgradePlugin)); LogInfo("Loaded"); } private void OnDestroy() { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch { } } private void Update() { if (!CfgEnabled.Value || !CanRunHere() || !SafeLevelGenDone()) { return; } bool flag = SafeRunIsShop(); if (flag && !_wasInShop) { _shopVisitCount++; _ranThisShopVisit = false; LogInfo($"Shop entered. Count={_shopVisitCount}"); } if (!flag && _wasInShop) { _ranThisShopVisit = false; } if (flag && CfgTriggerOnShopEnter.Value) { if (CfgRunOncePerShopVisit.Value && _ranThisShopVisit) { _wasInShop = flag; return; } if (_lastAppliedFrame == Time.frameCount) { _wasInShop = flag; return; } int num = Mathf.Clamp(CfgShuffleEveryNLevels.Value, 1, 10); if (_shopVisitCount % num == 0) { TryShuffleNow("ShopEnter"); } else { LogInfo($"Skip by interval. ShopCount={_shopVisitCount} Every={num}"); } } _wasInShop = flag; } private void BindConfigs() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Expected O, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Expected O, but got Unknown //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Expected O, but got Unknown //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Expected O, but got Unknown //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Expected O, but got Unknown //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Expected O, but got Unknown //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Expected O, but got Unknown //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Expected O, but got Unknown //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Expected O, but got Unknown //IL_0323: Unknown result type (might be due to invalid IL or missing references) //IL_032d: Expected O, but got Unknown //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0359: Expected O, but got Unknown //IL_037b: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Expected O, but got Unknown //IL_03a7: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Expected O, but got Unknown //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03dd: Expected O, but got Unknown //IL_03ff: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Expected O, but got Unknown //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Expected O, but got Unknown //IL_0457: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Expected O, but got Unknown //IL_0483: Unknown result type (might be due to invalid IL or missing references) //IL_048d: Expected O, but got Unknown //IL_04af: Unknown result type (might be due to invalid IL or missing references) //IL_04b9: Expected O, but got Unknown //IL_04db: Unknown result type (might be due to invalid IL or missing references) //IL_04e5: Expected O, but got Unknown //IL_0507: Unknown result type (might be due to invalid IL or missing references) //IL_0511: Expected O, but got Unknown //IL_0533: Unknown result type (might be due to invalid IL or missing references) //IL_053d: Expected O, but got Unknown //IL_055f: Unknown result type (might be due to invalid IL or missing references) //IL_0569: Expected O, but got Unknown //IL_058b: Unknown result type (might be due to invalid IL or missing references) //IL_0595: Expected O, but got Unknown //IL_05b7: Unknown result type (might be due to invalid IL or missing references) //IL_05c1: Expected O, but got Unknown //IL_05e3: Unknown result type (might be due to invalid IL or missing references) //IL_05ed: Expected O, but got Unknown //IL_061d: Unknown result type (might be due to invalid IL or missing references) //IL_0627: Expected O, but got Unknown //IL_0657: Unknown result type (might be due to invalid IL or missing references) //IL_0661: Expected O, but got Unknown //IL_0691: Unknown result type (might be due to invalid IL or missing references) //IL_069b: Expected O, but got Unknown //IL_06cb: Unknown result type (might be due to invalid IL or missing references) //IL_06d5: Expected O, but got Unknown //IL_0705: Unknown result type (might be due to invalid IL or missing references) //IL_070f: Expected O, but got Unknown //IL_073f: Unknown result type (might be due to invalid IL or missing references) //IL_0749: Expected O, but got Unknown //IL_0779: Unknown result type (might be due to invalid IL or missing references) //IL_0783: Expected O, but got Unknown //IL_07b3: Unknown result type (might be due to invalid IL or missing references) //IL_07bd: Expected O, but got Unknown //IL_07ed: Unknown result type (might be due to invalid IL or missing references) //IL_07f7: Expected O, but got Unknown //IL_0827: Unknown result type (might be due to invalid IL or missing references) //IL_0831: Expected O, but got Unknown //IL_0861: Unknown result type (might be due to invalid IL or missing references) //IL_086b: Expected O, but got Unknown //IL_089b: Unknown result type (might be due to invalid IL or missing references) //IL_08a5: Expected O, but got Unknown //IL_08cd: Unknown result type (might be due to invalid IL or missing references) //IL_08d7: Expected O, but got Unknown CfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, new ConfigDescription("Enable this mod.このMODを有効化", (AcceptableValueBase)null, Array.Empty<object>())); CfgHostOnlyInMultiplayer = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "HostOnlyInMultiplayer", true, new ConfigDescription("Run only on host in multiplayer.マルチではホストのみ実行", (AcceptableValueBase)null, Array.Empty<object>())); CfgTriggerOnShopEnter = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "TriggerOnShopEnter", true, new ConfigDescription("Shuffle automatically when entering shop.ショップ入場時に自動シャッフル", (AcceptableValueBase)null, Array.Empty<object>())); CfgRunOncePerShopVisit = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RunOncePerShopVisit", true, new ConfigDescription("Run only once per shop visit.同一ショップ滞在中は1回だけ実行", (AcceptableValueBase)null, Array.Empty<object>())); CfgShuffleEveryNLevels = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ShuffleEveryNLevels", 1, new ConfigDescription("Run shuffle every N shop visits.ショップ訪問N回ごとにシャッフル実行", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>())); CfgShuffleMode = ((BaseUnityPlugin)this).Config.Bind<ShuffleMode>("General", "ShuffleMode", ShuffleMode.FullRandomKeepGrandTotal, new ConfigDescription("Shuffle mode by name.PreserveTypeTotalsRandom / FullRandomKeepGrandTotal / ReassignWholeBuilds / RandomPattern の名前選択", (AcceptableValueBase)null, Array.Empty<object>())); CfgEnableRandomPatternMode3 = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableRandomPatternMode3", true, new ConfigDescription("Allow RandomPattern mode execution.RandomPatternモード実行を許可", (AcceptableValueBase)null, Array.Empty<object>())); CfgCallStatSyncAll = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CallStatSyncAll", true, new ConfigDescription("Call SemiFunc.StatSyncAll after apply.適用後にSemiFunc.StatSyncAllを呼ぶ", (AcceptableValueBase)null, Array.Empty<object>())); CfgUseFixedSeed = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UseFixedSeed", false, new ConfigDescription("Use fixed seed for reproducible results.再現性のため固定シードを使用", (AcceptableValueBase)null, Array.Empty<object>())); CfgFixedSeed = ((BaseUnityPlugin)this).Config.Bind<int>("General", "FixedSeed", 12345, new ConfigDescription("Fixed random seed value.固定乱数シード値", (AcceptableValueBase)null, Array.Empty<object>())); CfgChaosRandom = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ChaosRandom", 90, new ConfigDescription("Imbalance intensity for mode 0 and 1 from 0 to 100.モード0と1の偏り強度 0から100", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); CfgLogSummary = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogSummary", true, new ConfigDescription("Log summary messages.サマリーログ出力", (AcceptableValueBase)null, Array.Empty<object>())); CfgLogDetails = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogDetails", false, new ConfigDescription("Log per-player details.プレイヤーごとの詳細ログ出力", (AcceptableValueBase)null, Array.Empty<object>())); CfgLogExcludedUpgrades = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogExcludedUpgrades", true, new ConfigDescription("Log excluded upgrade list.除外アップグレード一覧を出力", (AcceptableValueBase)null, Array.Empty<object>())); CfgEnableUndoCommand = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableUndoCommand", true, new ConfigDescription("Enable /undo command./undoコマンドを有効化", (AcceptableValueBase)null, Array.Empty<object>())); CfgUndoRequireHost = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UndoRequireHost", true, new ConfigDescription("Require host for /undo in multiplayer.マルチ時の/undoをホスト限定", (AcceptableValueBase)null, Array.Empty<object>())); CfgUndoHistoryKeepCount = ((BaseUnityPlugin)this).Config.Bind<int>("General", "UndoHistoryKeepCount", 1, new ConfigDescription("Number of undo snapshots to keep.Undo履歴保持件数", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), Array.Empty<object>())); CfgEnableMaxClampSafety = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableMaxClampSafety", true, new ConfigDescription("Clamp each upgrade by max settings for safety.安全のため各アップグレードを上限クランプ", (AcceptableValueBase)null, Array.Empty<object>())); CfgEnableMode0 = ((BaseUnityPlugin)this).Config.Bind<bool>("RandomPatternCandidates", "EnableMode_PreserveTypeTotalsRandom", true, new ConfigDescription("Include PreserveTypeTotalsRandom in RandomPattern.RandomPattern候補にPreserveTypeTotalsRandomを含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgEnableMode1 = ((BaseUnityPlugin)this).Config.Bind<bool>("RandomPatternCandidates", "EnableMode_FullRandomKeepGrandTotal", true, new ConfigDescription("Include FullRandomKeepGrandTotal in RandomPattern.RandomPattern候補にFullRandomKeepGrandTotalを含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgEnableMode2 = ((BaseUnityPlugin)this).Config.Bind<bool>("RandomPatternCandidates", "EnableMode_ReassignWholeBuilds", true, new ConfigDescription("Include ReassignWholeBuilds in RandomPattern.RandomPattern候補にReassignWholeBuildsを含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeHealth = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeHealth", true, new ConfigDescription("Include HEALTH in shuffle.HEALTHをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeStamina = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeStamina", true, new ConfigDescription("Include STAMINA in shuffle.STAMINAをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeExtraJump = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeExtraJump", true, new ConfigDescription("Include EXTRA JUMP in shuffle.EXTRA JUMPをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeTumbleLaunch = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeTumbleLaunch", true, new ConfigDescription("Include TUMBLE LAUNCH in shuffle.TUMBLE LAUNCHをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeTumbleClimb = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeTumbleClimb", true, new ConfigDescription("Include TUMBLE CLIMB in shuffle.TUMBLE CLIMBをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeMapPlayerCount = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeMapPlayerCount", false, new ConfigDescription("Include MAP PLAYER COUNT in shuffle.MAP PLAYER COUNTをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeDeathHeadBattery = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeDeathHeadBattery", true, new ConfigDescription("Include DEATH HEAD BATTERY in shuffle.DEATH HEAD BATTERYをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeTumbleWings = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeTumbleWings", true, new ConfigDescription("Include TUMBLE WINGS in shuffle.TUMBLE WINGSをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeSprintSpeed = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeSprintSpeed", true, new ConfigDescription("Include SPRINT SPEED in shuffle.SPRINT SPEEDをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeCrouchRest = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeCrouchRest", true, new ConfigDescription("Include CROUCH REST in shuffle.CROUCH RESTをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeGrabStrength = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeGrabStrength", true, new ConfigDescription("Include GRAB STRENGTH in shuffle.GRAB STRENGTHをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeThrow = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeThrow", false, new ConfigDescription("Include THROW in shuffle.THROWをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgIncludeGrabRange = ((BaseUnityPlugin)this).Config.Bind<bool>("IncludeUpgrade", "IncludeGrabRange", true, new ConfigDescription("Include GRAB RANGE in shuffle.GRAB RANGEをシャッフル対象に含める", (AcceptableValueBase)null, Array.Empty<object>())); CfgMaxHealth = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxHealth", 999, new ConfigDescription("Max HEALTH per player.1人あたりのHEALTH最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxStamina = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxStamina", 999, new ConfigDescription("Max STAMINA per player.1人あたりのSTAMINA最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxExtraJump = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxExtraJump", 999, new ConfigDescription("Max EXTRA JUMP per player.1人あたりのEXTRA JUMP最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxTumbleLaunch = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxTumbleLaunch", 999, new ConfigDescription("Max TUMBLE LAUNCH per player.1人あたりのTUMBLE LAUNCH最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxTumbleClimb = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxTumbleClimb", 999, new ConfigDescription("Max TUMBLE CLIMB per player.1人あたりのTUMBLE CLIMB最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxMapPlayerCount = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxMapPlayerCount", 999, new ConfigDescription("Max MAP PLAYER COUNT per player.1人あたりのMAP PLAYER COUNT最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxDeathHeadBattery = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxDeathHeadBattery", 999, new ConfigDescription("Max DEATH HEAD BATTERY per player.1人あたりのDEATH HEAD BATTERY最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxTumbleWings = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxTumbleWings", 999, new ConfigDescription("Max TUMBLE WINGS per player.1人あたりのTUMBLE WINGS最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxSprintSpeed = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxSprintSpeed", 999, new ConfigDescription("Max SPRINT SPEED per player.1人あたりのSPRINT SPEED最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxCrouchRest = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxCrouchRest", 999, new ConfigDescription("Max CROUCH REST per player.1人あたりのCROUCH REST最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxGrabStrength = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxGrabStrength", 999, new ConfigDescription("Max GRAB STRENGTH per player.1人あたりのGRAB STRENGTH最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxThrow = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxThrow", 999, new ConfigDescription("Max THROW per player.1人あたりのTHROW最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>())); CfgMaxGrabRange = ((BaseUnityPlugin)this).Config.Bind<int>("UpgradeMaxClamp", "MaxGrabRange", 8, new ConfigDescription("Max GRAB RANGE per player.1人あたりのGRAB RANGE最大値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 8), Array.Empty<object>())); } private void ResetRandom() { if (CfgUseFixedSeed != null && CfgUseFixedSeed.Value) { _random = new Random(CfgFixedSeed.Value); } else { _random = new Random(Environment.TickCount ^ DateTime.Now.Millisecond); } } private bool CanRunHere() { if (CfgHostOnlyInMultiplayer.Value && SemiFunc.IsMultiplayer() && !PhotonNetwork.IsMasterClient) { return false; } return true; } private bool SafeRunIsShop() { try { return SemiFunc.RunIsShop(); } catch { return false; } } private bool SafeLevelGenDone() { try { return SemiFunc.LevelGenDone(); } catch { return false; } } private void TryShuffleNow(string reason) { if (_lastAppliedFrame == Time.frameCount) { return; } List<PlayerUpgradeBuild> list = BuildPlayerList(); if (list.Count <= 0) { LogInfo("Skip shuffle: no valid players"); return; } List<UpgradeKind> includedKinds = GetIncludedKinds(); if (includedKinds.Count <= 0) { LogInfo("Skip shuffle: no included upgrades"); return; } if (CfgLogExcludedUpgrades.Value) { List<string> list2 = (from UpgradeKind k in Enum.GetValues(typeof(UpgradeKind)) where !includedKinds.Contains(k) select k.ToString()).ToList(); LogInfo("ExcludedUpgrades: " + string.Join(", ", list2.ToArray())); } ReadCurrentUpgrades(list); ShuffleMode shuffleMode = ResolveMode(CfgShuffleMode.Value, list.Count); ShuffleSnapshot shuffleSnapshot = new ShuffleSnapshot { ShopVisitCount = _shopVisitCount, ModeName = shuffleMode.ToString() }; foreach (PlayerUpgradeBuild item in list) { shuffleSnapshot.SteamIds.Add(item.SteamId); shuffleSnapshot.Before[item.SteamId] = CloneValues(item.Values); } Dictionary<string, Dictionary<UpgradeKind, int>> targets = CreateTargets(list, includedKinds, shuffleMode); MergeExcludedValues(list, targets, includedKinds); ApplyClampToTargets(targets, includeExcluded: false, includedKinds); if (CfgLogDetails.Value) { LogPlayerValues("Before", list); LogTargets("Target", list, targets); } if (!ApplyTargetsWithBatchedDeltas(list, targets, includedKinds)) { LogWarn("Shuffle apply failed"); return; } ReadCurrentUpgrades(list); foreach (PlayerUpgradeBuild item2 in list) { shuffleSnapshot.After[item2.SteamId] = CloneValues(item2.Values); } EnqueueUndo(shuffleSnapshot); _ranThisShopVisit = true; _lastAppliedFrame = Time.frameCount; if (CfgLogSummary.Value) { LogInfo($"Shuffle applied. Reason={reason} Mode={shuffleMode} Players={list.Count} ShopCount={_shopVisitCount} ChaosRandom={CfgChaosRandom.Value}"); } if (CfgLogDetails.Value) { LogPlayerValues("After", list); } } private List<PlayerUpgradeBuild> BuildPlayerList() { List<PlayerUpgradeBuild> list = new List<PlayerUpgradeBuild>(); if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null) { return list; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null) { continue; } string text; try { text = player.steamID; } catch { text = ""; } if (string.IsNullOrEmpty(text)) { continue; } string displayName = text; try { string text2 = SemiFunc.PlayerGetName(player); if (!string.IsNullOrEmpty(text2)) { displayName = text2; } } catch { } PlayerUpgradeBuild playerUpgradeBuild = new PlayerUpgradeBuild { SteamId = text, DisplayName = displayName }; foreach (UpgradeKind value in Enum.GetValues(typeof(UpgradeKind))) { playerUpgradeBuild.Values[value] = 0; } list.Add(playerUpgradeBuild); } return list.OrderBy<PlayerUpgradeBuild, string>((PlayerUpgradeBuild p) => p.SteamId, StringComparer.Ordinal).ToList(); } private void ReadCurrentUpgrades(List<PlayerUpgradeBuild> players) { StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null) { return; } foreach (PlayerUpgradeBuild player in players) { player.Values[UpgradeKind.Health] = GetDictValue(instance.playerUpgradeHealth, player.SteamId); player.Values[UpgradeKind.Stamina] = GetDictValue(instance.playerUpgradeStamina, player.SteamId); player.Values[UpgradeKind.ExtraJump] = GetDictValue(instance.playerUpgradeExtraJump, player.SteamId); player.Values[UpgradeKind.TumbleLaunch] = GetDictValue(instance.playerUpgradeLaunch, player.SteamId); player.Values[UpgradeKind.TumbleClimb] = GetDictValue(instance.playerUpgradeTumbleClimb, player.SteamId); player.Values[UpgradeKind.MapPlayerCount] = GetDictValue(instance.playerUpgradeMapPlayerCount, player.SteamId); player.Values[UpgradeKind.DeathHeadBattery] = GetDictValue(instance.playerUpgradeDeathHeadBattery, player.SteamId); player.Values[UpgradeKind.TumbleWings] = GetDictValue(instance.playerUpgradeTumbleWings, player.SteamId); player.Values[UpgradeKind.SprintSpeed] = GetDictValue(instance.playerUpgradeSpeed, player.SteamId); player.Values[UpgradeKind.CrouchRest] = GetDictValue(instance.playerUpgradeCrouchRest, player.SteamId); player.Values[UpgradeKind.GrabStrength] = GetDictValue(instance.playerUpgradeStrength, player.SteamId); player.Values[UpgradeKind.Throw] = GetDictValue(instance.playerUpgradeThrow, player.SteamId); player.Values[UpgradeKind.GrabRange] = GetDictValue(instance.playerUpgradeRange, player.SteamId); } } private int GetDictValue(Dictionary<string, int> dict, string key) { if (dict == null || string.IsNullOrEmpty(key)) { return 0; } if (dict.TryGetValue(key, out var value)) { return Mathf.Max(0, value); } return 0; } private List<UpgradeKind> GetIncludedKinds() { List<UpgradeKind> list = new List<UpgradeKind>(); if (CfgIncludeHealth.Value) { list.Add(UpgradeKind.Health); } if (CfgIncludeStamina.Value) { list.Add(UpgradeKind.Stamina); } if (CfgIncludeExtraJump.Value) { list.Add(UpgradeKind.ExtraJump); } if (CfgIncludeTumbleLaunch.Value) { list.Add(UpgradeKind.TumbleLaunch); } if (CfgIncludeTumbleClimb.Value) { list.Add(UpgradeKind.TumbleClimb); } if (CfgIncludeMapPlayerCount.Value) { list.Add(UpgradeKind.MapPlayerCount); } if (CfgIncludeDeathHeadBattery.Value) { list.Add(UpgradeKind.DeathHeadBattery); } if (CfgIncludeTumbleWings.Value) { list.Add(UpgradeKind.TumbleWings); } if (CfgIncludeSprintSpeed.Value) { list.Add(UpgradeKind.SprintSpeed); } if (CfgIncludeCrouchRest.Value) { list.Add(UpgradeKind.CrouchRest); } if (CfgIncludeGrabStrength.Value) { list.Add(UpgradeKind.GrabStrength); } if (CfgIncludeThrow.Value) { list.Add(UpgradeKind.Throw); } if (CfgIncludeGrabRange.Value) { list.Add(UpgradeKind.GrabRange); } return list; } private ShuffleMode ResolveMode(ShuffleMode configured, int playerCount) { if (configured == ShuffleMode.RandomPattern && !CfgEnableRandomPatternMode3.Value) { return ShuffleMode.PreserveTypeTotalsRandom; } if (configured == ShuffleMode.ReassignWholeBuilds && playerCount < 2) { return ShuffleMode.PreserveTypeTotalsRandom; } if (configured != ShuffleMode.RandomPattern) { return configured; } List<ShuffleMode> list = new List<ShuffleMode>(); if (CfgEnableMode0.Value) { list.Add(ShuffleMode.PreserveTypeTotalsRandom); } if (CfgEnableMode1.Value) { list.Add(ShuffleMode.FullRandomKeepGrandTotal); } if (CfgEnableMode2.Value && playerCount >= 2) { list.Add(ShuffleMode.ReassignWholeBuilds); } if (list.Count <= 0) { return ShuffleMode.PreserveTypeTotalsRandom; } int index = _random.Next(list.Count); return list[index]; } private Dictionary<string, Dictionary<UpgradeKind, int>> CreateTargets(List<PlayerUpgradeBuild> players, List<UpgradeKind> includedKinds, ShuffleMode mode) { Dictionary<string, Dictionary<UpgradeKind, int>> dictionary = new Dictionary<string, Dictionary<UpgradeKind, int>>(); foreach (PlayerUpgradeBuild player in players) { dictionary[player.SteamId] = new Dictionary<UpgradeKind, int>(); foreach (UpgradeKind value in Enum.GetValues(typeof(UpgradeKind))) { dictionary[player.SteamId][value] = 0; } } PlayerBiasProfile profile = BuildBiasProfiles(players, includedKinds); switch (mode) { case ShuffleMode.PreserveTypeTotalsRandom: CreateTargetsMode0(players, includedKinds, dictionary, profile); break; case ShuffleMode.FullRandomKeepGrandTotal: CreateTargetsMode1(players, includedKinds, dictionary, profile); break; case ShuffleMode.ReassignWholeBuilds: CreateTargetsMode2(players, includedKinds, dictionary); break; default: CreateTargetsMode0(players, includedKinds, dictionary, profile); break; } return dictionary; } private PlayerBiasProfile BuildBiasProfiles(List<PlayerUpgradeBuild> players, List<UpgradeKind> includedKinds) { PlayerBiasProfile playerBiasProfile = new PlayerBiasProfile(); int num = Mathf.Clamp(CfgChaosRandom.Value, 0, 100); float num2 = (float)num / 100f; foreach (PlayerUpgradeBuild player in players) { float num3 = Mathf.Lerp(1f, 0.9f, num2); float num4 = Mathf.Lerp(1f, 1.15f, num2); float value = Mathf.Lerp(num3, num4, (float)_random.NextDouble()); playerBiasProfile.PlayerTotalWeight[player.SteamId] = value; } foreach (PlayerUpgradeBuild player2 in players) { Dictionary<UpgradeKind, float> dictionary = new Dictionary<UpgradeKind, float>(); foreach (UpgradeKind includedKind in includedKinds) { dictionary[includedKind] = 1f; } if (includedKinds.Count <= 0) { playerBiasProfile.KindWeightByPlayer[player2.SteamId] = dictionary; continue; } int num5 = Mathf.Max(1, includedKinds.Count); int num6 = Mathf.Clamp(1 + (int)Mathf.Floor(num2 * (float)Mathf.Min(3, num5 - 1)), 1, num5); int num7 = Mathf.Clamp(1 + (int)Mathf.Floor(num2 * (float)Mathf.Min(3, num5 - 1)), 1, num5); List<UpgradeKind> list = includedKinds.OrderBy((UpgradeKind _) => _random.Next()).ToList(); for (int i = 0; i < num6 && i < list.Count; i++) { UpgradeKind key = list[i]; float num8 = Mathf.Lerp(1f, 3.5f, num2); dictionary[key] *= num8; } for (int j = 0; j < num7 && j < list.Count; j++) { UpgradeKind key2 = list[list.Count - 1 - j]; float num9 = Mathf.Lerp(1f, 0.3f, num2); dictionary[key2] *= num9; } bool flag = false; foreach (UpgradeKind includedKind2 in includedKinds) { dictionary[includedKind2] = Mathf.Clamp(dictionary[includedKind2], 0.1f, 100f); if (dictionary[includedKind2] >= 1.2f) { flag = true; } } if (!flag && includedKinds.Count > 0) { UpgradeKind key3 = includedKinds[_random.Next(includedKinds.Count)]; dictionary[key3] = Mathf.Max(dictionary[key3], Mathf.Lerp(1.4f, 2.4f, num2)); } playerBiasProfile.KindWeightByPlayer[player2.SteamId] = dictionary; } return playerBiasProfile; } private void CreateTargetsMode0(List<PlayerUpgradeBuild> players, List<UpgradeKind> includedKinds, Dictionary<string, Dictionary<UpgradeKind, int>> targets, PlayerBiasProfile profile) { int num = 0; Dictionary<UpgradeKind, int> dictionary = new Dictionary<UpgradeKind, int>(); foreach (UpgradeKind includedKind in includedKinds) { int num2 = 0; foreach (PlayerUpgradeBuild player in players) { num2 += Mathf.Max(0, player.Values[includedKind]); } dictionary[includedKind] = num2; num += num2; } Dictionary<string, int> dictionary2 = BuildPlayerQuotas(players, num, profile); Dictionary<string, Dictionary<UpgradeKind, int>> caps = BuildRemainingCaps(players); foreach (UpgradeKind includedKind2 in includedKinds) { for (int num3 = dictionary[includedKind2]; num3 > 0; num3--) { List<PlayerUpgradeBuild> list = new List<PlayerUpgradeBuild>(); foreach (PlayerUpgradeBuild player2 in players) { if (HasCapacity(player2.SteamId, includedKind2, caps)) { int num4 = SumIncludedForPlayer(targets[player2.SteamId], includedKinds); int num5 = dictionary2[player2.SteamId]; int num6 = Mathf.Max(1, num3 / Mathf.Max(1, players.Count)); if (num4 <= num5 + num6) { list.Add(player2); } } } if (list.Count <= 0) { foreach (PlayerUpgradeBuild player3 in players) { if (HasCapacity(player3.SteamId, includedKind2, caps)) { list.Add(player3); } } } if (list.Count <= 0) { break; } PlayerUpgradeBuild playerUpgradeBuild = WeightedPickPlayerForKindMode0(list, includedKind2, targets, includedKinds, dictionary2, profile); targets[playerUpgradeBuild.SteamId][includedKind2]++; ConsumeCap(playerUpgradeBuild.SteamId, includedKind2, caps); } } RebalanceMode0PlayerTotals(players, includedKinds, targets, dictionary2); } private void CreateTargetsMode1(List<PlayerUpgradeBuild> players, List<UpgradeKind> includedKinds, Dictionary<string, Dictionary<UpgradeKind, int>> targets, PlayerBiasProfile profile) { int num = 0; foreach (PlayerUpgradeBuild player in players) { foreach (UpgradeKind includedKind in includedKinds) { num += Mathf.Max(0, player.Values[includedKind]); } } Dictionary<string, int> dictionary = BuildPlayerQuotas(players, num, profile); Dictionary<string, Dictionary<UpgradeKind, int>> caps = BuildRemainingCaps(players); foreach (PlayerUpgradeBuild player2 in players) { for (int num2 = dictionary[player2.SteamId]; num2 > 0; num2--) { List<UpgradeKind> list = new List<UpgradeKind>(); foreach (UpgradeKind includedKind2 in includedKinds) { if (HasCapacity(player2.SteamId, includedKind2, caps)) { list.Add(includedKind2); } } if (list.Count <= 0) { break; } UpgradeKind upgradeKind = WeightedPickKindForPlayerMode1(player2.SteamId, list, targets[player2.SteamId], includedKinds, profile); targets[player2.SteamId][upgradeKind]++; ConsumeCap(player2.SteamId, upgradeKind, caps); } } int num3 = 0; foreach (PlayerUpgradeBuild player3 in players) { num3 += SumIncludedForPlayer(targets[player3.SteamId], includedKinds); } for (int num4 = num - num3; num4 > 0; num4--) { List<PlayerUpgradeBuild> list2 = new List<PlayerUpgradeBuild>(); foreach (PlayerUpgradeBuild player4 in players) { bool flag = false; foreach (UpgradeKind includedKind3 in includedKinds) { if (HasCapacity(player4.SteamId, includedKind3, caps)) { flag = true; break; } } if (flag) { list2.Add(player4); } } if (list2.Count <= 0) { break; } PlayerUpgradeBuild playerUpgradeBuild = WeightedPickPlayerByTotalNeed(list2, targets, includedKinds, dictionary, profile); List<UpgradeKind> list3 = new List<UpgradeKind>(); foreach (UpgradeKind includedKind4 in includedKinds) { if (HasCapacity(playerUpgradeBuild.SteamId, includedKind4, caps)) { list3.Add(includedKind4); } } if (list3.Count <= 0) { break; } UpgradeKind upgradeKind2 = WeightedPickKindForPlayerMode1(playerUpgradeBuild.SteamId, list3, targets[playerUpgradeBuild.SteamId], includedKinds, profile); targets[playerUpgradeBuild.SteamId][upgradeKind2]++; ConsumeCap(playerUpgradeBuild.SteamId, upgradeKind2, caps); } } private void CreateTargetsMode2(List<PlayerUpgradeBuild> players, List<UpgradeKind> includedKinds, Dictionary<string, Dictionary<UpgradeKind, int>> targets) { if (players.Count < 2) { foreach (PlayerUpgradeBuild player in players) { foreach (UpgradeKind includedKind in includedKinds) { targets[player.SteamId][includedKind] = Mathf.Max(0, player.Values[includedKind]); } } return; } List<int> list = BuildDerangement(players.Count); for (int i = 0; i < players.Count; i++) { int index = list[i]; foreach (UpgradeKind includedKind2 in includedKinds) { targets[players[i].SteamId][includedKind2] = Mathf.Max(0, players[index].Values[includedKind2]); } } } private Dictionary<string, int> BuildPlayerQuotas(List<PlayerUpgradeBuild> players, int grandTotal, PlayerBiasProfile profile) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); int num = Mathf.Max(1, players.Count); int value = grandTotal / num; int num2 = grandTotal % num; foreach (PlayerUpgradeBuild player in players) { dictionary[player.SteamId] = value; } if (num2 > 0) { float num3 = 0f; Dictionary<string, float> dictionary2 = new Dictionary<string, float>(); foreach (PlayerUpgradeBuild player2 in players) { float num4 = 1f; if (profile != null && profile.PlayerTotalWeight.ContainsKey(player2.SteamId)) { num4 = Mathf.Max(0.01f, profile.PlayerTotalWeight[player2.SteamId]); } dictionary2[player2.SteamId] = num4; num3 += num4; } while (num2 > 0) { dictionary[WeightedPickSteamId(dictionary2, num3)]++; num2--; } } return dictionary; } private PlayerUpgradeBuild WeightedPickPlayerForKindMode0(List<PlayerUpgradeBuild> candidates, UpgradeKind kind, Dictionary<string, Dictionary<UpgradeKind, int>> targets, List<UpgradeKind> includedKinds, Dictionary<string, int> playerQuotas, PlayerBiasProfile profile) { Dictionary<string, float> dictionary = new Dictionary<string, float>(); float num = 0f; foreach (PlayerUpgradeBuild candidate in candidates) { float num2 = 1f; if (profile != null && profile.KindWeightByPlayer.ContainsKey(candidate.SteamId) && profile.KindWeightByPlayer[candidate.SteamId].ContainsKey(kind)) { num2 = Mathf.Max(0.05f, profile.KindWeightByPlayer[candidate.SteamId][kind]); } int num3 = SumIncludedForPlayer(targets[candidate.SteamId], includedKinds); int num4 = playerQuotas[candidate.SteamId]; float num5 = ((num3 >= num4) ? (0.65f / (1f + (float)(num3 - num4) * 0.25f)) : (1.25f + (float)(num4 - num3) * 0.15f)); float num6 = Mathf.Clamp(num2 * num5, 0.01f, 100000f); dictionary[candidate.SteamId] = num6; num += num6; } string selectedId = WeightedPickSteamId(dictionary, num); return candidates.First((PlayerUpgradeBuild x) => x.SteamId == selectedId); } private PlayerUpgradeBuild WeightedPickPlayerByTotalNeed(List<PlayerUpgradeBuild> candidates, Dictionary<string, Dictionary<UpgradeKind, int>> targets, List<UpgradeKind> includedKinds, Dictionary<string, int> playerQuotas, PlayerBiasProfile profile) { Dictionary<string, float> dictionary = new Dictionary<string, float>(); float num = 0f; foreach (PlayerUpgradeBuild candidate in candidates) { int num2 = SumIncludedForPlayer(targets[candidate.SteamId], includedKinds); int num3 = (playerQuotas.ContainsKey(candidate.SteamId) ? playerQuotas[candidate.SteamId] : 0); float num4 = 1f + (float)Mathf.Max(0, num3 - num2) * 0.2f; float num5 = 1f; if (profile != null && profile.PlayerTotalWeight.ContainsKey(candidate.SteamId)) { num5 = Mathf.Max(0.05f, profile.PlayerTotalWeight[candidate.SteamId]); } float num6 = Mathf.Clamp(num4 * num5, 0.01f, 100000f); dictionary[candidate.SteamId] = num6; num += num6; } string selectedId = WeightedPickSteamId(dictionary, num); return candidates.First((PlayerUpgradeBuild x) => x.SteamId == selectedId); } private UpgradeKind WeightedPickKindForPlayerMode1(string steamId, List<UpgradeKind> kindCandidates, Dictionary<UpgradeKind, int> currentMap, List<UpgradeKind> includedKinds, PlayerBiasProfile profile) { Dictionary<UpgradeKind, float> dictionary = new Dictionary<UpgradeKind, float>(); float num = 0f; foreach (UpgradeKind kindCandidate in kindCandidates) { float num2 = 1f; if (profile != null && profile.KindWeightByPlayer.ContainsKey(steamId) && profile.KindWeightByPlayer[steamId].ContainsKey(kindCandidate)) { num2 = Mathf.Max(0.05f, profile.KindWeightByPlayer[steamId][kindCandidate]); } int num3 = (currentMap.ContainsKey(kindCandidate) ? currentMap[kindCandidate] : 0); float num4 = 1f / (1f + (float)num3 * 0.12f); float num5 = Mathf.Clamp01((float)CfgChaosRandom.Value / 100f); num4 = Mathf.Lerp(num4, 1f, num5 * 0.55f); float num7 = (dictionary[kindCandidate] = Mathf.Clamp(num2 * num4, 0.01f, 100000f)); num += num7; } return WeightedPickKind(dictionary, num); } private void RebalanceMode0PlayerTotals(List<PlayerUpgradeBuild> players, List<UpgradeKind> includedKinds, Dictionary<string, Dictionary<UpgradeKind, int>> targets, Dictionary<string, int> playerQuotas) { if (players.Count < 2) { return; } int num = players.Count * includedKinds.Count * 8; for (int i = 0; i < num; i++) { PlayerUpgradeBuild playerUpgradeBuild = null; PlayerUpgradeBuild playerUpgradeBuild2 = null; int num2 = 0; int num3 = 0; foreach (PlayerUpgradeBuild player in players) { int num4 = SumIncludedForPlayer(targets[player.SteamId], includedKinds); int num5 = playerQuotas[player.SteamId]; int num6 = num5 - num4; if (num6 > num2) { num2 = num6; playerUpgradeBuild = player; } if (-num6 > num3) { num3 = -num6; playerUpgradeBuild2 = player; } } if (playerUpgradeBuild == null || playerUpgradeBuild2 == null || num2 <= 0 || num3 <= 0) { break; } UpgradeKind? upgradeKind = null; float num7 = float.MinValue; foreach (UpgradeKind includedKind in includedKinds) { int num8 = targets[playerUpgradeBuild2.SteamId][includedKind]; if (num8 <= 0) { continue; } if (CfgEnableMaxClampSafety.Value) { int maxForKind = GetMaxForKind(includedKind); if (targets[playerUpgradeBuild.SteamId][includedKind] >= maxForKind) { continue; } } float num9 = 1f + (float)Mathf.Max(0, 3 - targets[playerUpgradeBuild.SteamId][includedKind]) * 0.25f; float num10 = 1f + (float)Mathf.Max(0, targets[playerUpgradeBuild2.SteamId][includedKind] - 3) * 0.15f; float num11 = num9 + num10; if (num11 > num7) { num7 = num11; upgradeKind = includedKind; } } if (!upgradeKind.HasValue) { break; } targets[playerUpgradeBuild2.SteamId][upgradeKind.Value]--; targets[playerUpgradeBuild.SteamId][upgradeKind.Value]++; } } private List<int> BuildDerangement(int count) { if (count == 2) { return new List<int> { 1, 0 }; } for (int i = 0; i < 64; i++) { List<int> list = Enumerable.Range(0, count).ToList(); for (int num = list.Count - 1; num > 0; num--) { int index = _random.Next(num + 1); int value = list[num]; list[num] = list[index]; list[index] = value; } bool flag = true; for (int j = 0; j < count; j++) { if (list[j] == j) { flag = false; break; } } if (flag) { return list; } } List<int> list2 = new List<int>(); int num2 = _random.Next(1, count); for (int k = 0; k < count; k++) { list2.Add((k + num2) % count); } return list2; } private void MergeExcludedValues(List<PlayerUpgradeBuild> players, Dictionary<string, Dictionary<UpgradeKind, int>> targets, List<UpgradeKind> includedKinds) { HashSet<UpgradeKind> hashSet = new HashSet<UpgradeKind>(includedKinds); foreach (PlayerUpgradeBuild player in players) { foreach (UpgradeKind value in Enum.GetValues(typeof(UpgradeKind))) { if (!hashSet.Contains(value)) { targets[player.SteamId][value] = Mathf.Max(0, player.Values[value]); } } } } private void ApplyClampToTargets(Dictionary<string, Dictionary<UpgradeKind, int>> targets, bool includeExcluded, List<UpgradeKind> includedKinds) { if (!CfgEnableMaxClampSafety.Value) { return; } HashSet<UpgradeKind> hashSet = new HashSet<UpgradeKind>(includedKinds); foreach (string item in targets.Keys.ToList()) { Dictionary<UpgradeKind, int> dictionary = targets[item]; foreach (UpgradeKind item2 in dictionary.Keys.ToList()) { if (includeExcluded || hashSet.Contains(item2)) { dictionary[item2] = Mathf.Clamp(dictionary[item2], 0, GetMaxForKind(item2)); } } } } private bool ApplyTargetsWithBatchedDeltas(List<PlayerUpgradeBuild> players, Dictionary<string, Dictionary<UpgradeKind, int>> targets, List<UpgradeKind> includedKinds) { if ((Object)(object)PunManager.instance == (Object)null || (Object)(object)StatsManager.instance == (Object)null) { LogWarn("PunManager or StatsManager is null"); return false; } ApplyClampToTargets(targets, includeExcluded: false, includedKinds); foreach (PlayerUpgradeBuild player in players) { Dictionary<UpgradeKind, int> values = player.Values; Dictionary<UpgradeKind, int> dictionary = targets[player.SteamId]; foreach (UpgradeKind includedKind in includedKinds) { int num = (values.ContainsKey(includedKind) ? Mathf.Max(0, values[includedKind]) : 0); int num2 = (dictionary.ContainsKey(includedKind) ? Mathf.Max(0, dictionary[includedKind]) : 0); if (CfgEnableMaxClampSafety.Value) { num2 = Mathf.Clamp(num2, 0, GetMaxForKind(includedKind)); } int num3 = num2 - num; if (num3 != 0) { try { ApplyDelta(player.SteamId, includedKind, num3); } catch (Exception ex) { LogWarn($"ApplyDelta failed. Player={player.DisplayName} Kind={includedKind} Delta={num3} Err={ex.Message}"); return false; } } } } if (CfgCallStatSyncAll.Value) { try { SemiFunc.StatSyncAll(); } catch (Exception ex2) { LogWarn("StatSyncAll failed. " + ex2.Message); } } return true; } private void ApplyDelta(string steamId, UpgradeKind kind, int delta) { switch (kind) { case UpgradeKind.Health: PunManager.instance.UpgradePlayerHealth(steamId, delta); break; case UpgradeKind.Stamina: PunManager.instance.UpgradePlayerEnergy(steamId, delta); break; case UpgradeKind.ExtraJump: PunManager.instance.UpgradePlayerExtraJump(steamId, delta); break; case UpgradeKind.TumbleLaunch: PunManager.instance.UpgradePlayerTumbleLaunch(steamId, delta); break; case UpgradeKind.TumbleClimb: PunManager.instance.UpgradePlayerTumbleClimb(steamId, delta); break; case UpgradeKind.MapPlayerCount: PunManager.instance.UpgradeMapPlayerCount(steamId, delta); break; case UpgradeKind.DeathHeadBattery: PunManager.instance.UpgradeDeathHeadBattery(steamId, delta); break; case UpgradeKind.TumbleWings: PunManager.instance.UpgradePlayerTumbleWings(steamId, delta); break; case UpgradeKind.SprintSpeed: PunManager.instance.UpgradePlayerSprintSpeed(steamId, delta); break; case UpgradeKind.CrouchRest: PunManager.instance.UpgradePlayerCrouchRest(steamId, delta); break; case UpgradeKind.GrabStrength: PunManager.instance.UpgradePlayerGrabStrength(steamId, delta); break; case UpgradeKind.Throw: PunManager.instance.UpgradePlayerThrowStrength(steamId, delta); break; case UpgradeKind.GrabRange: PunManager.instance.UpgradePlayerGrabRange(steamId, delta); break; } } private int GetMaxForKind(UpgradeKind kind) { return kind switch { UpgradeKind.Health => Mathf.Max(0, CfgMaxHealth.Value), UpgradeKind.Stamina => Mathf.Max(0, CfgMaxStamina.Value), UpgradeKind.ExtraJump => Mathf.Max(0, CfgMaxExtraJump.Value), UpgradeKind.TumbleLaunch => Mathf.Max(0, CfgMaxTumbleLaunch.Value), UpgradeKind.TumbleClimb => Mathf.Max(0, CfgMaxTumbleClimb.Value), UpgradeKind.MapPlayerCount => Mathf.Max(0, CfgMaxMapPlayerCount.Value), UpgradeKind.DeathHeadBattery => Mathf.Max(0, CfgMaxDeathHeadBattery.Value), UpgradeKind.TumbleWings => Mathf.Max(0, CfgMaxTumbleWings.Value), UpgradeKind.SprintSpeed => Mathf.Max(0, CfgMaxSprintSpeed.Value), UpgradeKind.CrouchRest => Mathf.Max(0, CfgMaxCrouchRest.Value), UpgradeKind.GrabStrength => Mathf.Max(0, CfgMaxGrabStrength.Value), UpgradeKind.Throw => Mathf.Max(0, CfgMaxThrow.Value), UpgradeKind.GrabRange => Mathf.Max(0, CfgMaxGrabRange.Value), _ => 999, }; } private Dictionary<string, Dictionary<UpgradeKind, int>> BuildRemainingCaps(List<PlayerUpgradeBuild> players) { Dictionary<string, Dictionary<UpgradeKind, int>> dictionary = new Dictionary<string, Dictionary<UpgradeKind, int>>(); foreach (PlayerUpgradeBuild player in players) { dictionary[player.SteamId] = new Dictionary<UpgradeKind, int>(); foreach (UpgradeKind value in Enum.GetValues(typeof(UpgradeKind))) { if (!CfgEnableMaxClampSafety.Value) { dictionary[player.SteamId][value] = 536870911; } else { dictionary[player.SteamId][value] = Mathf.Max(0, GetMaxForKind(value)); } } } return dictionary; } private bool HasCapacity(string steamId, UpgradeKind kind, Dictionary<string, Dictionary<UpgradeKind, int>> caps) { if (caps == null) { return true; } if (!caps.ContainsKey(steamId)) { return true; } if (!caps[steamId].ContainsKey(kind)) { return true; } return caps[steamId][kind] > 0; } private void ConsumeCap(string steamId, UpgradeKind kind, Dictionary<string, Dictionary<UpgradeKind, int>> caps) { if (caps != null && caps.ContainsKey(steamId) && caps[steamId].ContainsKey(kind) && caps[steamId][kind] > 0) { caps[steamId][kind]--; } } private string WeightedPickSteamId(Dictionary<string, float> weights, float sum) { if (weights == null || weights.Count <= 0) { return ""; } if (sum <= 0f) { return weights.Keys.ElementAt(_random.Next(weights.Count)); } double num = _random.NextDouble() * (double)sum; float num2 = 0f; foreach (KeyValuePair<string, float> weight in weights) { num2 += Mathf.Max(0.0001f, weight.Value); if (num <= (double)num2) { return weight.Key; } } return weights.Keys.Last(); } private UpgradeKind WeightedPickKind(Dictionary<UpgradeKind, float> weights, float sum) { if (weights == null || weights.Count <= 0) { return UpgradeKind.Health; } if (sum <= 0f) { return weights.Keys.ElementAt(_random.Next(weights.Count)); } double num = _random.NextDouble() * (double)sum; float num2 = 0f; foreach (KeyValuePair<UpgradeKind, float> weight in weights) { num2 += Mathf.Max(0.0001f, weight.Value); if (num <= (double)num2) { return weight.Key; } } return weights.Keys.Last(); } private int SumIncludedForPlayer(Dictionary<UpgradeKind, int> map, List<UpgradeKind> includedKinds) { int num = 0; foreach (UpgradeKind includedKind in includedKinds) { if (map.ContainsKey(includedKind)) { num += Mathf.Max(0, map[includedKind]); } } return num; } private void EnqueueUndo(ShuffleSnapshot snapshot) { _undoHistory.Enqueue(snapshot); int num = Mathf.Clamp(CfgUndoHistoryKeepCount.Value, 1, 5); while (_undoHistory.Count > num) { _undoHistory.Dequeue(); } } private void TryUndo() { if (!CfgEnableUndoCommand.Value) { LogInfo("Undo disabled"); return; } if (CfgUndoRequireHost.Value && SemiFunc.IsMultiplayer() && !PhotonNetwork.IsMasterClient) { LogInfo("Undo denied: host only"); return; } if (_undoHistory.Count <= 0) { LogInfo("Undo failed: no history"); return; } ShuffleSnapshot shuffleSnapshot = _undoHistory.Last(); List<PlayerUpgradeBuild> list = BuildPlayerList(); List<string> list2 = list.Select((PlayerUpgradeBuild p) => p.SteamId).OrderBy<string, string>((string x) => x, StringComparer.Ordinal).ToList(); List<string> list3 = shuffleSnapshot.SteamIds.OrderBy<string, string>((string x) => x, StringComparer.Ordinal).ToList(); if (list2.Count != list3.Count) { LogInfo("Undo failed: player count changed"); return; } for (int i = 0; i < list2.Count; i++) { if (!string.Equals(list2[i], list3[i], StringComparison.Ordinal)) { LogInfo("Undo failed: players changed"); return; } } ReadCurrentUpgrades(list); List<UpgradeKind> includedKinds = GetIncludedKinds(); Dictionary<string, Dictionary<UpgradeKind, int>> dictionary = new Dictionary<string, Dictionary<UpgradeKind, int>>(); foreach (PlayerUpgradeBuild item in list) { dictionary[item.SteamId] = CloneValues(shuffleSnapshot.Before[item.SteamId]); } ApplyClampToTargets(dictionary, includeExcluded: false, includedKinds); if (!ApplyTargetsWithBatchedDeltas(list, dictionary, includedKinds)) { LogInfo("Undo failed: apply error"); return; } _undoHistory.Clear(); LogInfo($"Undo success. Restored last shuffle. Mode={shuffleSnapshot.ModeName} ShopCount={shuffleSnapshot.ShopVisitCount}"); } private Dictionary<UpgradeKind, int> CloneValues(Dictionary<UpgradeKind, int> src) { Dictionary<UpgradeKind, int> dictionary = new Dictionary<UpgradeKind, int>(); foreach (KeyValuePair<UpgradeKind, int> item in src) { dictionary[item.Key] = item.Value; } return dictionary; } private void LogPlayerValues(string prefix, List<PlayerUpgradeBuild> players) { foreach (PlayerUpgradeBuild player in players) { LogInfo(prefix + " " + player.DisplayName + "(" + player.SteamId + ") " + FormatValues(player.Values)); } } private void LogTargets(string prefix, List<PlayerUpgradeBuild> players, Dictionary<string, Dictionary<UpgradeKind, int>> targets) { foreach (PlayerUpgradeBuild player in players) { if (targets.ContainsKey(player.SteamId)) { LogInfo(prefix + " " + player.DisplayName + "(" + player.SteamId + ") " + FormatValues(targets[player.SteamId])); } } } private string FormatValues(Dictionary<UpgradeKind, int> values) { return string.Join(", ", (from x in values.OrderBy<KeyValuePair<UpgradeKind, int>, string>((KeyValuePair<UpgradeKind, int> x) => x.Key.ToString(), StringComparer.Ordinal) select $"{x.Key}={x.Value}").ToArray()); } private void LogInfo(string message) { ConfigEntry<bool> cfgLogSummary = CfgLogSummary; if (cfgLogSummary == null || cfgLogSummary.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("[ShuffleUpgrade] " + message)); } } private void LogWarn(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[ShuffleUpgrade] " + message)); } } }