Decompiled source of BetterSpawn v1.0.1

BetterSpawn.dll

Decompiled 9 hours ago
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);
	}
}