using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BetterSpawn;
using Bifrost.Cooked;
using HarmonyLib;
using MelonLoader;
using MelonLoader.Preferences;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(BetterSpawnMod), "BetterSpawn", "1.0.1", "lyj_0322", null)]
[assembly: MelonGame("ReLUGames", "MIMESIS")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace BetterSpawn;
public class BetterSpawnMod : MelonMod
{
internal const string PrefCategory = "BetterSpawn";
internal static Instance Log;
internal static MelonPreferences_Entry<bool> Enabled;
internal static MelonPreferences_Entry<bool> CooldownEnabled;
internal static MelonPreferences_Entry<float>[] Cooldown;
internal static MelonPreferences_Entry<bool> ScaleEnabled;
internal static MelonPreferences_Entry<int> BasePlayers;
internal static MelonPreferences_Entry<float> ExtraPerPlayer;
internal static MelonPreferences_Entry<float> MaxScale;
internal static MelonPreferences_Entry<float> ScaleDelay;
internal static MelonPreferences_Entry<bool> VerboseLog;
private Harmony _harmony;
public override void OnInitializeMelon()
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Expected O, but got Unknown
Log = ((MelonBase)this).LoggerInstance;
BuildPrefs();
SpawnRuntime.EnsureReady();
try
{
_harmony = new Harmony("mimesis.rationalspawn.v1");
_harmony.PatchAll(typeof(BetterSpawnMod).Assembly);
Log.Msg("合理化刷新 v1.0.1 已加载,补丁已应用");
}
catch (Exception ex)
{
Log.Error("补丁安装失败(模组不会生效):" + ex);
}
}
private static void BuildPrefs()
{
MelonPreferences_Category val = MelonPreferences.CreateCategory("BetterSpawn", "合理化刷新");
Enabled = val.CreateEntry<bool>("Enabled", true, "总开关", "关闭后本模组完全不介入,游戏回到原版行为", false, false, (ValueValidator)null, (string)null);
CooldownEnabled = val.CreateEntry<bool>("CooldownEnabled", true, "击杀冷却", "伪人被打死后,按当前地牢难度推迟下一次刷新", false, false, (ValueValidator)null, (string)null);
Cooldown = new MelonPreferences_Entry<float>[5];
string[] array = new string[5] { "难度 1", "难度 2", "难度 3", "难度 4", "难度 5" };
string[] array2 = new string[5] { "发车时选到难度 1 的地牢:伪人死后至少等这么久才会再刷", "发车时选到难度 2 的地牢:伪人死后至少等这么久才会再刷", "发车时选到难度 3 的地牢:伪人死后至少等这么久才会再刷", "发车时选到难度 4 的地牢:伪人死后至少等这么久才会再刷", "发车时选到难度 5 的地牢:伪人死后至少等这么久才会再刷" };
float[] array3 = new float[5] { 150f, 130f, 110f, 90f, 70f };
for (int i = 0; i < 5; i++)
{
Cooldown[i] = val.CreateEntry<float>("CooldownH" + (i + 1), array3[i], array[i] + " 击杀冷却(秒)", array2[i] + "。填 0 表示不干预。", false, false, (ValueValidator)null, (string)null);
}
ScaleEnabled = val.CreateEntry<bool>("ScaleEnabled", true, "人数缩放", "按队伍人数放大伪人总量,抵消多人局的人均稀释", false, false, (ValueValidator)null, (string)null);
BasePlayers = val.CreateEntry<int>("BasePlayers", 4, "基准人数", "队伍人数等于这个值时倍率为 1.0(原版上限是 4 人)", false, false, (ValueValidator)null, (string)null);
ExtraPerPlayer = val.CreateEntry<float>("ExtraPerPlayer", 0.25f, "每多一人增幅", "每多 1 人,伪人总量增加的比例。0.25 表示 +25%", false, false, (ValueValidator)null, (string)null);
MaxScale = val.CreateEntry<float>("MaxScale", 2f, "最大倍率", "倍率上限,避免人数很多时伪人泛滥", false, false, (ValueValidator)null, (string)null);
ScaleDelay = val.CreateEntry<float>("ScaleDelay", 5f, "缩放延迟(秒)", "进图后等这么久再读取人数,避免队友还在加载时数少了", false, false, (ValueValidator)null, (string)null);
VerboseLog = val.CreateEntry<bool>("VerboseLog", false, "详细日志", "在控制台打印每次冷却与缩放的计算结果,排查时用", false, false, (ValueValidator)null, (string)null);
}
public override void OnDeinitializeMelon()
{
try
{
if (_harmony != null)
{
_harmony.UnpatchSelf();
}
}
catch (Exception)
{
}
}
}
internal static class BuildInfo
{
internal const string Name = "BetterSpawn";
internal const string Version = "1.0.1";
internal const string Author = "lyj_0322";
}
[HarmonyPatch(typeof(DungeonRoom), "OnActorExit")]
internal static class Patch_OnActorExit
{
private static void Postfix(DungeonRoom __instance, VActor actor)
{
SpawnRuntime.HandleActorExit(__instance, actor);
}
}
[HarmonyPatch(typeof(DungeonRoom), "ManageSpawnData")]
internal static class Patch_ManageSpawnData
{
private static void Prefix(DungeonRoom __instance)
{
SpawnRuntime.MaybeApplyScaling(__instance);
}
}
internal static class SpawnRuntime
{
private sealed class RoomState
{
internal float FirstSeen;
internal bool Applied;
}
private static readonly ConditionalWeakTable<DungeonRoom, RoomState> States = new ConditionalWeakTable<DungeonRoom, RoomState>();
private const BindingFlags AllFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
private static readonly FieldInfo FLastMimicSpawn = Find(typeof(DungeonRoom), "_lastMimicSpawnTime");
private static readonly FieldInfo FMimicMax = Find(typeof(DungeonRoom), "_mimicSpawnCountMax");
private static readonly FieldInfo FMimicRemain = Find(typeof(DungeonRoom), "_mimicSpawnCountRemain");
private static readonly FieldInfo FMimicInit = Find(typeof(DungeonRoom), "_mimicInitialSpawnEvaluated");
private static readonly FieldInfo FMasterInfo = Find(typeof(DungeonRoom), "_dungeonMasterInfo");
private static readonly FieldInfo FPlayerDict = Find(typeof(DungeonRoom), "_vPlayerDict");
private static readonly FieldInfo FHazard = Find(typeof(DungeonMasterInfo), "HazardLevel");
private static readonly FieldInfo FPeriod = Find(typeof(DungeonMasterInfo), "MimicSpawnPeriod");
private static readonly FieldInfo FInitPeriod = Find(typeof(DungeonMasterInfo), "MimicInitialSpawnPeriod");
private static readonly FieldInfo FTickBase = Find(typeof(TimeUtil), "m_TickBase");
private static bool _warned;
internal static void EnsureReady()
{
if (_warned)
{
return;
}
_warned = true;
if (typeof(DungeonRoom).GetMethod("OnActorExit", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) == null)
{
Instance log = BetterSpawnMod.Log;
if (log != null)
{
log.Error("[合理化刷新] 找不到 DungeonRoom.OnActorExit,击杀冷却不会生效。");
}
}
if (typeof(DungeonRoom).GetMethod("ManageSpawnData", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) == null)
{
Instance log2 = BetterSpawnMod.Log;
if (log2 != null)
{
log2.Error("[合理化刷新] 找不到 DungeonRoom.ManageSpawnData,人数缩放不会生效。");
}
}
if (FLastMimicSpawn == null || FMimicMax == null || FMimicRemain == null || FMasterInfo == null || FPlayerDict == null || FHazard == null || FPeriod == null || FInitPeriod == null || FTickBase == null)
{
Instance log3 = BetterSpawnMod.Log;
if (log3 != null)
{
log3.Error("[合理化刷新] 反射失败,功能不会生效。缺失字段:" + ((FLastMimicSpawn == null) ? "_lastMimicSpawnTime " : "") + ((FMimicMax == null) ? "_mimicSpawnCountMax " : "") + ((FMimicRemain == null) ? "_mimicSpawnCountRemain " : "") + ((FMasterInfo == null) ? "_dungeonMasterInfo " : "") + ((FPlayerDict == null) ? "_vPlayerDict " : "") + ((FHazard == null) ? "HazardLevel " : "") + ((FPeriod == null) ? "MimicSpawnPeriod " : "") + ((FTickBase == null) ? "m_TickBase " : ""));
}
}
}
private static FieldInfo Find(Type type, string name)
{
Type type2 = type;
while (type2 != null)
{
FieldInfo field = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
return field;
}
type2 = type2.BaseType;
}
return null;
}
private static bool PrefOn(MelonPreferences_Entry<bool> entry)
{
return entry?.Value ?? false;
}
private static float Pref(MelonPreferences_Entry<float> entry, float fallback)
{
return entry?.Value ?? fallback;
}
private static int Pref(MelonPreferences_Entry<int> entry, int fallback)
{
return entry?.Value ?? fallback;
}
private static long NowMs()
{
try
{
Stopwatch stopwatch = ((FTickBase != null) ? (FTickBase.GetValue(null) as Stopwatch) : null);
if (stopwatch != null)
{
return stopwatch.ElapsedMilliseconds;
}
}
catch (Exception)
{
}
return 0L;
}
private static int HazardOf(DungeonRoom room)
{
try
{
object obj = ((FMasterInfo != null) ? FMasterInfo.GetValue(room) : null);
if (obj != null && FHazard != null)
{
int num = (int)FHazard.GetValue(obj);
if (num <= 0)
{
return 3;
}
return Mathf.Clamp(num, 1, 5);
}
}
catch (Exception)
{
}
return 3;
}
private static int PeriodOf(DungeonRoom room)
{
try
{
object obj = ((FMasterInfo != null) ? FMasterInfo.GetValue(room) : null);
if (obj == null)
{
return 90000;
}
FieldInfo fieldInfo = ((FMimicInit != null && (bool)FMimicInit.GetValue(room)) ? FInitPeriod : FPeriod);
if (fieldInfo == null)
{
return 90000;
}
int num = (int)fieldInfo.GetValue(obj);
return (num > 0) ? num : 90000;
}
catch (Exception)
{
return 90000;
}
}
private static float CooldownSeconds(int hazard)
{
MelonPreferences_Entry<float>[] cooldown = BetterSpawnMod.Cooldown;
if (cooldown == null || cooldown.Length < 5)
{
return 0f;
}
MelonPreferences_Entry<float> val = cooldown[Mathf.Clamp(hazard - 1, 0, 4)];
if (val == null)
{
return 0f;
}
return Mathf.Max(0f, val.Value);
}
private static int PlayerCount(DungeonRoom room)
{
try
{
if (((FPlayerDict != null) ? FPlayerDict.GetValue(room) : null) is ICollection collection)
{
return collection.Count;
}
}
catch (Exception)
{
}
return 0;
}
private static bool IsMimic(VMonster monster)
{
try
{
CreatureAIInfo creatureAIData = ((VCreature)monster).GetCreatureAIData();
return creatureAIData != null && creatureAIData.IsMimic();
}
catch (Exception)
{
return false;
}
}
internal static void HandleActorExit(DungeonRoom room, VActor actor)
{
if (room == null || actor == null || actor.SpawnPointIndex == 0 || !PrefOn(BetterSpawnMod.Enabled) || !PrefOn(BetterSpawnMod.CooldownEnabled))
{
return;
}
VMonster val = (VMonster)(object)((actor is VMonster) ? actor : null);
if (val == null || !IsMimic(val))
{
return;
}
int hazard = HazardOf(room);
float num = CooldownSeconds(hazard);
if (num <= 0f)
{
return;
}
long num2 = NowMs();
if (num2 <= 0)
{
return;
}
int num3 = PeriodOf(room);
long num4 = num2 + (long)(num * 1000f) - num3;
object value = FLastMimicSpawn.GetValue(room);
if (value == null)
{
return;
}
long num5 = (long)value;
if (num4 > num5)
{
FLastMimicSpawn.SetValue(room, num4);
if (PrefOn(BetterSpawnMod.VerboseLog))
{
BetterSpawnMod.Log.Msg("[合理化刷新] 难度 " + hazard + " 伪人已离开,冷却 " + num.ToString("0.#") + " 秒(原周期 " + ((float)num3 / 1000f).ToString("0.#") + " 秒)");
}
}
}
internal static void MaybeApplyScaling(DungeonRoom room)
{
if (room == null || (States.TryGetValue(room, out var value) && value.Applied) || !PrefOn(BetterSpawnMod.Enabled) || !PrefOn(BetterSpawnMod.ScaleEnabled) || FMimicMax == null || FMimicRemain == null || FPlayerDict == null)
{
return;
}
if (value == null)
{
value = new RoomState
{
FirstSeen = Time.realtimeSinceStartup
};
States.Add(room, value);
return;
}
float num = Pref(BetterSpawnMod.ScaleDelay, 5f);
if (num > 0f && Time.realtimeSinceStartup - value.FirstSeen < num)
{
return;
}
value.Applied = true;
int num2 = PlayerCount(room);
if (num2 <= 0)
{
value.Applied = false;
return;
}
float num3 = ComputeScale(num2);
int num4 = (int)FMimicMax.GetValue(room);
if (num4 <= 0)
{
return;
}
int num5 = Mathf.Max(1, Mathf.RoundToInt((float)num4 * num3));
int num6 = num5 - num4;
if (num6 == 0)
{
if (PrefOn(BetterSpawnMod.VerboseLog))
{
BetterSpawnMod.Log.Msg("[合理化刷新] " + num2 + " 人,倍率 " + num3.ToString("0.##") + ",伪人数量保持 " + num4);
}
return;
}
int num7 = (int)FMimicRemain.GetValue(room);
FMimicMax.SetValue(room, num5);
FMimicRemain.SetValue(room, Mathf.Max(0, num7 + num6));
if (PrefOn(BetterSpawnMod.VerboseLog))
{
BetterSpawnMod.Log.Msg("[合理化刷新] " + num2 + " 人,倍率 " + num3.ToString("0.##") + ",伪人数量 " + num4 + " → " + num5);
}
}
private static float ComputeScale(int players)
{
int num = Mathf.Clamp(Pref(BetterSpawnMod.BasePlayers, 4), 1, 32);
float num2 = Mathf.Max(0f, Pref(BetterSpawnMod.ExtraPerPlayer, 0.25f));
float num3 = Mathf.Max(1f, Pref(BetterSpawnMod.MaxScale, 2f));
return Mathf.Clamp(1f + (float)(players - num) * num2, 0.5f, num3);
}
}