using System; using System.Diagnostics; 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 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("REPOJP")] [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.EnemySpawnRange { [BepInPlugin("REPOJP.EnemySpawnRange", "EnemySpawnRange", "1.1.0")] public sealed class EnemySpawnRangePlugin : BaseUnityPlugin { [HarmonyPatch(typeof(SemiFunc), "EnemySpawn")] private static class SemiFunc_EnemySpawn_Patch { private static bool Prefix(Enemy enemy, ref bool __result) { try { if (!CanRunCustomSpawn(enemy)) { return true; } GetResolvedSpawnDistance(out var minDistance, out var maxDistance); if (TryCustomSpawn(enemy, minDistance, maxDistance, out var blockedPointEncountered)) { __result = true; return false; } bool flag = blockedPointEncountered; if (FallbackToVanillaOnFailure.Value && !IsVanillaDistance(minDistance, maxDistance)) { if (TryVanillaSpawn(enemy, 18f, 35f, out var blockedPointEncountered2)) { WriteLog($"FallbackSuccess Enemy={((object)enemy).GetType().Name}"); __result = true; return false; } flag = flag || blockedPointEncountered2; } FinalizeSpawnFailure(enemy, flag); WriteLog(string.Format("Failure Enemy={0}", ((Object)(object)enemy != (Object)null) ? ((object)enemy).GetType().Name : "null")); __result = false; return false; } catch (Exception ex) { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: SemiFunc.EnemySpawn"); ((BaseUnityPlugin)Instance).Logger.LogError((object)ex); } return true; } } } public const string PluginGuid = "REPOJP.EnemySpawnRange"; public const string PluginName = "EnemySpawnRange"; public const string PluginVersion = "1.1.0"; internal static EnemySpawnRangePlugin Instance; private Harmony harmony; internal static ConfigEntry ModEnabled; internal static ConfigEntry MinSpawnDistance; internal static ConfigEntry MaxSpawnDistance; internal static ConfigEntry RespectDebugSpawnClose; internal static ConfigEntry FallbackToVanillaOnFailure; internal static ConfigEntry ExcludeArena; internal static ConfigEntry ExcludeShop; internal static ConfigEntry RequireLevelGenerated; internal static ConfigEntry LogEnabled; private static FieldInfo enemyParentFirstSpawnPointUsedField; private static FieldInfo enemyDirectorDebugSpawnCloseField; private static FieldInfo roundDirectorAllExtractionPointsCompletedField; private static FieldInfo enemyRigidbodyRbField; private void Awake() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown try { Instance = this; if ((Object)(object)((Component)this).transform.parent != (Object)null) { ((Component)this).transform.parent = null; } ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); InitializeConfig(); InitializeReflection(); harmony = new Harmony("REPOJP.EnemySpawnRange"); harmony.PatchAll(); WriteAlways("Loaded v1.1.0"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)"Failure: Awake"); ((BaseUnityPlugin)this).Logger.LogError((object)ex); } } private void OnDestroy() { try { if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)"Failure: OnDestroy"); ((BaseUnityPlugin)this).Logger.LogError((object)ex); } } private void InitializeConfig() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown ModEnabled = ((BaseUnityPlugin)this).Config.Bind("General", "ModEnabled", true, "Enable or disable this mod. MOD有効無効"); MinSpawnDistance = ((BaseUnityPlugin)this).Config.Bind("General", "MinSpawnDistance", 5f, new ConfigDescription("Minimum player distance allowed for enemy spawn points. 敵スポーン候補の最小距離", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 999f), Array.Empty())); MaxSpawnDistance = ((BaseUnityPlugin)this).Config.Bind("General", "MaxSpawnDistance", 15f, new ConfigDescription("Maximum player distance allowed for enemy spawn points. 敵スポーン候補の最大距離", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 999f), Array.Empty())); RespectDebugSpawnClose = ((BaseUnityPlugin)this).Config.Bind("Compatibility", "RespectDebugSpawnClose", true, "Use the game's debugSpawnClose behavior when enabled. ゲーム本体のdebugSpawnClose有効時に0-999を優先"); FallbackToVanillaOnFailure = ((BaseUnityPlugin)this).Config.Bind("Safety", "FallbackToVanillaOnFailure", true, "Retry with vanilla 18-35 range if custom spawn fails. 設定距離失敗時に18-35で再試行"); ExcludeArena = ((BaseUnityPlugin)this).Config.Bind("Safety", "ExcludeArena", true, "Do not override enemy spawn distance in arena levels. アリーナでは敵スポーン距離を変更しない"); ExcludeShop = ((BaseUnityPlugin)this).Config.Bind("Safety", "ExcludeShop", true, "Do not override enemy spawn distance in shop levels. ショップでは敵スポーン距離を変更しない"); RequireLevelGenerated = ((BaseUnityPlugin)this).Config.Bind("Safety", "RequireLevelGenerated", true, "Run only after level generation is completed. レベル生成完了後のみ実行"); LogEnabled = ((BaseUnityPlugin)this).Config.Bind("Debug", "LogEnabled", false, "Enable detailed log output. 詳細ログ出力"); } private void InitializeReflection() { enemyParentFirstSpawnPointUsedField = AccessTools.Field(typeof(EnemyParent), "firstSpawnPointUsed"); enemyDirectorDebugSpawnCloseField = AccessTools.Field(typeof(EnemyDirector), "debugSpawnClose"); roundDirectorAllExtractionPointsCompletedField = AccessTools.Field(typeof(RoundDirector), "allExtractionPointsCompleted"); enemyRigidbodyRbField = AccessTools.Field(typeof(EnemyRigidbody), "rb"); } internal static void WriteAlways(string message) { if (!((Object)(object)Instance == (Object)null)) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[EnemySpawnRange] " + message)); } } internal static void WriteLog(string message) { if (!((Object)(object)Instance == (Object)null) && LogEnabled != null && LogEnabled.Value) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[EnemySpawnRange] " + message)); } } internal static bool IsSafeGameplayLevel() { try { if ((Object)(object)RunManager.instance == (Object)null) { return false; } if ((Object)(object)LevelGenerator.Instance == (Object)null) { return false; } if (RequireLevelGenerated.Value && !LevelGenerator.Instance.Generated) { return false; } if (ExcludeShop.Value && SemiFunc.RunIsShop()) { return false; } if (ExcludeArena.Value && SemiFunc.RunIsArena()) { return false; } if (!SemiFunc.RunIsLevel()) { return false; } return true; } catch (Exception ex) { WriteLog("IsSafeGameplayLevel failure: " + ex.Message); return false; } } internal static bool CanRunCustomSpawn(Enemy enemy) { try { if (ModEnabled == null || !ModEnabled.Value) { return false; } if ((Object)(object)enemy == (Object)null) { return false; } if (!IsSafeGameplayLevel()) { return false; } if ((Object)(object)EnemyDirector.instance == (Object)null) { return false; } if ((Object)(object)GameDirector.instance == (Object)null) { return false; } if (GameDirector.instance.PlayerList == null) { return false; } if (GameDirector.instance.PlayerList.Count == 0) { return false; } if (!SemiFunc.IsMasterClientOrSingleplayer()) { return false; } return true; } catch (Exception ex) { WriteLog("CanRunCustomSpawn failure: " + ex.Message); return false; } } internal static bool IsDebugSpawnCloseEnabled() { try { if ((Object)(object)EnemyDirector.instance == (Object)null) { return false; } if (enemyDirectorDebugSpawnCloseField == null) { return false; } object value = enemyDirectorDebugSpawnCloseField.GetValue(EnemyDirector.instance); if (value is bool) { return (bool)value; } } catch (Exception ex) { WriteLog("DebugSpawnClose read failure: " + ex.Message); } return false; } internal static bool AreAllExtractionPointsCompleted() { try { if ((Object)(object)RoundDirector.instance == (Object)null) { return false; } if (roundDirectorAllExtractionPointsCompletedField == null) { return false; } object value = roundDirectorAllExtractionPointsCompletedField.GetValue(RoundDirector.instance); if (value is bool) { return (bool)value; } } catch (Exception ex) { WriteLog("AllExtractionPointsCompleted read failure: " + ex.Message); } return false; } internal static void GetResolvedSpawnDistance(out float minDistance, out float maxDistance) { minDistance = Mathf.Max(0f, MinSpawnDistance.Value); maxDistance = Mathf.Max(0f, MaxSpawnDistance.Value); if (minDistance > maxDistance) { float num = minDistance; minDistance = maxDistance; maxDistance = num; } if (RespectDebugSpawnClose.Value && IsDebugSpawnCloseEnabled()) { minDistance = 0f; maxDistance = 999f; } } internal static bool IsVanillaDistance(float minDistance, float maxDistance) { return Mathf.Approximately(minDistance, 18f) && Mathf.Approximately(maxDistance, 35f); } internal static EnemyParent GetEnemyParent(Enemy enemy) { if ((Object)(object)enemy == (Object)null) { return null; } return ((Component)enemy).GetComponentInParent(); } internal static void SetFirstSpawnPointUsed(EnemyParent enemyParent, bool value) { try { if (!((Object)(object)enemyParent == (Object)null) && !(enemyParentFirstSpawnPointUsedField == null)) { enemyParentFirstSpawnPointUsedField.SetValue(enemyParent, value); } } catch (Exception ex) { WriteLog("SetFirstSpawnPointUsed failure: " + ex.Message); } } internal static EnemyRigidbody GetEnemyRigidbody(Enemy enemy) { if ((Object)(object)enemy == (Object)null) { return null; } return ((Component)enemy).GetComponentInChildren(); } internal static Rigidbody GetUnityRigidbody(EnemyRigidbody enemyRigidbody) { try { if ((Object)(object)enemyRigidbody == (Object)null) { return null; } if (enemyRigidbodyRbField == null) { return null; } object? value = enemyRigidbodyRbField.GetValue(enemyRigidbody); return (Rigidbody)((value is Rigidbody) ? value : null); } catch (Exception ex) { WriteLog("GetUnityRigidbody failure: " + ex.Message); return null; } } internal static bool IsBlockedSpawn(Enemy enemy, Vector3 targetPosition) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) try { EnemyRigidbody enemyRigidbody = GetEnemyRigidbody(enemy); Rigidbody unityRigidbody = GetUnityRigidbody(enemyRigidbody); if ((Object)(object)unityRigidbody != (Object)null) { return SemiFunc.EnemyPhysObjectBoundingBoxCheck(targetPosition, targetPosition, unityRigidbody, false, 1.2f); } return SemiFunc.EnemyPhysObjectSphereCheck(targetPosition, 1f); } catch (Exception ex) { WriteLog("IsBlockedSpawn failure: " + ex.Message); return true; } } internal static LevelPoint GetCustomSpawnLevelPoint(Vector3 currentPosition, float minDistance, float maxDistance) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) LevelPoint val = null; if (AreAllExtractionPointsCompleted()) { val = SemiFunc.LevelPointGetPlayerDistance(currentPosition, minDistance, maxDistance, true); } if (!Object.op_Implicit((Object)(object)val)) { val = SemiFunc.LevelPointGetPlayerDistance(currentPosition, minDistance, maxDistance, false); } return val; } internal static LevelPoint GetVanillaSpawnLevelPoint(Enemy enemy, float minDistance, float maxDistance) { if ((Object)(object)enemy == (Object)null) { return null; } return enemy.TeleportToPoint(minDistance, maxDistance); } internal static bool TryCustomSpawn(Enemy enemy, float minDistance, float maxDistance, out bool blockedPointEncountered) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) blockedPointEncountered = false; EnemyParent enemyParent = GetEnemyParent(enemy); if ((Object)(object)enemyParent == (Object)null) { WriteLog("CustomSpawn enemyParent not found"); return false; } LevelPoint customSpawnLevelPoint = GetCustomSpawnLevelPoint(((Component)enemy).transform.position, minDistance, maxDistance); if (!Object.op_Implicit((Object)(object)customSpawnLevelPoint)) { WriteLog($"Custom NoPoint Enemy={((object)enemy).GetType().Name} Min={minDistance:0.###} Max={maxDistance:0.###}"); return false; } Vector3 position = ((Component)customSpawnLevelPoint).transform.position; if (IsBlockedSpawn(enemy, position)) { blockedPointEncountered = true; WriteLog($"Custom Blocked Enemy={((object)enemy).GetType().Name} Min={minDistance:0.###} Max={maxDistance:0.###} Point=({position.x:0.##}, {position.y:0.##}, {position.z:0.##})"); return false; } enemy.EnemyTeleported(position); SetFirstSpawnPointUsed(enemyParent, value: true); WriteLog($"Custom Success Enemy={((object)enemy).GetType().Name} Min={minDistance:0.###} Max={maxDistance:0.###} Point=({position.x:0.##}, {position.y:0.##}, {position.z:0.##})"); return true; } internal static bool TryVanillaSpawn(Enemy enemy, float minDistance, float maxDistance, out bool blockedPointEncountered) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) blockedPointEncountered = false; EnemyParent enemyParent = GetEnemyParent(enemy); if ((Object)(object)enemyParent == (Object)null) { WriteLog("VanillaSpawn enemyParent not found"); return false; } LevelPoint vanillaSpawnLevelPoint = GetVanillaSpawnLevelPoint(enemy, minDistance, maxDistance); if (!Object.op_Implicit((Object)(object)vanillaSpawnLevelPoint)) { WriteLog($"Vanilla NoPoint Enemy={((object)enemy).GetType().Name} Min={minDistance:0.###} Max={maxDistance:0.###}"); return false; } Vector3 position = ((Component)vanillaSpawnLevelPoint).transform.position; if (IsBlockedSpawn(enemy, position)) { blockedPointEncountered = true; WriteLog($"Vanilla Blocked Enemy={((object)enemy).GetType().Name} Min={minDistance:0.###} Max={maxDistance:0.###} Point=({position.x:0.##}, {position.y:0.##}, {position.z:0.##})"); return false; } SetFirstSpawnPointUsed(enemyParent, value: true); WriteLog($"Vanilla Success Enemy={((object)enemy).GetType().Name} Min={minDistance:0.###} Max={maxDistance:0.###} Point=({position.x:0.##}, {position.y:0.##}, {position.z:0.##})"); return true; } internal static void FinalizeSpawnFailure(Enemy enemy, bool blockedPointEncountered) { try { EnemyParent enemyParent = GetEnemyParent(enemy); if (!((Object)(object)enemyParent == (Object)null)) { if (blockedPointEncountered && (Object)(object)EnemyDirector.instance != (Object)null) { EnemyDirector.instance.FirstSpawnPointAdd(enemyParent); } enemyParent.Despawn(); enemyParent.DespawnedTimerSet(Random.Range(2f, 3f), true); } } catch (Exception ex) { WriteLog("FinalizeSpawnFailure failure: " + ex.Message); } } } }