Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SparkleEngineerTweaks v1.11.0
plugins/SparkleEngineerTweaks.dll
Decompiled 12 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using EntityStates; using EntityStates.Engi.EngiMissilePainter; using EntityStates.Engi.Mine; using EntityStates.EngiTurret.EngiTurretWeapon; using HarmonyLib; using Microsoft.CodeAnalysis; using R2API; using RoR2; using RoR2.CharacterAI; using RoR2.Projectile; using RoR2.Skills; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("SparkleEngineerTweaks")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SparkleEngineerTweaks")] [assembly: AssemblyTitle("SparkleEngineerTweaks")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 SparkleEngineerTweaks { internal static class EngineerBubbleShieldArmor { private sealed class Shield { public GameObject gameObject; public float radius = -1f; public Transform center; } private static readonly string[] DeployedTypeNames = new string[2] { "EntityStates.Engi.EngiBubbleShield.Deployed", "EntityStates.Engi.EngiBubbleShield+Deployed" }; private const string BubbleChildName = "Bubble"; private const string CollisionChildName = "Collision"; private const float FallbackRadius = 10f; private static ManualLogSource _log; private static ConfigEntry<bool> _enabled; private static ConfigEntry<float> _radius; private static ConfigEntry<float> _duration; private static ConfigEntry<float> _interval; private static readonly List<Shield> _shields = new List<Shield>(); private static Type _deployedType; private static float _nextTick; private static bool _radiusLogged; private static bool _buffLogged; private static bool _registered; private static bool _errorLogged; private static bool Enabled { get { if (_enabled != null) { return _enabled.Value; } return true; } } private static float Radius { get { if (_radius == null) { return 0f; } return _radius.Value; } } private static float Duration { get { if (_duration == null) { return 0.5f; } return Mathf.Max(0.1f, _duration.Value); } } private static float Interval { get { if (_interval == null) { return 0.2f; } return Mathf.Clamp(_interval.Value, 0.05f, 2f); } } internal static void Init(ConfigFile config, ManualLogSource log) { _log = log; _enabled = config.Bind<bool>("位移技能.泡泡护盾", "BubbleShieldArmor", true, "是否给处在泡泡护盾里的友军(含工程师自己)+200 点护甲。用的是原版 ArmorBoost buff,走出护盾后短暂失效。"); _radius = config.Bind<float>("位移技能.泡泡护盾", "Radius", 0f, "护盾判定半径(米):0 = 自动按护盾模型算(推荐)。"); _duration = config.Bind<float>("位移技能.泡泡护盾", "BuffDuration", 0.5f, "每次续上的护甲 buff 时长(秒):走出护盾后大约这么多秒失效。"); _interval = config.Bind<float>("位移技能.泡泡护盾", "CheckInterval", 0.2f, "多久检查一次谁在护盾里(秒)。"); } internal static string Summary() { if (!Enabled) { return ""; } return " [泡泡护盾:+200护甲]"; } internal static void Patch(Harmony harmony) { //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown if (!Enabled) { Log((LogLevel)16, "泡泡护盾护甲:按配置保持原版,不挂补丁。"); return; } Type type = null; for (int i = 0; i < DeployedTypeNames.Length; i++) { if (!(type == null)) { break; } type = AccessTools.TypeByName(DeployedTypeNames[i]); } if (type == null) { Log((LogLevel)2, "泡泡护盾护甲补丁失败:找不到 " + string.Join(" / ", DeployedTypeNames) + "。"); return; } MethodInfo methodInfo = AccessTools.Method(type, "OnEnter", (Type[])null, (Type[])null); if (methodInfo == null) { Log((LogLevel)2, "泡泡护盾护甲补丁失败:取不到 " + type.FullName + ".OnEnter。"); return; } _deployedType = type; harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(EngineerBubbleShieldArmor), "DeployedOnEnterPostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log((LogLevel)16, "补丁已挂载:" + type.FullName + ".OnEnter <- EngineerBubbleShieldArmor.DeployedOnEnterPostfix(护盾内友军 +200 护甲)"); } private static void DeployedOnEnterPostfix(EntityState __instance) { try { if (__instance == null || (Object)(object)__instance.outer == (Object)null || (_deployedType != null && ((object)__instance).GetType() != _deployedType)) { return; } GameObject gameObject = ((Component)__instance.outer).gameObject; if ((Object)(object)gameObject == (Object)null) { return; } for (int i = 0; i < _shields.Count; i++) { if (_shields[i].gameObject == gameObject) { return; } } _shields.Add(new Shield { gameObject = gameObject }); if (!_registered) { _registered = true; Log((LogLevel)16, "泡泡护盾护甲:已登记一面护盾(" + ((Object)gameObject).name + "),开始给范围内的同队角色续 +200 护甲 buff。"); } } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "泡泡护盾护甲:登记护盾时异常(已忽略)—— " + ex.GetType().Name + " " + ex.Message); } } internal static void Tick() { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: 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_013c: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || !NetworkServer.active) { return; } float time = Time.time; if (time < _nextTick) { return; } _nextTick = time + Interval; try { for (int num = _shields.Count - 1; num >= 0; num--) { if ((Object)(object)_shields[num].gameObject == (Object)null) { _shields.RemoveAt(num); } } if (_shields.Count == 0) { return; } BuffDef armorBoost = Buffs.ArmorBoost; if ((Object)(object)armorBoost == (Object)null) { return; } IList<CharacterBody> readOnlyInstancesList = CharacterBody.readOnlyInstancesList; if (readOnlyInstancesList == null) { return; } for (int i = 0; i < _shields.Count; i++) { Shield shield = _shields[i]; float num2 = RadiusOf(shield); Vector3 val = CenterOf(shield); TeamIndex val2 = TeamOf(shield); float num3 = num2 * num2; for (int j = 0; j < readOnlyInstancesList.Count; j++) { CharacterBody val3 = readOnlyInstancesList[j]; if ((Object)(object)val3 == (Object)null || (Object)(object)val3.teamComponent == (Object)null || val3.teamComponent.teamIndex != val2 || (Object)(object)val3.healthComponent == (Object)null || !val3.healthComponent.alive) { continue; } Vector3 val4 = ((Component)val3).transform.position - val; if (((Vector3)(ref val4)).sqrMagnitude <= num3) { val3.AddTimedBuff(armorBoost, Duration); if (!_buffLogged) { _buffLogged = true; Log((LogLevel)16, "泡泡护盾护甲:已给护盾内的 " + val3.GetDisplayName() + " 挂上 +200 护甲 buff(" + ((Object)armorBoost).name + ",半径 " + num2.ToString("0.##") + " 米)。"); } } } } } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "泡泡护盾护甲:异常(已忽略本帧)—— " + ex.GetType().Name + " " + ex.Message); } } private static TeamIndex TeamOf(Shield shield) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) GameObject gameObject = shield.gameObject; if ((Object)(object)gameObject == (Object)null) { return (TeamIndex)1; } TeamComponent component = gameObject.GetComponent<TeamComponent>(); if ((Object)(object)component != (Object)null) { return component.teamIndex; } TeamFilter component2 = gameObject.GetComponent<TeamFilter>(); if (!((Object)(object)component2 != (Object)null)) { return (TeamIndex)1; } return component2.teamIndex; } private static Vector3 CenterOf(Shield shield) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)shield.center != (Object)null) { return shield.center.position; } if (!((Object)(object)shield.gameObject != (Object)null)) { return Vector3.zero; } return shield.gameObject.transform.position; } private static float RadiusOf(Shield shield) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (shield.radius > 0f) { return shield.radius; } float radius = Radius; if (radius > 0f) { shield.radius = radius; return radius; } float num = 0f; if ((Object)(object)shield.gameObject != (Object)null) { ChildLocator componentInChildren = shield.gameObject.GetComponentInChildren<ChildLocator>(); if ((Object)(object)componentInChildren != (Object)null) { Transform val = componentInChildren.FindChild("Bubble"); if ((Object)(object)val == (Object)null) { val = componentInChildren.FindChild("Collision"); } if ((Object)(object)val != (Object)null) { shield.center = val; Collider component = ((Component)val).GetComponent<Collider>(); if ((Object)(object)component != (Object)null) { Bounds bounds = component.bounds; num = Mathf.Max(((Bounds)(ref bounds)).extents.x, Mathf.Max(((Bounds)(ref bounds)).extents.y, ((Bounds)(ref bounds)).extents.z)); } } } } if (num > 0f) { shield.radius = num; if (!_radiusLogged) { _radiusLogged = true; Log((LogLevel)16, "泡泡护盾护甲:判定半径 " + num.ToString("0.##") + " 米(自动按护盾模型算,可用配置 Radius 覆盖)。"); } return num; } return 10f; } private static void LogOnce(ref bool flag, LogLevel level, string message) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!flag) { flag = true; Log(level, message); } } private static void Log(LogLevel level, string message) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (_log != null) { _log.Log(level, (object)message); } } } internal static class EngineerPressureMine { private const float VanillaDamagePercent = 900f; private static ManualLogSource _log; private static ConfigEntry<bool> _enabled; private static ConfigEntry<float> _damagePercent; private static ConfigEntry<bool> _noEarlyDetonation; private static readonly FieldInfo _targetFinderField = AccessTools.Field(typeof(WaitForTarget), "targetFinder"); private static FieldInfo _lookRangeField; private static float _scaleBase = -1f; private static float _scaleLastWritten = float.MinValue; private static readonly HashSet<int> _fullyArmed = new HashSet<int>(); private static PropertyInfo _armingMachineProp; private static PropertyInfo _machineStateProp; private static bool _propsResolved; private static bool _damageLogged; private static bool _triggerLogged; private static bool _suppressLogged; private static bool _propsLogged; private static bool _detonateLogged; private static bool _errorLogged; private static bool Enabled { get { if (_enabled != null) { return _enabled.Value; } return true; } } private static float DamagePercent { get { if (_damagePercent == null) { return 900f; } return Mathf.Max(0f, _damagePercent.Value); } } private static bool NoEarlyDetonation { get { if (_noEarlyDetonation != null) { return _noEarlyDetonation.Value; } return true; } } private static float DamageRatio => DamagePercent / 900f; internal static void Init(ConfigFile config, ManualLogSource log) { _log = log; _enabled = config.Bind<bool>("副武器.压力地雷", "PressureMineTweaks", true, "是否启用压力地雷(M2)的两项调整:满蓄力伤害提到 1000%,以及蓄满之前不会被引爆。"); _damagePercent = config.Bind<float>("副武器.压力地雷", "DamagePercent", 1000f, "满蓄力时地雷造成的伤害(百分比):原版是 900(投掷 300% × 满蓄力倍率 3)。只改满蓄力那一档,半蓄力仍是原来的 300%。"); _noEarlyDetonation = config.Bind<bool>("副武器.压力地雷", "NoEarlyDetonation", true, "地雷在完全准备好(蓄满)之前不会被引爆:原版蓄力中的 3 秒里,敌人进到 4 米内就能把它引爆(只有 300% 伤害、爆炸半径还只有 0.2 倍)。"); } internal static string Summary() { if (!Enabled) { return ""; } string text = " [压力地雷:" + DamagePercent.ToString("0") + "%"; if (NoEarlyDetonation) { text += " 蓄满前不炸"; } return text + "]"; } internal static void Patch(Harmony harmony) { if (!Enabled) { Log((LogLevel)16, "压力地雷:按配置保持原版,不挂补丁。"); return; } SafePatch(harmony, typeof(BaseMineArmingState), "OnEnter", "蓄力状态", "ArmingOnEnterPostfix", prefix: false); SafePatch(harmony, typeof(Detonate), "OnEnter", "引爆(只打日志)", "DetonateOnEnterPostfix", prefix: false); if (NoEarlyDetonation) { SafePatch(harmony, typeof(WaitForTarget), "OnEnter", "等目标状态", "WaitForTargetOnEnterPostfix", prefix: false); SafePatch(harmony, typeof(WaitForTarget), "FixedUpdate", "等目标状态每帧", "WaitForTargetFixedUpdatePrefix", prefix: true); } } private static void SafePatch(Harmony harmony, Type stateType, string methodName, string label, string patchName, bool prefix) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(stateType, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { Log((LogLevel)2, "压力地雷补丁失败:取不到 " + stateType.Name + "." + methodName + "。"); return; } HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(EngineerPressureMine), patchName, (Type[])null, (Type[])null)); if (prefix) { harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } Log((LogLevel)16, "补丁已挂载:" + stateType.Name + "." + methodName + " <- EngineerPressureMine." + patchName + "(" + label + ")"); } private static void ArmingOnEnterPostfix(BaseMineArmingState __instance) { try { if (__instance != null) { bool flag = __instance is MineArmingFull; TrackFullState(__instance, flag); if (flag) { ApplyDamageScale(__instance); } else { ApplyNoEarlyTrigger(__instance); } } } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "压力地雷:改蓄力状态时异常(已忽略)—— " + ex.GetType().Name + " " + ex.Message); } } private static void TrackFullState(BaseMineArmingState state, bool full) { if ((Object)(object)((EntityState)state).outer == (Object)null) { return; } GameObject gameObject = ((Component)((EntityState)state).outer).gameObject; if ((Object)(object)gameObject == (Object)null) { return; } int instanceID = ((Object)gameObject).GetInstanceID(); if (full) { if (_fullyArmed.Count > 256) { _fullyArmed.Clear(); } _fullyArmed.Add(instanceID); } else { _fullyArmed.Remove(instanceID); } } private static void ApplyDamageScale(BaseMineArmingState state) { float damageRatio = DamageRatio; if (Mathf.Abs(damageRatio - 1f) < 0.0001f) { return; } float damageScale = state.damageScale; if (damageScale <= 0f) { return; } if (Mathf.Abs(damageScale - _scaleLastWritten) > 0.0001f) { _scaleBase = damageScale; } float num = _scaleBase * damageRatio; if (!(Mathf.Abs(damageScale - num) <= 0.0001f)) { state.damageScale = num; _scaleLastWritten = num; if (!_damageLogged) { _damageLogged = true; Log((LogLevel)16, "压力地雷:满蓄力伤害倍率 " + _scaleBase.ToString("0.###") + " → " + num.ToString("0.###") + "(投掷 300% × 倍率 → 合计 " + (300f * num).ToString("0.#") + "%);半蓄力档不受影响。"); } } } private static void ApplyNoEarlyTrigger(BaseMineArmingState state) { if (NoEarlyDetonation && state.triggerRadius != 0f) { state.triggerRadius = 0f; if (!_triggerLogged) { _triggerLogged = true; Log((LogLevel)16, "压力地雷:未蓄满的档位触发半径已归零(索敌器关闭 → 完全准备好之前不会引爆)。"); } } } private static void WaitForTargetOnEnterPostfix(WaitForTarget __instance) { try { if (__instance == null || IsFullyArmed(__instance)) { return; } Suppress(__instance); if (!(_targetFinderField != null)) { return; } object? value = _targetFinderField.GetValue(__instance); Behaviour val = (Behaviour)((value is Behaviour) ? value : null); if ((Object)(object)val != (Object)null) { val.enabled = false; if (_lookRangeField == null) { _lookRangeField = AccessTools.Field(((object)val).GetType(), "lookRange"); } if (_lookRangeField != null) { _lookRangeField.SetValue(val, 0f); } } } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "压力地雷:关闭索敌器时异常(已忽略)—— " + ex.GetType().Name + " " + ex.Message); } } private static void WaitForTargetFixedUpdatePrefix(WaitForTarget __instance) { try { if (__instance != null && !IsFullyArmed(__instance)) { Suppress(__instance); } } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "压力地雷:屏蔽索敌时异常(已忽略本帧)—— " + ex.GetType().Name + " " + ex.Message); } } private static bool IsFullyArmed(WaitForTarget state) { if ((Object)(object)((EntityState)state).outer != (Object)null && (Object)(object)((Component)((EntityState)state).outer).gameObject != (Object)null && _fullyArmed.Contains(((Object)((Component)((EntityState)state).outer).gameObject).GetInstanceID())) { return true; } return CurrentArmingState(state) is MineArmingFull; } private static object CurrentArmingState(object mineState) { ResolveProps(); if (_armingMachineProp == null || _machineStateProp == null) { return null; } object value = _armingMachineProp.GetValue(mineState, null); if (value == null) { return null; } return _machineStateProp.GetValue(value, null); } private static void ResolveProps() { if (!_propsResolved) { _propsResolved = true; _armingMachineProp = typeof(BaseMineState).GetProperty("armingStateMachine", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _machineStateProp = typeof(EntityStateMachine).GetProperty("state", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (_armingMachineProp == null || _machineStateProp == null) { LogOnce(ref _propsLogged, (LogLevel)4, "压力地雷:取不到蓄力状态机的反射入口,将只靠「是否记录过蓄满」判定。"); } } } private static void Suppress(WaitForTarget state) { if ((Object)(object)((EntityState)state).outer == (Object)null) { return; } GameObject gameObject = ((Component)((EntityState)state).outer).gameObject; if ((Object)(object)gameObject == (Object)null) { return; } ProjectileSphereTargetFinder[] componentsInChildren = gameObject.GetComponentsInChildren<ProjectileSphereTargetFinder>(true); for (int i = 0; i < componentsInChildren.Length; i++) { if (!((Object)(object)componentsInChildren[i] == (Object)null)) { ((Behaviour)componentsInChildren[i]).enabled = false; componentsInChildren[i].lookRange = 0f; } } ProjectileTargetComponent component = gameObject.GetComponent<ProjectileTargetComponent>(); if ((Object)(object)component != (Object)null && (Object)(object)component.target != (Object)null) { component.target = null; } if (!_suppressLogged) { _suppressLogged = true; Log((LogLevel)16, "压力地雷:蓄满前屏蔽索敌(清掉已锁定的目标 + 关闭 " + componentsInChildren.Length + " 个索敌器)→ 只会在完全准备好之后引爆。"); } } private static void DetonateOnEnterPostfix(Detonate __instance) { try { if (__instance != null && !_detonateLogged) { object obj = CurrentArmingState(__instance); string text = ((obj != null) ? obj.GetType().Name : "未知"); float num = -1f; BaseMineArmingState val = (BaseMineArmingState)((obj is BaseMineArmingState) ? obj : null); if (val != null) { num = val.damageScale; } _detonateLogged = true; Log((LogLevel)16, "压力地雷:引爆时蓄力档 = " + text + ",伤害倍率 = " + num.ToString("0.###") + "(合计约 " + (300f * Mathf.Max(0f, num)).ToString("0.#") + "%)。"); } } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "压力地雷:引爆日志异常(已忽略)—— " + ex.GetType().Name + " " + ex.Message); } } private static void LogOnce(ref bool flag, LogLevel level, string message) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!flag) { flag = true; Log(level, message); } } private static void Log(LogLevel level, string message) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (_log != null) { _log.Log(level, (object)message); } } } internal static class EngineerShoulderHarpoons { private const string TurretBodyName = "EngiWalkerTurretBody"; private const string HarpoonPrefabName = "EngiHarpoon"; private const float BeamDisabledCooldown = 99999f; private static ManualLogSource _log; private static ConfigEntry<bool> _enabled; private static ConfigEntry<float> _interval; private static ConfigEntry<float> _damagePercent; private static ConfigEntry<bool> _disableBeam; private static ConfigEntry<float> _attackSpeedScale; private static readonly Type _fireStateType = typeof(Fire); private static readonly FieldInfo _prefabField = AccessTools.Field(_fireStateType, "projectilePrefab"); private static readonly FieldInfo _muzzleField = AccessTools.Field(_fireStateType, "muzzleflashEffectPrefab"); private static GameObject _projectilePrefab; private static GameObject _muzzleEffect; private static Type _aiOwnershipType; private static FieldInfo _aiOwnerMasterField; private static Type _minionOwnershipType; private static PropertyInfo _minionOwnerMasterProp; private static readonly Dictionary<int, float> _timers = new Dictionary<int, float>(); private static readonly List<int> _present = new List<int>(); private static float _pruneTimer; private static bool _indexResolved; private static BodyIndex _turretBodyIndex; private static bool _missingLogged; private static bool _errorLogged; private static bool _prefabLogged; private static bool _beamLogged; private static bool _firstFireLogged; private static bool _clientLogged; private static bool _ownerMissingLogged; private static bool Enabled { get { if (_enabled != null) { return _enabled.Value; } return true; } } private static float Interval { get { if (_interval == null) { return 3f; } return Mathf.Max(0.05f, _interval.Value); } } private static float DamagePercent { get { if (_damagePercent == null) { return 500f; } return _damagePercent.Value; } } private static bool DisableBeam { get { if (_disableBeam != null) { return _disableBeam.Value; } return true; } } private static float AttackSpeedScale { get { if (_attackSpeedScale == null) { return 0.5f; } return Mathf.Max(0f, _attackSpeedScale.Value); } } internal static void Init(ConfigFile config, ManualLogSource log) { _log = log; _enabled = config.Bind<bool>("特殊技能.移动炮台.鱼叉", "HarpoonWeapon", true, "是否把移动炮台的主武器换成热鱼叉(Thermal Harpoons)导弹。"); _interval = config.Bind<float>("特殊技能.移动炮台.鱼叉", "Interval", 3f, "开火间隔(秒):每隔这么久朝当前目标打一发。"); _damagePercent = config.Bind<float>("特殊技能.移动炮台.鱼叉", "DamagePercent", 500f, "鱼叉伤害倍率(百分比):500 = 500% 攻击力。原版工程师的热鱼叉正好也是 500%/发。"); _disableBeam = config.Bind<bool>("特殊技能.移动炮台.鱼叉", "DisableOriginalBeam", true, "是否停用移动炮台原来的激光主武器(把它的冷却顶到走不完)。"); _attackSpeedScale = config.Bind<float>("特殊技能.移动炮台.鱼叉", "AttackSpeedScale", 0.5f, "开火频率受攻击速度加成的比例:实际间隔 = 间隔 ÷ (1 + (攻击速度 - 1) × 这个值)。 1 = 完全跟随攻击速度;0.5 = 只吃一半(+200% 攻击速度实际只当 +100% 用,3 秒一发变成 1.5 秒一发);0 = 完全不受攻击速度影响。"); _aiOwnershipType = AccessTools.TypeByName("RoR2.CharacterAI.AIOwnership"); if (_aiOwnershipType != null) { _aiOwnerMasterField = AccessTools.Field(_aiOwnershipType, "ownerMaster"); } _minionOwnershipType = AccessTools.TypeByName("RoR2.MinionOwnership"); if (_minionOwnershipType != null) { _minionOwnerMasterProp = AccessTools.Property(_minionOwnershipType, "ownerMaster"); } if (_prefabField == null && !_missingLogged) { _missingLogged = true; Log((LogLevel)4, "肩炮鱼叉:取不到 EngiMissilePainter.Fire.projectilePrefab,会改用 ProjectileCatalog 找 EngiHarpoon。"); } } internal static string Summary() { if (!Enabled) { return ""; } string text = ((AttackSpeedScale > 0f) ? (" 射速受攻速影响×" + AttackSpeedScale.ToString("0.##")) : ""); return " [主武器:热鱼叉 " + Interval.ToString("0.##") + "s / " + DamagePercent.ToString("0") + "%" + text + "]"; } internal static void Tick() { if (!Enabled) { return; } try { if (ResolveIndex()) { ResolveProjectile(); if (!NetworkServer.active) { LogOnce(ref _clientLogged, (LogLevel)16, "肩炮鱼叉:当前不是服务端(联机客户端),开火交给主机处理。"); return; } _present.Clear(); ScanAndFire(); Prune(Time.deltaTime); } } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "肩炮鱼叉:逻辑异常(已忽略本帧)—— " + ex.GetType().Name + " " + ex.Message); } } private static bool ResolveIndex() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (_indexResolved) { return true; } BodyIndex val = BodyCatalog.FindBodyIndex("EngiWalkerTurretBody"); if ((int)val == -1) { return false; } _turretBodyIndex = val; _indexResolved = true; return true; } private static void ResolveProjectile() { if ((Object)(object)_projectilePrefab != (Object)null) { return; } if (_prefabField != null) { object? value = _prefabField.GetValue(null); _projectilePrefab = (GameObject)((value is GameObject) ? value : null); } if ((Object)(object)_projectilePrefab == (Object)null) { int num = ProjectileCatalog.FindProjectileIndex("EngiHarpoon"); if (num >= 0) { _projectilePrefab = ProjectileCatalog.GetProjectilePrefab(num); } } if (!((Object)(object)_projectilePrefab == (Object)null)) { if (_muzzleField != null) { object? value2 = _muzzleField.GetValue(null); _muzzleEffect = (GameObject)((value2 is GameObject) ? value2 : null); } if (!_prefabLogged) { _prefabLogged = true; Log((LogLevel)16, "肩炮鱼叉:已拿到热鱼叉射弹(" + ((Object)_projectilePrefab).name + ")" + (((Object)(object)_muzzleEffect != (Object)null) ? (",枪口火焰 " + ((Object)_muzzleEffect).name) : "") + "。"); } } } private static void ScanAndFire() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) IList<CharacterBody> readOnlyInstancesList = CharacterBody.readOnlyInstancesList; if (readOnlyInstancesList == null) { return; } float deltaTime = Time.deltaTime; for (int i = 0; i < readOnlyInstancesList.Count; i++) { CharacterBody val = readOnlyInstancesList[i]; if ((Object)(object)val == (Object)null || val.bodyIndex != _turretBodyIndex) { continue; } int instanceID = ((Object)val).GetInstanceID(); _present.Add(instanceID); DisableBeamNow(val); if (!((Object)(object)_projectilePrefab == (Object)null)) { if (!_timers.TryGetValue(instanceID, out var value)) { value = 0f; } value -= deltaTime; if (value <= 0f) { value = (Fire(val) ? IntervalFor(val) : 0f); } _timers[instanceID] = value; } } } private static float IntervalFor(CharacterBody turret) { float attackSpeedScale = AttackSpeedScale; if (attackSpeedScale <= 0f) { return Interval; } float num = 1f + (AttackSpeedOf(turret) - 1f) * attackSpeedScale; if (num < 0.1f) { num = 0.1f; } return Mathf.Max(0.05f, Interval / num); } private static float AttackSpeedOf(CharacterBody turret) { CharacterMaster master = turret.master; CharacterMaster val = (((Object)(object)master != (Object)null) ? OwnerMasterOf(master) : null); CharacterBody val2 = (((Object)(object)val != (Object)null) ? val.GetBody() : null); if ((Object)(object)val2 != (Object)null) { return val2.attackSpeed; } LogOnce(ref _ownerMissingLogged, (LogLevel)16, "肩炮鱼叉:找不到炮台的主人,攻击速度改用炮台自己的数值(射速加成可能不生效)。"); return turret.attackSpeed; } private static CharacterMaster OwnerMasterOf(CharacterMaster turretMaster) { if ((Object)(object)turretMaster == (Object)null) { return null; } if (_aiOwnershipType != null && _aiOwnerMasterField != null) { Component component = ((Component)turretMaster).GetComponent(_aiOwnershipType); if ((Object)(object)component != (Object)null) { object? value = _aiOwnerMasterField.GetValue(component); CharacterMaster val = (CharacterMaster)((value is CharacterMaster) ? value : null); if ((Object)(object)val != (Object)null) { return val; } } } if (_minionOwnershipType != null && _minionOwnerMasterProp != null) { Component component2 = ((Component)turretMaster).GetComponent(_minionOwnershipType); if ((Object)(object)component2 != (Object)null) { object? value2 = _minionOwnerMasterProp.GetValue(component2, null); CharacterMaster val2 = (CharacterMaster)((value2 is CharacterMaster) ? value2 : null); if ((Object)(object)val2 != (Object)null) { return val2; } } } return null; } internal static void DisableBeamNow(CharacterBody body) { if (!DisableBeam || (Object)(object)body == (Object)null) { return; } SkillLocator skillLocator = body.skillLocator; GenericSkill val = (((Object)(object)skillLocator != (Object)null) ? skillLocator.primary : null); SkillDef val2 = (((Object)(object)val != (Object)null) ? val.skillDef : null); if ((Object)(object)val2 == (Object)null) { return; } if (val2.baseRechargeInterval != 99999f) { float baseRechargeInterval = val2.baseRechargeInterval; val2.baseRechargeInterval = 99999f; val.RecalculateValues(); if (!_beamLogged) { _beamLogged = true; Log((LogLevel)16, "肩炮鱼叉:已停用移动炮台的激光主武器(" + ((Object)val2).name + ",冷却 " + baseRechargeInterval.ToString("0.##") + " → " + 99999f.ToString("0") + " 秒)。"); } } if (val.stock > 0) { val.stock = 0; } } private static bool Fire(CharacterBody body) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) CharacterMaster master = body.master; BaseAI val = (((Object)(object)master != (Object)null) ? ((Component)master).GetComponent<BaseAI>() : null); if ((Object)(object)val == (Object)null) { return false; } Target currentEnemy = val.currentEnemy; if (currentEnemy == null || (Object)(object)currentEnemy.gameObject == (Object)null) { return false; } Vector3 val2 = (((Object)(object)body.inputBank != (Object)null) ? body.inputBank.aimOrigin : ((Component)body).transform.position); Vector3 position = default(Vector3); if (!currentEnemy.GetBullseyePosition(ref position)) { position = currentEnemy.gameObject.transform.position; } Vector3 val3 = position - val2; if (((Vector3)(ref val3)).sqrMagnitude < 0.25f) { return false; } ((Vector3)(ref val3)).Normalize(); if ((Object)(object)_muzzleEffect != (Object)null) { EffectManager.SimpleEffect(_muzzleEffect, val2, Util.QuaternionSafeLookRotation(val3), true); } float num = body.damage * (DamagePercent / 100f); bool flag = Util.CheckRoll(body.crit, master); MissileUtils.FireMissile(val2, body, default(ProcChainMask), currentEnemy.gameObject, num, flag, _projectilePrefab, (DamageColorIndex)0, Vector3.up, 0f, false); if (!_firstFireLogged) { _firstFireLogged = true; CharacterMaster val4 = (((Object)(object)body.master != (Object)null) ? OwnerMasterOf(body.master) : null); CharacterBody val5 = (((Object)(object)val4 != (Object)null) ? val4.GetBody() : null); Log((LogLevel)16, "肩炮鱼叉:第一发已打出 —— 伤害 " + num.ToString("0.##") + "(" + DamagePercent.ToString("0") + "% × 攻击力 " + body.damage.ToString("0.##") + "),基础间隔 " + Interval.ToString("0.##") + " 秒,实际间隔 " + IntervalFor(body).ToString("0.##") + " 秒。攻击速度:炮台自身 " + body.attackSpeed.ToString("0.##") + (((Object)(object)val5 != (Object)null) ? (",主人 " + val5.attackSpeed.ToString("0.##")) : "(未找到主人)") + ",生效比例 " + AttackSpeedScale.ToString("0.##") + "。"); } return true; } private static void Prune(float delta) { _pruneTimer -= delta; if (_pruneTimer > 0f) { return; } _pruneTimer = 5f; List<int> list = new List<int>(); foreach (int key in _timers.Keys) { if (!_present.Contains(key)) { list.Add(key); } } for (int i = 0; i < list.Count; i++) { _timers.Remove(list[i]); } } private static void LogOnce(ref bool flag, LogLevel level, string message) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!flag) { flag = true; Log(level, message); } } private static void Log(LogLevel level, string message) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (_log != null) { _log.Log(level, (object)message); } } } internal static class EngineerShoulderTurrets { internal sealed class Attachment { public CharacterBody turret; public CharacterBody owner; public int slot; public Collider[] disabledColliders = EmptyColliders; public CharacterDirection disabledDirection; } private const string TurretBodyName = "EngiWalkerTurretBody"; private const string EngineerBodyName = "EngiBody"; private static ManualLogSource _log; private static ConfigEntry<bool> _enabled; private static ConfigEntry<float> _sideOffset; private static ConfigEntry<float> _heightOffset; private static ConfigEntry<float> _tiltAngle; private static ConfigEntry<float> _forwardOffset; private static ConfigEntry<float> _scale; private static ConfigEntry<float> _fallbackRadius; private static readonly List<Attachment> _attached = new List<Attachment>(); private static readonly Dictionary<CharacterBody, int> _slotCounter = new Dictionary<CharacterBody, int>(); private static bool _indicesResolved; private static BodyIndex _turretBodyIndex; private static BodyIndex _engineerBodyIndex; private static Type _aiOwnershipType; private static FieldInfo _aiOwnerMasterField; private static bool _catalogLogged; private static bool _firstAttachLogged; private static bool _failedLogged; private static readonly Collider[] EmptyColliders = (Collider[])(object)new Collider[0]; internal static List<Attachment> Attached => _attached; private static bool Enabled { get { if (_enabled != null) { return _enabled.Value; } return true; } } private static float Side { get { if (_sideOffset == null) { return 0.85f; } return _sideOffset.Value; } } private static float Height { get { if (_heightOffset == null) { return 0.5f; } return _heightOffset.Value; } } private static float Tilt { get { if (_tiltAngle == null) { return 90f; } return _tiltAngle.Value; } } private static float Forward { get { if (_forwardOffset == null) { return 0f; } return _forwardOffset.Value; } } private static float Scale { get { if (_scale == null) { return 0.5f; } return Mathf.Max(0.05f, _scale.Value); } } private static float FallbackRadius { get { if (_fallbackRadius == null) { return 12f; } return _fallbackRadius.Value; } } internal static void Init(ConfigFile config, ManualLogSource log) { _log = log; _enabled = config.Bind<bool>("特殊技能.移动炮台", "ShoulderMount", true, "替代 R「移动炮台」生成的炮台是否吸附在工程师的两肩上(true),而不是在地上自由行走(false)。"); _sideOffset = config.Bind<float>("特殊技能.移动炮台", "SideOffset", 0.85f, "肩部横向偏移:炮台中心离工程师中轴左右各多远。炮台是横躺的(长 2.0 × 缩放 0.5 = 1.0),中心离中轴太近的话内侧会插进工程师身体里(工程师半径 0.5)。"); _heightOffset = config.Bind<float>("特殊技能.移动炮台", "HeightOffset", 0.5f, "肩部高度:以工程师身体根节点(胶囊中心,脚底往上约 0.91)为 0 点往上多少。0 = 和身体中心齐平(约胸口),1.5 = 头顶上方。"); _tiltAngle = config.Bind<float>("特殊技能.移动炮台", "TiltAngle", 90f, "炮台的侧翻角度(度):绕它自己的前后轴翻转,让炮台横躺在肩上、头顶朝外,而不是竖直站在地上。0 = 站着(原版),90 = 完全横躺(右肩的向右倒、左肩的向左倒)。"); _forwardOffset = config.Bind<float>("特殊技能.移动炮台", "ForwardOffset", 0f, "肩部前后偏移:正数朝工程师正面,负数朝背面。"); _scale = config.Bind<float>("特殊技能.移动炮台", "Scale", 0.5f, "炮台缩放:1 = 原版大小(比工程师还高,架在肩上会很夸张)。"); _fallbackRadius = config.Bind<float>("特殊技能.移动炮台", "OwnerSearchRadius", 12f, "兜底归属判定半径(米):读不到 AIOwnership 时(例如联机客户端),在这个半径内找最近的工程师当主人。小于 0 = 不做兜底。"); try { _aiOwnershipType = AccessTools.TypeByName("RoR2.CharacterAI.AIOwnership"); if (_aiOwnershipType != null) { _aiOwnerMasterField = AccessTools.Field(_aiOwnershipType, "ownerMaster"); } } catch (Exception ex) { Log((LogLevel)4, "移动炮台:取 AIOwnership 类型失败(只影响归属判定精度)—— " + ex.GetType().Name + " " + ex.Message); } } internal static string Summary() { if (!Enabled) { return " [移动炮台:原样在地上走]"; } return " [移动炮台:吸附双肩 侧" + Side.ToString("0.##") + " 高" + Height.ToString("0.##") + " 缩放" + Scale.ToString("0.##") + "]"; } internal static void Tick() { if (!Enabled) { return; } try { if (ResolveBodyIndices()) { Prune(); ScanForNew(); AssignSlots(); UpdateAll(); } } catch (Exception ex) { if (!_failedLogged) { _failedLogged = true; Log((LogLevel)4, "移动炮台:肩挂逻辑异常(已忽略本帧)—— " + ex.GetType().Name + " " + ex.Message); } } } private unsafe static bool ResolveBodyIndices() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (_indicesResolved) { return true; } BodyIndex val = BodyCatalog.FindBodyIndex("EngiWalkerTurretBody"); BodyIndex val2 = BodyCatalog.FindBodyIndex("EngiBody"); if ((int)val == -1 || (int)val2 == -1) { return false; } _turretBodyIndex = val; _engineerBodyIndex = val2; _indicesResolved = true; if (!_catalogLogged) { _catalogLogged = true; Log((LogLevel)16, "移动炮台:bodyIndex 已解析 —— EngiWalkerTurretBody=" + ((object)(*(BodyIndex*)(&val))/*cast due to .constrained prefix*/).ToString() + ",EngiBody=" + ((object)(*(BodyIndex*)(&val2))/*cast due to .constrained prefix*/).ToString()); } return true; } private static void Prune() { for (int num = _attached.Count - 1; num >= 0; num--) { Attachment attachment = _attached[num]; if ((Object)(object)attachment.turret == (Object)null) { _attached.RemoveAt(num); } else if ((Object)(object)attachment.owner == (Object)null || (Object)(object)attachment.owner.healthComponent == (Object)null || !attachment.owner.healthComponent.alive) { Restore(attachment); _attached.RemoveAt(num); } else { HealthComponent healthComponent = attachment.turret.healthComponent; if ((Object)(object)healthComponent != (Object)null && !healthComponent.alive) { Restore(attachment); _attached.RemoveAt(num); } } } } private static void AssignSlots() { Dictionary<CharacterBody, int> slotCounter = _slotCounter; slotCounter.Clear(); for (int i = 0; i < _attached.Count; i++) { Attachment attachment = _attached[i]; if (!((Object)(object)attachment.owner == (Object)null)) { if (!slotCounter.TryGetValue(attachment.owner, out var value)) { value = 0; } attachment.slot = value % 2; slotCounter[attachment.owner] = value + 1; } } } private static void UpdateAll() { for (int i = 0; i < _attached.Count; i++) { Attachment attachment = _attached[i]; Rigidbody component = ((Component)attachment.turret).GetComponent<Rigidbody>(); if ((Object)(object)component != (Object)null && !component.isKinematic) { Mount(attachment); } CharacterDirection component2 = ((Component)attachment.turret).GetComponent<CharacterDirection>(); if ((Object)(object)component2 != (Object)null && ((Behaviour)component2).enabled) { ((Behaviour)component2).enabled = false; attachment.disabledDirection = component2; } Follow(attachment); } } internal static void Forget(CharacterBody turret) { if ((Object)(object)turret == (Object)null) { return; } for (int num = _attached.Count - 1; num >= 0; num--) { if (_attached[num].turret == turret) { _attached.RemoveAt(num); } } } private static void ScanForNew() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) List<Attachment> list = new List<Attachment>(); IList<CharacterBody> readOnlyInstancesList = CharacterBody.readOnlyInstancesList; if (readOnlyInstancesList == null) { return; } for (int i = 0; i < readOnlyInstancesList.Count; i++) { CharacterBody val = readOnlyInstancesList[i]; if (!((Object)(object)val == (Object)null) && val.bodyIndex == _turretBodyIndex && !IsTracked(val)) { CharacterBody val2 = FindOwner(val); if (!((Object)(object)val2 == (Object)null)) { Attachment item = new Attachment { turret = val, owner = val2, slot = -1 }; _attached.Add(item); list.Add(item); } } } if (list.Count == 0) { return; } AssignSlots(); for (int j = 0; j < list.Count; j++) { Attachment attachment = list[j]; Mount(attachment); Follow(attachment); if (!_firstAttachLogged) { _firstAttachLogged = true; Log((LogLevel)16, "移动炮台:已把炮台吸到工程师肩上(槽位 " + attachment.slot + ",横向 " + Side.ToString("0.##") + " / 高度 " + Height.ToString("0.##") + " / 前后 " + Forward.ToString("0.##") + " / 侧翻 " + Tilt.ToString("0") + ",缩放 " + Scale.ToString("0.##") + ")。"); } else { Log((LogLevel)32, "移动炮台:新炮台挂到槽位 " + attachment.slot + "。"); } } } private static bool IsTracked(CharacterBody turret) { for (int i = 0; i < _attached.Count; i++) { if (_attached[i].turret == turret) { return true; } } return false; } private static void Mount(Attachment a) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) CharacterBody turret = a.turret; CharacterMotor component = ((Component)turret).GetComponent<CharacterMotor>(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } Rigidbody component2 = ((Component)turret).GetComponent<Rigidbody>(); if ((Object)(object)component2 != (Object)null) { component2.velocity = Vector3.zero; component2.angularVelocity = Vector3.zero; component2.isKinematic = true; } CharacterDirection component3 = ((Component)turret).GetComponent<CharacterDirection>(); if ((Object)(object)component3 != (Object)null && ((Behaviour)component3).enabled) { ((Behaviour)component3).enabled = false; a.disabledDirection = component3; } Collider[] components = ((Component)turret).GetComponents<Collider>(); if (components != null && components.Length != 0) { List<Collider> list = new List<Collider>(); for (int i = 0; i < components.Length; i++) { if ((Object)(object)components[i] != (Object)null && components[i].enabled) { components[i].enabled = false; list.Add(components[i]); } } a.disabledColliders = list.ToArray(); } ((Component)turret).transform.localScale = Vector3.one * Scale; } private static void Follow(Attachment a) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) CharacterBody owner = a.owner; Vector3 val = (((Object)(object)owner.characterDirection != (Object)null) ? owner.characterDirection.forward : ((Component)owner).transform.forward); val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { val = Vector3.forward; } else { ((Vector3)(ref val)).Normalize(); } Vector3 val2 = Vector3.Cross(Vector3.up, val); float num = ((a.slot == 0) ? Side : (0f - Side)); Vector3 position = ((Component)owner).transform.position + Vector3.up * Height + val2 * num + val * Forward; ((Component)a.turret).transform.position = position; Quaternion val3 = Quaternion.LookRotation(val, Vector3.up); float num2 = ((a.slot == 0) ? (0f - Tilt) : Tilt); ((Component)a.turret).transform.rotation = val3 * Quaternion.AngleAxis(num2, Vector3.forward); } private static void Restore(Attachment a) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) CharacterBody turret = a.turret; if ((Object)(object)turret == (Object)null) { return; } for (int i = 0; i < a.disabledColliders.Length; i++) { if ((Object)(object)a.disabledColliders[i] != (Object)null) { a.disabledColliders[i].enabled = true; } } a.disabledColliders = EmptyColliders; if ((Object)(object)a.disabledDirection != (Object)null) { ((Behaviour)a.disabledDirection).enabled = true; a.disabledDirection = null; } ((Component)turret).transform.localScale = Vector3.one; CharacterMotor component = ((Component)turret).GetComponent<CharacterMotor>(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = true; } Rigidbody component2 = ((Component)turret).GetComponent<Rigidbody>(); if ((Object)(object)component2 != (Object)null) { component2.isKinematic = false; } Collider[] components = ((Component)turret).GetComponents<Collider>(); if (components == null) { return; } for (int j = 0; j < components.Length; j++) { if ((Object)(object)components[j] != (Object)null) { components[j].enabled = true; } } } private static CharacterBody FindOwner(CharacterBody turret) { CharacterBody val = FindOwnerByAiOwnership(turret); if ((Object)(object)val == (Object)null) { val = FindNearestEngineer(turret); } if ((Object)(object)val != (Object)null && (Object)(object)val.healthComponent != (Object)null && !val.healthComponent.alive) { return null; } return val; } private static CharacterBody FindOwnerByAiOwnership(CharacterBody turret) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (_aiOwnershipType == null || _aiOwnerMasterField == null || (Object)(object)turret.master == (Object)null) { return null; } Component component = ((Component)turret.master).GetComponent(_aiOwnershipType); if ((Object)(object)component == (Object)null) { return null; } object? value = _aiOwnerMasterField.GetValue(component); CharacterMaster val = (CharacterMaster)((value is CharacterMaster) ? value : null); if ((Object)(object)val == (Object)null) { return null; } CharacterBody body = val.GetBody(); if ((Object)(object)body == (Object)null || body.bodyIndex != _engineerBodyIndex) { return null; } return body; } private static CharacterBody FindNearestEngineer(CharacterBody turret) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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_008e: Unknown result type (might be due to invalid IL or missing references) float fallbackRadius = FallbackRadius; if (fallbackRadius < 0f) { return null; } IList<CharacterBody> readOnlyInstancesList = CharacterBody.readOnlyInstancesList; if (readOnlyInstancesList == null) { return null; } TeamIndex val = (TeamIndex)(((Object)(object)turret.teamComponent != (Object)null) ? ((int)turret.teamComponent.teamIndex) : 0); Vector3 position = ((Component)turret).transform.position; CharacterBody result = null; float num = fallbackRadius * fallbackRadius; for (int i = 0; i < readOnlyInstancesList.Count; i++) { CharacterBody val2 = readOnlyInstancesList[i]; if (!((Object)(object)val2 == (Object)null) && val2.bodyIndex == _engineerBodyIndex && (!((Object)(object)val2.teamComponent != (Object)null) || val2.teamComponent.teamIndex == val)) { Vector3 val3 = ((Component)val2).transform.position - position; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude <= num) { num = sqrMagnitude; result = val2; } } } return result; } private static void Log(LogLevel level, string message) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (_log != null) { _log.Log(level, (object)message); } } } internal static class EngineerSkillText { private const string MineToken = "ENGI_SECONDARY_DESCRIPTION"; private const string BubbleToken = "ENGI_UTILITY_DESCRIPTION"; private const string TurretToken = "ENGI_SPECIAL_ALT1_DESCRIPTION"; private const string MineDefault = "放置一枚能够造成<style=cIsDamage>1000%的伤害</style>的压力地雷,但是需要时间准备。最多放置4枚。"; private const string BubbleDefault = "放置一个圆形防护盾以阻挡伤害。"; private const string TurretDefault = "立即装备便携式鱼叉导弹发射器,发射的导弹可造成<style=cIsDamage>500%的伤害</style>,每次放置2座。"; private static ManualLogSource _log; private static ConfigEntry<bool> _enabled; private static ConfigEntry<string> _languages; private static ConfigEntry<string> _mineText; private static ConfigEntry<string> _bubbleText; private static ConfigEntry<string> _turretText; private static bool _applied; private static bool _errorLogged; private static bool Enabled { get { if (_enabled != null) { return _enabled.Value; } return false; } } private static string[] Languages { get { string text = ((_languages != null) ? _languages.Value : "zh-CN"); if (string.IsNullOrEmpty(text)) { return new string[1] { "zh-CN" }; } string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { array[i] = array[i].Trim(); } return array; } } private static string MineText { get { if (_mineText == null) { return "放置一枚能够造成<style=cIsDamage>1000%的伤害</style>的压力地雷,但是需要时间准备。最多放置4枚。"; } return _mineText.Value; } } private static string BubbleText { get { if (_bubbleText == null) { return "放置一个圆形防护盾以阻挡伤害。"; } return _bubbleText.Value; } } private static string TurretText { get { if (_turretText == null) { return "立即装备便携式鱼叉导弹发射器,发射的导弹可造成<style=cIsDamage>500%的伤害</style>,每次放置2座。"; } return _turretText.Value; } } internal static void Init(ConfigFile config, ManualLogSource log) { _log = log; _enabled = config.Bind<bool>("技能文本", "OverrideSkillText", true, "是否改写工程师三个技能在游戏里显示的说明文本(压力地雷 / 泡泡护盾 / 移动炮台)。"); _languages = config.Bind<string>("技能文本", "Languages", "zh-CN,en", "把这些文本写进哪些语言(逗号分隔的语言代码)。默认中文和英文都写,这样切语言也能看到新文本;只想要中文就填 zh-CN。"); _mineText = config.Bind<string>("技能文本", "MineText", "放置一枚能够造成<style=cIsDamage>1000%的伤害</style>的压力地雷,但是需要时间准备。最多放置4枚。", "M2「压力地雷」的说明文本。"); _bubbleText = config.Bind<string>("技能文本", "BubbleShieldText", "放置一个圆形防护盾以阻挡伤害。", "Shift「泡泡护盾」的说明文本。"); _turretText = config.Bind<string>("技能文本", "WalkerTurretText", "立即装备便携式鱼叉导弹发射器,发射的导弹可造成<style=cIsDamage>500%的伤害</style>,每次放置2座。", "替代 R「移动炮台」的说明文本。"); Apply(); } internal static string Summary() { if (!Enabled) { return ""; } return " [技能文本已改写]"; } internal static void Apply() { if (!Enabled || _applied) { return; } _applied = true; string[] languages = Languages; try { for (int i = 0; i < languages.Length; i++) { Add("ENGI_SECONDARY_DESCRIPTION", MineText, languages[i]); Add("ENGI_UTILITY_DESCRIPTION", BubbleText, languages[i]); Add("ENGI_SPECIAL_ALT1_DESCRIPTION", TurretText, languages[i]); } Log((LogLevel)16, "技能文本:已改写 压力地雷 / 泡泡护盾 / 移动炮台 的说明(语言:" + string.Join("/", languages) + ")。"); } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)2, "技能文本:写入语言表失败 —— " + ex.GetType().Name + " " + ex.Message); } } private static void Add(string token, string text, string language) { if (!string.IsNullOrEmpty(text)) { LanguageAPI.Add(token, text, language); } } private static void LogOnce(ref bool flag, LogLevel level, string message) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!flag) { flag = true; Log(level, message); } } private static void Log(LogLevel level, string message) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (_log != null) { _log.Log(level, (object)message); } } } internal static class EngineerTurretPair { private const string TurretBodyName = "EngiWalkerTurretBody"; private const string TurretMasterName = "EngiWalkerTurretMaster"; private const string TurretSkillName = "PlaceWalkerTurret"; private const string TurretDescriptionToken = "ENGI_SPECIAL_ALT1_DESCRIPTION"; private const float VanillaRecharge = 30f; private const int PairCount = 2; private static ManualLogSource _log; private static ConfigEntry<bool> _enabled; private static ConfigEntry<int> _maxStock; private static ConfigEntry<float> _recharge; private static ConfigEntry<bool> _killOld; private static readonly FieldInfo _reportSummon = AccessTools.Field(typeof(MasterSummonReport), "masterSummon"); private static readonly FieldInfo _reportSummonMaster = AccessTools.Field(typeof(MasterSummonReport), "summonMasterInstance"); private static readonly FieldInfo _reportSummonBody = AccessTools.Field(typeof(MasterSummonReport), "summonBodyInstance"); private static readonly FieldInfo _reportLeader = AccessTools.Field(typeof(MasterSummonReport), "leaderMasterInstance"); private static Type _aiOwnershipType; private static FieldInfo _aiOwnerMasterField; private static readonly Dictionary<int, List<CharacterBody>> _byOwner = new Dictionary<int, List<CharacterBody>>(); private static SkillDef _skillDef; private static bool _stockLogged; private static bool _rechargeLogged; private static bool _spawnLogged; private static bool _errorLogged; private static bool _recursing; private static bool Enabled { get { if (_enabled != null) { return _enabled.Value; } return true; } } private static int MaxStock { get { if (_maxStock == null) { return 1; } return Mathf.Max(1, _maxStock.Value); } } private static float RechargeInterval { get { if (_recharge == null) { return 30f; } return Mathf.Max(0f, _recharge.Value); } } private static bool KillOld { get { if (_killOld != null) { return _killOld.Value; } return true; } } internal static void Init(ConfigFile config, ManualLogSource log) { _log = log; _enabled = config.Bind<bool>("特殊技能.移动炮台.数量", "PairTurrets", true, "是否让「移动炮台」成对生成:技能只有一层,但一次放出来两个(分别挂左右肩)。"); _maxStock = config.Bind<int>("特殊技能.移动炮台.数量", "MaxStock", 1, "移动炮台技能最多能攒几层:原版是 2,改成 1 就是「放一次就用完」。"); _recharge = config.Bind<float>("特殊技能.移动炮台.数量", "RechargeInterval", 60f, "移动炮台技能每层的充能时间(秒):原版是 30 秒。"); _killOld = config.Bind<bool>("特殊技能.移动炮台.数量", "KillOldOnRecast", true, "重放时是否先让上一次那两个炮台死亡(只杀同一个工程师名下的)。"); try { _aiOwnershipType = AccessTools.TypeByName("RoR2.CharacterAI.AIOwnership"); if (_aiOwnershipType != null) { _aiOwnerMasterField = AccessTools.Field(_aiOwnershipType, "ownerMaster"); } } catch (Exception ex) { Log((LogLevel)4, "移动炮台数量:取 AIOwnership 失败(只影响认主精度)—— " + ex.GetType().Name + " " + ex.Message); } MasterSummon.onServerMasterSummonGlobal += OnServerMasterSummon; } internal static string Summary() { if (!Enabled) { return ""; } return " [移动炮台:成对 上限" + MaxStock + "层 冷却" + RechargeInterval.ToString("0.#") + "s" + (KillOld ? " 重放清旧" : "") + "]"; } internal static void Tick() { if (!Enabled) { return; } try { ApplySkillValues(); EnforceCap(); } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "移动炮台数量:异常(已忽略本帧)—— " + ex.GetType().Name + " " + ex.Message); } } private static void ApplySkillValues() { if ((Object)(object)_skillDef == (Object)null) { _skillDef = FindTurretSkill(); if ((Object)(object)_skillDef == (Object)null) { return; } } bool flag = false; int maxStock = MaxStock; if (_skillDef.baseMaxStock != maxStock) { int baseMaxStock = _skillDef.baseMaxStock; _skillDef.baseMaxStock = maxStock; flag = true; if (!_stockLogged) { _stockLogged = true; Log((LogLevel)16, "移动炮台:PlaceWalkerTurret 的层数上限 " + baseMaxStock + " → " + maxStock + "。"); } } float rechargeInterval = RechargeInterval; if (Mathf.Abs(_skillDef.baseRechargeInterval - rechargeInterval) > 0.01f) { float baseRechargeInterval = _skillDef.baseRechargeInterval; _skillDef.baseRechargeInterval = rechargeInterval; flag = true; if (!_rechargeLogged) { _rechargeLogged = true; Log((LogLevel)16, "移动炮台:PlaceWalkerTurret 的冷却 " + baseRechargeInterval.ToString("0.#") + "s → " + rechargeInterval.ToString("0.#") + "s。"); } } if (flag) { RecalculateEngineers(); } } private static SkillDef FindTurretSkill() { IEnumerable<SkillDef> allSkillDefs = SkillCatalog.allSkillDefs; if (allSkillDefs == null) { return null; } foreach (SkillDef item in allSkillDefs) { if (!((Object)(object)item == (Object)null)) { string name = ((Object)item).name; if (string.Equals(item.skillName, "PlaceWalkerTurret", StringComparison.Ordinal) || (name != null && name.IndexOf("PlaceWalkerTurret", StringComparison.Ordinal) >= 0) || string.Equals(item.skillDescriptionToken, "ENGI_SPECIAL_ALT1_DESCRIPTION", StringComparison.Ordinal)) { return item; } } } return null; } private static void RecalculateEngineers() { IList<CharacterBody> readOnlyInstancesList = CharacterBody.readOnlyInstancesList; if (readOnlyInstancesList == null) { return; } for (int i = 0; i < readOnlyInstancesList.Count; i++) { CharacterBody val = readOnlyInstancesList[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.skillLocator == (Object)null)) { GenericSkill special = val.skillLocator.special; if ((Object)(object)special != (Object)null) { special.RecalculateValues(); } } } } private static void OnServerMasterSummon(MasterSummonReport report) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) if (!Enabled) { return; } try { CharacterBody val = (CharacterBody)((_reportSummonBody != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val != (Object)null && IsWalkerTurret(val)) { EngineerShoulderHarpoons.DisableBeamNow(val); object turretMaster = (object)((_reportSummonMaster != null) ? /*isinst with value type is only supported in some contexts*/: null); CharacterMaster val2 = (CharacterMaster)((_reportLeader != null) ? /*isinst with value type is only supported in some contexts*/: null); Register(OwnerOf((CharacterMaster)turretMaster) ?? val2, val); } if (_recursing || !IsWalkerTurret(report, val)) { return; } MasterSummon val3 = (MasterSummon)((_reportSummon != null) ? /*isinst with value type is only supported in some contexts*/: null); if (val3 != null) { CharacterMaster val4 = (CharacterMaster)((_reportLeader != null) ? /*isinst with value type is only supported in some contexts*/: null); int num = 0; if (KillOld) { num = KillOldTurrets(val4, val); } _recursing = true; try { Clone(val3).Perform(); } finally { _recursing = false; } if (!_spawnLogged) { _spawnLogged = true; Log((LogLevel)16, "移动炮台数量:一次放置已补成两个(主人 " + (((Object)(object)val4 != (Object)null) ? ((Object)val4).name : "未知") + (KillOld ? (",清掉旧炮台 " + num + " 个") : "") + ")。"); } else if (KillOld) { Log((LogLevel)32, "移动炮台数量:重放清掉旧炮台 " + num + " 个。"); } } } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "移动炮台数量:处理召唤事件时异常(已忽略)—— " + ex.GetType().Name + " " + ex.Message); } } private static bool IsWalkerTurret(MasterSummonReport report, CharacterBody body) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) CharacterMaster val = (CharacterMaster)((_reportSummonMaster != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val != (Object)null && ((Object)val).name.StartsWith("EngiWalkerTurretMaster", StringComparison.Ordinal)) { return true; } if ((Object)(object)body != (Object)null) { return IsWalkerTurret(body); } return false; } private static bool IsWalkerTurret(CharacterBody body) { return ((Object)body).name.StartsWith("EngiWalkerTurretBody", StringComparison.Ordinal); } private static int KeyFor(CharacterMaster owner) { if (!((Object)(object)owner != (Object)null)) { return 0; } return ((Object)owner).GetInstanceID(); } private static List<CharacterBody> ListFor(int key) { if (!_byOwner.TryGetValue(key, out var value)) { value = new List<CharacterBody>(); _byOwner[key] = value; } return value; } private static void Register(CharacterMaster owner, CharacterBody body) { if ((Object)(object)body == (Object)null) { return; } List<CharacterBody> list = ListFor(KeyFor(owner)); for (int i = 0; i < list.Count; i++) { if (list[i] == body) { return; } } list.Add(body); } private static CharacterMaster OwnerOf(CharacterMaster turretMaster) { if ((Object)(object)turretMaster == (Object)null || _aiOwnershipType == null || _aiOwnerMasterField == null) { return null; } Component component = ((Component)turretMaster).GetComponent(_aiOwnershipType); if ((Object)(object)component == (Object)null) { return null; } object? value = _aiOwnerMasterField.GetValue(component); return (CharacterMaster)((value is CharacterMaster) ? value : null); } private static int KillOldTurrets(CharacterMaster owner, CharacterBody keep) { List<CharacterBody> list = ListFor(KeyFor(owner)); List<CharacterBody> list2 = new List<CharacterBody>(); for (int i = 0; i < list.Count; i++) { CharacterBody val = list[i]; if (!((Object)(object)val == (Object)null) && val != keep) { list2.Add(val); } } for (int j = 0; j < list2.Count; j++) { list.Remove(list2[j]); Kill(list2[j]); } return list2.Count; } private static void EnforceCap() { List<int> list = new List<int>(_byOwner.Keys); for (int i = 0; i < list.Count; i++) { List<CharacterBody> list2 = _byOwner[list[i]]; for (int num = list2.Count - 1; num >= 0; num--) { if ((Object)(object)list2[num] == (Object)null) { list2.RemoveAt(num); } } while (list2.Count > 2) { CharacterBody turret = list2[0]; list2.RemoveAt(0); Kill(turret); } } } private static void Kill(CharacterBody turret) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)turret == (Object)null) { return; } EngineerShoulderTurrets.Forget(turret); CharacterMaster master = turret.master; if ((Object)(object)master != (Object)null) { master.TrueKill(); return; } HealthComponent healthComponent = turret.healthComponent; if ((Object)(object)healthComponent != (Object)null) { healthComponent.Suicide((GameObject)null, (GameObject)null, DamageTypeCombo.op_Implicit((DamageType)0)); } } private static MasterSummon Clone(MasterSummon source) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown MasterSummon val = new MasterSummon(); FieldInfo[] fields = typeof(MasterSummon).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < fields.Length; i++) { fields[i].SetValue(val, fields[i].GetValue(source)); } return val; } private static void LogOnce(ref bool flag, LogLevel level, string message) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!flag) { flag = true; Log(level, message); } } private static void Log(LogLevel level, string message) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (_log != null) { _log.Log(level, (object)message); } } } internal static class EngineerWalkerTurretAI { private const string StaticMasterName = "EngiTurretMaster"; private const string WalkerMasterName = "EngiWalkerTurretMaster"; private static readonly string[] DriverFields = new string[28] { "customName", "skillSlot", "requiredSkill", "requireSkillReady", "requireEquipmentReady", "shouldFireEquipment", "minUserHealthFraction", "maxUserHealthFraction", "minTargetHealthFraction", "maxTargetHealthFraction", "minDistance", "maxDistance", "selectionRequiresTargetLoS", "selectionRequiresOnGround", "selectionRequiresTargetNonFlier", "selectionRequiresAimTarget", "maxTimesSelected", "moveTargetType", "activationRequiresTargetLoS", "activationRequiresAimTargetLoS", "activationRequiresAimConfirmation", "movementType", "moveInputScale", "aimVectorDampTimeOverride", "aimVectorMaxSpeedOverride", "aimType", "ignoreNodeGraph", "shouldSprint" }; private static readonly string[] AiFields = new string[17] { "fullVision", "xrayVision", "retaliateUntargetable", "neverRetaliateFriendlies", "ignoreFliers", "isHealer", "prioritizePlayers", "copyLeaderTarget", "enemyAttentionDuration", "leaderTargetCooldown", "desiredSpawnNodeGraphType", "aimVectorDampTime", "aimVectorMaxSpeed", "aimVectorDampTimeOverride", "aimVectorMaxSpeedOverride", "shouldMissFirstOffScreenShot", "forceUpdateEveryFrame" }; private static readonly string[] SkippedAiFields = new string[2] { "targetRefreshInterval", "maxVisionDistance" }; private static ManualLogSource _log; private static ConfigEntry<bool> _enabled; private static FieldInfo[] _driverFields; private static FieldInfo[] _aiFields; private static GameObject _staticPrefab; private static GameObject _walkerPrefab; private static MasterIndex _walkerMasterIndex; private static bool _resolved; private static bool _prefabApplied; private static readonly HashSet<int> _syncedInstances = new HashSet<int>(); private static bool _missingLogged; private static bool _errorLogged; private static bool Enabled { get { if (_enabled != null) { return _enabled.Value; } return true; } } internal static void Init(ConfigFile config, ManualLogSource log) { _log = log; _enabled = config.Bind<bool>("特殊技能.移动炮台.AI", "CopyStaticTurretAI", true, "让移动炮台的 AI 完整照抄固定炮台:站定在原地、60 米内对有视线的敌人瞄准后才开火,不再追击、绕圈、跑回主人身边。改的是 master prefab,关掉本项需要重开游戏才恢复。"); _driverFields = Resolve(typeof(AISkillDriver), DriverFields); _aiFields = Resolve(typeof(BaseAI), AiFields); } internal static string Summary() { if (!Enabled) { return ""; } return " [移动炮台 AI = 固定炮台 AI]"; } private static FieldInfo[] Resolve(Type type, string[] names) { List<FieldInfo> list = new List<FieldInfo>(); for (int i = 0; i < names.Length; i++) { FieldInfo fieldInfo = AccessTools.Field(type, names[i]); if (fieldInfo != null) { list.Add(fieldInfo); } else if (!_missingLogged) { _missingLogged = true; if (_log != null) { _log.Log((LogLevel)4, (object)("移动炮台 AI:取不到字段 " + type.Name + "." + names[i] + "(该字段不做照搬,其余照常)。")); } } } return list.ToArray(); } internal static void Tick() { if (!Enabled) { return; } try { if (_resolved || ResolvePrefabs()) { if (!_prefabApplied) { Apply(_staticPrefab, _walkerPrefab, "移动炮台 prefab"); _prefabApplied = true; } SyncExisting(); } } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "移动炮台 AI:照搬时异常(已忽略本帧)—— " + ex.GetType().Name + " " + ex.Message); } } private static bool ResolvePrefabs() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) MasterIndex val = MasterCatalog.FindMasterIndex("EngiTurretMaster"); MasterIndex val2 = MasterCatalog.FindMasterIndex("EngiWalkerTurretMaster"); if (val == MasterIndex.none || val2 == MasterIndex.none) { return false; } GameObject masterPrefab = MasterCatalog.GetMasterPrefab(val); GameObject masterPrefab2 = MasterCatalog.GetMasterPrefab(val2); if ((Object)(object)masterPrefab == (Object)null || (Object)(object)masterPrefab2 == (Object)null) { return false; } _staticPrefab = masterPrefab; _walkerPrefab = masterPrefab2; _walkerMasterIndex = val2; _resolved = true; Log((LogLevel)16, "移动炮台 AI:已定位到两个 master prefab(模板 EngiTurretMaster → 目标 EngiWalkerTurretMaster)。"); return true; } private static void Apply(GameObject src, GameObject dst, string what) { BaseAI component = src.GetComponent<BaseAI>(); BaseAI component2 = dst.GetComponent<BaseAI>(); if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null) { Copy(component, component2, _aiFields); } AISkillDriver[] components = src.GetComponents<AISkillDriver>(); AISkillDriver[] components2 = dst.GetComponents<AISkillDriver>(); if (components == null || components2 == null) { return; } int num = Math.Min(components.Length, components2.Length); List<string> list = new List<string>(); for (int i = 0; i < components2.Length; i++) { AISkillDriver val = components2[i]; if ((Object)(object)val == (Object)null) { continue; } if (i < num && (Object)(object)components[i] != (Object)null) { AISkillDriver val2 = components[i]; string customName = val.customName; Copy(val2, val, _driverFields); ((Behaviour)val).enabled = ((Behaviour)val2).enabled; list.Add(" " + customName + " → " + val.customName + "(" + val.minDistance.ToString("0.#") + "~" + (float.IsInfinity(val.maxDistance) ? "∞" : val.maxDistance.ToString("0.#")) + "m,skillSlot " + ((object)Unsafe.As<SkillSlot, SkillSlot>(ref val.skillSlot)/*cast due to .constrained prefix*/).ToString() + ")"); } else { list.Add(" " + val.customName + " → 停用"); if (((Behaviour)val).enabled) { ((Behaviour)val).enabled = false; } } } if (list.Count > 0) { Log((LogLevel)16, "移动炮台 AI 已照抄固定炮台(" + what + "):"); for (int j = 0; j < list.Count; j++) { Log((LogLevel)16, list[j]); } } } private static void SyncExisting() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) IList<CharacterMaster> readOnlyInstancesList = CharacterMaster.readOnlyInstancesList; if (readOnlyInstancesList == null) { return; } for (int i = 0; i < readOnlyInstancesList.Count; i++) { CharacterMaster val = readOnlyInstancesList[i]; if (!((Object)(object)val == (Object)null) && !(val.masterIndex != _walkerMasterIndex)) { int instanceID = ((Object)val).GetInstanceID(); if (!_syncedInstances.Contains(instanceID)) { _syncedInstances.Add(instanceID); Apply(_staticPrefab, ((Component)val).gameObject, "场上已有炮台"); } } } } private static void Copy(object src, object dst, FieldInfo[] fields) { foreach (FieldInfo fieldInfo in fields) { try { fieldInfo.SetValue(dst, fieldInfo.GetValue(src)); } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "移动炮台 AI:字段 " + fieldInfo.Name + " 照搬失败(已跳过)—— " + ex.GetType().Name + " " + ex.Message); } } } private static void LogOnce(ref bool flag, LogLevel level, string message) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!flag) { flag = true; Log(level, message); } } private static void Log(LogLevel level, string message) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (_log != null) { _log.Log(level, (object)message); } } } internal static class EngineerWalkerTurretStats { private static ManualLogSource _log; private static ConfigEntry<bool> _rangeEnabled; private static ConfigEntry<float> _rangeMultiplier; private static readonly FieldInfo _beamRangeField = AccessTools.Field(typeof(FireBeam), "maxDistance"); private static float _beamBase = -1f; private static float _beamLastWritten = float.MinValue; private static bool _missingLogged; private static bool _errorLogged; private static bool _beamLogged; private static bool RangeEnabled { get { if (_rangeEnabled != null) { return _rangeEnabled.Value; } return true; } } private static float RangeMul { get { if (_rangeMultiplier == null) { return 1.5f; } return Mathf.Max(0f, _rangeMultiplier.Value); } } internal static void Init(ConfigFile config, ManualLogSource log) { _log = log; _rangeEnabled = config.Bind<bool>("特殊技能.移动炮台.数值", "BoostBeamRange", true, "是否加长移动炮台的激光射程。"); _rangeMultiplier = config.Bind<float>("特殊技能.移动炮台.数值", "BeamRangeMultiplier", 1.5f, "激光射程倍率:1 = 原版(FireBeam.maxDistance 25 米),2.4 = 刚好覆盖照抄后的 60 米索敌判定。"); if (_beamRangeField == null && !_missingLogged) { _missingLogged = true; Log((LogLevel)4, "移动炮台数值:取不到 FireBeam.maxDistance,激光射程改动不生效。"); } } internal static string Summary() { if (!RangeEnabled) { return ""; } float num = ((_beamBase > 0f) ? _beamBase : 25f); return " [激光射程 " + num.ToString("0.#") + " → " + (num * RangeMul).ToString("0.#") + "m]"; } internal static void Patch(Harmony harmony) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown if (!RangeEnabled) { Log((LogLevel)16, "移动炮台数值:按配置保持原版激光射程,不挂补丁。"); return; } Type typeFromHandle = typeof(FireBeam); MethodInfo methodInfo = AccessTools.Method(typeFromHandle, "OnEnter", (Type[])null, (Type[])null); if (methodInfo == null) { Log((LogLevel)2, "移动炮台数值补丁失败:取不到 " + typeFromHandle.FullName + ".OnEnter。"); return; } harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(EngineerWalkerTurretStats), "FireBeamOnEnterPostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log((LogLevel)16, "补丁已挂载:" + typeFromHandle.Name + ".OnEnter <- EngineerWalkerTurretStats.FireBeamOnEnterPostfix(移动炮台激光射程 ×" + RangeMul.ToString("0.##") + ")"); } private static void FireBeamOnEnterPostfix(FireBeam __instance) { try { if (__instance == null) { return; } float num = ((_beamRangeField != null) ? ((float)_beamRangeField.GetValue(__instance)) : 0f); if (num <= 0f) { return; } if (Mathf.Abs(num - _beamLastWritten) > 0.001f) { _beamBase = num; } float num2 = _beamBase * RangeMul; if (!(Mathf.Abs(num - num2) <= 0.001f)) { _beamRangeField.SetValue(__instance, num2); _beamLastWritten = num2; if (!_beamLogged) { _beamLogged = true; Log((LogLevel)16, "移动炮台激光射程:" + _beamBase.ToString("0.##") + " → " + num2.ToString("0.##") + " 米(FireBeam.maxDistance;只有移动炮台用这个状态,默认炮台走 FireGauss 不受影响)。"); } } } catch (Exception ex) { LogOnce(ref _errorLogged, (LogLevel)4, "移动炮台数值:改激光射程时异常(已忽略)—— " + ex.GetType().Name + " " + ex.Message); } } private static void LogOnce(ref bool flag, LogLevel level, string message) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!flag) { flag = true; Log(level, message); } } private static void Log(LogLevel level, string message) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (_log != null) { _log.Log(level, (object)message); } } } [BepInPlugin("com.sparkletwilight.sparkleengineertweaks", "SparkleEngineerTweaks", "1.11.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class SparkleEngineerTweaksPlugin : BaseUnityPlugin { public const string PluginGUID = "com.sparkletwilight.sparkleengineertweaks"; public const string PluginName = "SparkleEngineerTweaks"; public const string PluginVersion = "1.11.0"; public void Awake() { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown EngineerShoulderTurrets.Init(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); EngineerWalkerTurretAI.Init(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); EngineerShoulderHarpoons.Init(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); EngineerTurretPair.Init(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); EngineerWalkerTurretStats.Init(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); EngineerBubbleShieldArmor.Init(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); EngineerPressureMine.Init(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); EngineerSkillText.Init(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); Harmony harmony = new Harmony("com.sparkletwilight.sparkleengineertweaks"); SafePatch("泡泡护盾护甲", delegate { EngineerBubbleShieldArmor.Patch(harmony); }); SafePatch("移动炮台数值", delegate { EngineerWalkerTurretStats.Patch(harmony); }); SafePatch("压力地雷", delegate { EngineerPressureMine.Patch(harmony); }); ((BaseUnityPlugin)this).Logger.LogInfo((object)("SparkleEngineerTweaks v1.11.0 已加载。" + EngineerShoulderTurrets.Summary() + EngineerWalkerTurretAI.Summary() + EngineerShoulderHarpoons.Summary() + EngineerTurretPair.Summary() + EngineerWalkerTurretStats.Summary() + EngineerBubbleShieldArmor.Summary() + EngineerPressureMine.Summary() + EngineerSkillText.Summary())); } public void Update() { EngineerShoulderTurrets.Tick(); EngineerWalkerTurretAI.Tick(); EngineerTurretPair.Tick(); EngineerShoulderHarpoons.Tick(); EngineerBubbleShieldArmor.Tick(); } private void SafePatch(string label, Action patch) { try { patch(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("SparkleEngineerTweaks:「" + label + "」挂补丁时抛异常,已跳过该模块 —— " + ex.GetType().Name + " " + ex.Message)); } } } }