using 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));
}
}
}