Decompiled source of MCIllagersMod v1.0.4

Illagers.dll

Decompiled 13 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using REPOLib.Modules;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.Serialization;

[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("Luca")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Illager enemies (Vindicator) for R.E.P.O.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+35c7f069872c93e96508ede468dac450cb04f289")]
[assembly: AssemblyProduct("Illagers")]
[assembly: AssemblyTitle("Illagers")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 Illagers
{
	public sealed class EvokerBehaviour : MonoBehaviour
	{
		private enum GroundMiss
		{
			None,
			NoGround,
			Blocked,
			HeightTolerance
		}

		[Header("引用(G1 第 7 条 / G5 第 7 条)")]
		[Tooltip("同一 Controller 上的表现层组件;由 EvokerVisuals 拖入这里。")]
		public EvokerVisuals Visuals;

		[Tooltip("恼鬼专用的 Enemy Setup(G8 第 4 条)。**不要**为它创建会进自然刷怪池的 EnemyContent。")]
		public EnemySetup VexSetup;

		[Tooltip("恼鬼 Prefab。用于独立注册网络 Prefab(不走 EnemyContent 的自然敌人注册路径)。")]
		public GameObject VexPrefab;

		[Tooltip("地刺 Prefab(G5 第 1 条)。仅作为本地展示物的模板,不注册网络 Prefab。")]
		public GameObject FangPrefab;

		[Tooltip("不死图腾 Prefab(G7)。掉落时走网络 Prefab 生成。")]
		public GameObject TotemPrefab;

		[Tooltip("施法表现的原点(G5 第 6 条):直线阵型的起点方向、召唤点的参考。必须挂在稳定节点下,**不要**放进会被双模型显隐关闭的子树。")]
		public Transform SpellOrigin;

		[Tooltip("E3 的 3 个出生点(G5 第 6 条)。数量与顺序对应 EvokerTuning.SummonCount。")]
		public Transform[] SummonPoints;

		private const float MovingSpeedThreshold = 0.1f;

		private const float IdleVoiceIntervalMin = 4f;

		private const float IdleVoiceIntervalMax = 7f;

		private const float DespawnDelay = 1.17f;

		private const float GroundSearchUp = 1.5f;

		private const float GroundSearchDown = 3f;

		private const float MinSupportNormalY = 0.5f;

		private const float OccupancyRadius = 0.15f;

		private const float OccupancyProbeHeight = 0.5f;

		private const int GroundBufferSize = 24;

		private int _skipNoGround;

		private int _skipBlocked;

		private int _skipHeight;

		private const float LineStartOffset = 1f;

		private const float WarnInterval = 10f;

		private EnemyBombThrower _bomb;

		private Enemy _enemy;

		private EnemyParent _parent;

		private EnemyHealth _health;

		private EnemyStateStunned _stunState;

		private EnemyNavMeshAgent _navMesh;

		private PhotonView _photonView;

		private bool _initDone;

		private bool _initOk;

		private bool _isAuthority;

		private EvokerCastPhase _phase = EvokerCastPhase.None;

		private int _spawnGeneration;

		private float _actionEndsAt;

		private EvokerSpell _currentSpell = EvokerSpell.None;

		private float _gapEndsAt;

		private int _castSequence;

		private float _summonReadyAt;

		private float _fangReadyAt;

		private bool _firstSummonDone;

		private bool _exitCleanupDone;

		private bool _summonAvailable;

		private bool _fangAvailable;

		private float _lastSummonWarnAt = -20f;

		private float _lastFangWarnAt = -20f;

		private float _lastGroundWarnAt = -20f;

		private int _lastHealthMax = -1;

		private int _lastHealthCurrent = -1;

		private EvokerVisualState _appliedVisual = (EvokerVisualState)(-1);

		private float _idleVoiceTimer;

		private EvokerCastPhase _netPhase = EvokerCastPhase.None;

		private EvokerSpell _netSpell = EvokerSpell.None;

		private float _netActionEndsAt;

		private EvokerCastPhase _lastSyncedCastPhase = (EvokerCastPhase)(-1);

		private bool _despawning;

		private float _despawnTimer;

		private bool _deathDropDone;

		private bool _healthEventsBound;

		private readonly RaycastHit[] _groundBuffer = (RaycastHit[])(object)new RaycastHit[24];

		private readonly Collider[] _overlapBuffer = (Collider[])(object)new Collider[12];

		private static int _playerLayer = int.MinValue;

		private static int _levelMask = int.MinValue;

		internal EvokerCastPhase CurrentPhase => _phase;

		internal bool CastInFlight => _phase != EvokerCastPhase.None;

		internal float CastRemainingSeconds => _actionEndsAt - Time.time;

		internal int SpawnGeneration => _spawnGeneration;

		private static bool DebugOn => Plugin.DebugLogging != null && Plugin.DebugLogging.Value;

		private static int PlayerLayer
		{
			get
			{
				if (_playerLayer == int.MinValue)
				{
					_playerLayer = LayerMask.NameToLayer("Player");
				}
				return _playerLayer;
			}
		}

		private static int LevelMask
		{
			get
			{
				if (_levelMask == int.MinValue)
				{
					_levelMask = LayerMask.GetMask(new string[1] { "Default" });
				}
				return _levelMask;
			}
		}

		private EvokerCastPhase DisplayPhase => _isAuthority ? _phase : _netPhase;

		private EvokerSpell DisplaySpell => _isAuthority ? _currentSpell : _netSpell;

		private float DisplayActionEndsAt => _isAuthority ? _actionEndsAt : _netActionEndsAt;

		private float CastNormalizedTime
		{
			get
			{
				float num = 1f;
				if (num <= 0f)
				{
					return 0f;
				}
				float num2 = Mathf.Clamp(DisplayActionEndsAt - Time.time, 0f, num);
				return Mathf.Clamp01((num - num2) / num);
			}
		}

		internal static EvokerBehaviour FindOn(EnemyBombThrower controller)
		{
			return ((Object)(object)controller == (Object)null) ? null : ((Component)controller).GetComponent<EvokerBehaviour>();
		}

		internal static EvokerBehaviour FindOnAnim(EnemyBombThrowerAnim anim)
		{
			return ((Object)(object)anim == (Object)null) ? null : FindOn(anim.controller);
		}

		internal static EvokerBehaviour FindOnHead(EnemyBombThrowerHead head)
		{
			return ((Object)(object)head == (Object)null) ? null : FindOn(head.controller);
		}

		private void Awake()
		{
			EnsureInit();
		}

		private void OnEnable()
		{
			if (EnsureInit())
			{
				ResetForSpawn();
			}
		}

		private void OnDisable()
		{
			if (_initOk)
			{
				EnterExitCleanup("退场/失活");
			}
		}

		private void OnDestroy()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			if ((Object)(object)_health != (Object)null && _healthEventsBound)
			{
				UnityEvent onHurt = _health.onHurt;
				if (onHurt != null)
				{
					onHurt.RemoveListener(new UnityAction(OnHurtCallback));
				}
				UnityEvent onDeath = _health.onDeath;
				if (onDeath != null)
				{
					onDeath.RemoveListener(new UnityAction(OnDeathCallback));
				}
				_healthEventsBound = false;
			}
		}

		private void Update()
		{
			if (!EnsureInit())
			{
				return;
			}
			try
			{
				_isAuthority = SemiFunc.IsMasterClientOrSingleplayer();
				UpdateVisualState();
				UpdateIdleVoice(Time.deltaTime);
				UpdateDespawn();
				if (_isAuthority)
				{
					EnsureMaxHealth();
					SyncHealthToClients();
					SyncCastPhaseToClients();
					TickCasting(Time.time);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("[Evoker] Update 异常(本帧已跳过,下帧继续):" + ex));
				}
			}
		}

		private bool EnsureInit()
		{
			if (_initDone)
			{
				return _initOk;
			}
			_initDone = true;
			try
			{
				_bomb = ((Component)this).GetComponent<EnemyBombThrower>();
				_enemy = ((Component)this).GetComponent<Enemy>() ?? ((Component)this).GetComponentInParent<Enemy>();
				_parent = ((Component)this).GetComponentInParent<EnemyParent>();
				_health = ((Component)this).GetComponent<EnemyHealth>();
				_stunState = ((Component)this).GetComponent<EnemyStateStunned>();
				_navMesh = ((Component)this).GetComponent<EnemyNavMeshAgent>();
				if ((Object)(object)_bomb == (Object)null || (Object)(object)_enemy == (Object)null || (Object)(object)_parent == (Object)null)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogError((object)"[Evoker] EvokerBehaviour 必须与 EnemyBombThrower / Enemy 同挂 Enable/Controller,并通过父物体找到 EnemyParent。组件已停用,不做半死不活的静默运行。");
					}
					_initOk = false;
					((Behaviour)this).enabled = false;
					return false;
				}
				_photonView = (((Object)(object)_enemy.PhotonView != (Object)null) ? _enemy.PhotonView : ((Component)this).GetComponent<PhotonView>());
				_fangAvailable = (Object)(object)FangPrefab != (Object)null;
				if (_fangAvailable)
				{
					EvokerNetwork.SetFangPrefab(FangPrefab);
				}
				else
				{
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogError((object)"[Evoker] FangPrefab 未拖引用:地刺技能已关闭(不会用空壳假装释放成功,G5 第 1/7 条)。GUI 里请把 Prefabs/EvokerFang.prefab 拖到该字段。");
					}
				}
				_summonAvailable = (Object)(object)VexSetup != (Object)null && (Object)(object)VexPrefab != (Object)null;
				if (_summonAvailable)
				{
					EvokerSummons.SetVexAssets(VexSetup, VexPrefab);
				}
				else
				{
					ManualLogSource log3 = Plugin.Log;
					if (log3 != null)
					{
						log3.LogError((object)"[Evoker] VexSetup / VexPrefab 未拖引用:召唤技能已关闭(G8 第 4 条)。");
					}
				}
				if ((Object)(object)TotemPrefab != (Object)null)
				{
					EvokerSummons.SetTotemPrefab(TotemPrefab);
				}
				else
				{
					ManualLogSource log4 = Plugin.Log;
					if (log4 != null)
					{
						log4.LogWarning((object)"[Evoker] TotemPrefab 未拖引用:死亡不会掉落不死图腾(D1),其余功能不受影响。");
					}
				}
				if ((Object)(object)Visuals == (Object)null)
				{
					ManualLogSource log5 = Plugin.Log;
					if (log5 != null)
					{
						log5.LogWarning((object)"[Evoker] EvokerVisuals 未拖引用:动画与语音全部静默跳过(G1 第 7 条)。");
					}
				}
				if ((Object)(object)SpellOrigin == (Object)null)
				{
					ManualLogSource log6 = Plugin.Log;
					if (log6 != null)
					{
						log6.LogWarning((object)"[Evoker] SpellOrigin 未拖引用:直线阵型退回用自身 Transform 定方向(G5 第 6 条)。");
					}
				}
				if (SummonPoints == null || SummonPoints.Length == 0)
				{
					ManualLogSource log7 = Plugin.Log;
					if (log7 != null)
					{
						log7.LogWarning((object)"[Evoker] SummonPoints 未配:召唤点为空,SummonBatch 会拿不到出生点(G5 第 6 条要求数量与 EvokerTuning.SummonCount 对应)。");
					}
				}
				VerifyVisionDots();
				DisableLegacyHurtCollider();
				MuteLegacySounds();
				SuppressLegacyParticles();
				ResetLegacyHead();
				BindHealthEvents();
				_initOk = true;
				ManualLogSource log8 = Plugin.Log;
				if (log8 != null)
				{
					log8.LogInfo((object)("[Evoker] 初始化完成:" + EvokerTuning.Preview()));
				}
				ManualLogSource log9 = Plugin.Log;
				if (log9 != null)
				{
					log9.LogInfo((object)("[Evoker] 能力:地刺=" + (_fangAvailable ? "开" : "关") + ",召唤=" + (_summonAvailable ? "开" : "关") + ",图腾=" + (_healthEventsBound ? "开" : "关") + ",表现层=" + (((Object)(object)Visuals != (Object)null) ? "有" : "无")));
				}
				return true;
			}
			catch (Exception ex)
			{
				_initOk = false;
				ManualLogSource log10 = Plugin.Log;
				if (log10 != null)
				{
					log10.LogError((object)("[Evoker] 初始化异常,组件已停用:" + ex));
				}
				((Behaviour)this).enabled = false;
				return false;
			}
		}

		internal void ResetForSpawn()
		{
			_spawnGeneration++;
			_phase = EvokerCastPhase.None;
			_currentSpell = EvokerSpell.None;
			_gapEndsAt = 0f;
			_actionEndsAt = 0f;
			_castSequence = 0;
			_firstSummonDone = false;
			_exitCleanupDone = false;
			_summonReadyAt = 0f;
			_fangReadyAt = 0f;
			_despawning = false;
			_despawnTimer = 0f;
			_deathDropDone = false;
			_appliedVisual = (EvokerVisualState)(-1);
			_idleVoiceTimer = Random.Range(4f, 7f);
			_lastHealthMax = -1;
			_lastHealthCurrent = -1;
			_netPhase = EvokerCastPhase.None;
			_netSpell = EvokerSpell.None;
			_netActionEndsAt = 0f;
			_lastSyncedCastPhase = (EvokerCastPhase)(-1);
			ResetLegacyHead();
			Visuals?.ResetForSpawn();
			if ((Object)(object)_parent != (Object)null && !_parent.Spawned)
			{
				Plugin.Debug("[Evoker] ResetForSpawn 时 EnemyParent.Spawned 仍为 false(SpawnRPC 尚未执行)");
			}
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				EnsureMaxHealth();
				EnemyHealth health = _health;
				if (health != null)
				{
					health.OnSpawn();
				}
			}
			else if (SemiFunc.IsMultiplayer() && (Object)(object)_photonView != (Object)null && (Object)(object)_parent != (Object)null && _parent.Spawned)
			{
				_photonView.RPC("RequestSyncRPC", (RpcTarget)2, Array.Empty<object>());
			}
		}

		private void EnterExitCleanup(string reason)
		{
			if (!_exitCleanupDone)
			{
				_exitCleanupDone = true;
				if (_phase != EvokerCastPhase.None)
				{
					Plugin.Debug($"[Evoker] 取消未释放的施法({reason})阶段={_phase} 技能={_currentSpell}");
					ClearAction();
				}
				_netPhase = EvokerCastPhase.None;
				_netSpell = EvokerSpell.None;
				_netActionEndsAt = 0f;
				Visuals?.ApplyState(EvokerVisualState.Stand);
				_appliedVisual = EvokerVisualState.Stand;
				Visuals?.HideCastParticles();
			}
		}

		private void TickCasting(float now)
		{
			switch (_phase)
			{
			case EvokerCastPhase.None:
			{
				EvokerSpell evokerSpell = SelectSpell(now);
				if (evokerSpell != EvokerSpell.None)
				{
					BeginAction(now, evokerSpell);
				}
				break;
			}
			case EvokerCastPhase.Gap:
				if (!GateOk(requireDistance: true, "空档结束"))
				{
					CancelAction("空档结束时门控失效");
				}
				else if (!(now < _gapEndsAt))
				{
					_phase = EvokerCastPhase.Casting;
					Visuals?.PlayVoice(EvokerVoice.Cast);
					Plugin.Debug($"[Evoker] 开始施法 {_currentSpell},释放时刻 {_actionEndsAt:F2}");
				}
				break;
			case EvokerCastPhase.Casting:
			{
				if (now < _actionEndsAt)
				{
					break;
				}
				if (!GateOk(requireDistance: true, "释放"))
				{
					CancelAction("释放时门控失效");
					break;
				}
				EvokerSpell currentSpell = _currentSpell;
				if (!TryGetTarget(out var target))
				{
					CancelAction("释放时无有效目标");
					break;
				}
				if (currentSpell switch
				{
					EvokerSpell.Summon => ReleaseSummon(now), 
					EvokerSpell.Fang => ReleaseFang(now, target), 
					_ => false, 
				})
				{
					_castSequence++;
					switch (currentSpell)
					{
					case EvokerSpell.Summon:
						_firstSummonDone = true;
						_summonReadyAt = now + IllagerConfig.EvokerSummonCooldown.Value;
						break;
					case EvokerSpell.Fang:
						_fangReadyAt = now + IllagerConfig.EvokerFangCooldown.Value;
						break;
					}
					Plugin.Debug($"[Evoker] 释放#{_castSequence} {currentSpell}(召唤就绪 {_summonReadyAt:F2}," + $"地刺就绪 {_fangReadyAt:F2})");
				}
				else
				{
					Plugin.Debug($"[Evoker] {currentSpell} 释放失败:不消耗 CD,下一帧重新走门控");
				}
				ClearAction();
				break;
			}
			}
		}

		private EvokerSpell SelectSpell(float now)
		{
			if (!GateOk(requireDistance: true, "选技能"))
			{
				return EvokerSpell.None;
			}
			if (!_firstSummonDone)
			{
				if (!_summonAvailable)
				{
					WarnThrottled(ref _lastSummonWarnAt, "[Evoker] 首次施法必须是召唤,但召唤能力已关闭(VexSetup/VexPrefab 缺失):唤魔者将一直不起手,直到补齐引用。");
					return EvokerSpell.None;
				}
				return (now >= _summonReadyAt) ? EvokerSpell.Summon : EvokerSpell.None;
			}
			bool flag = _summonAvailable && now >= _summonReadyAt;
			bool flag2 = _fangAvailable && now >= _fangReadyAt;
			if (flag && flag2)
			{
				return (!(_fangReadyAt < _summonReadyAt)) ? EvokerSpell.Summon : EvokerSpell.Fang;
			}
			if (flag)
			{
				return EvokerSpell.Summon;
			}
			if (flag2)
			{
				return EvokerSpell.Fang;
			}
			return EvokerSpell.None;
		}

		private void BeginAction(float now, EvokerSpell spell)
		{
			float num = ((_castSequence == 0) ? 0f : 1.5f);
			_currentSpell = spell;
			_phase = EvokerCastPhase.Gap;
			_gapEndsAt = now + num;
			_actionEndsAt = _gapEndsAt + 1f;
			Plugin.Debug($"[Evoker] 起手 {spell}:空档 {num:F2}s," + $"施法 {1f:F2}s,释放 {_actionEndsAt:F2}");
		}

		private void CancelAction(string reason)
		{
			if (_phase != EvokerCastPhase.None)
			{
				Plugin.Debug($"[Evoker] 取消施法({reason})阶段={_phase} 技能={_currentSpell}");
				ClearAction();
			}
		}

		private void ClearAction()
		{
			_phase = EvokerCastPhase.None;
			_currentSpell = EvokerSpell.None;
			_gapEndsAt = 0f;
			_actionEndsAt = 0f;
		}

		private bool GateOk(bool requireDistance, string phaseLabel)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Invalid comparison between Unknown and I4
			//IL_00b0: 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: Invalid comparison between Unknown and I4
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Invalid comparison between Unknown and I4
			//IL_00f4: 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)
			if ((Object)(object)_parent != (Object)null && !_parent.Spawned)
			{
				return false;
			}
			if ((Object)(object)_health != (Object)null && (_health.dead || _health.healthCurrent <= 0))
			{
				return false;
			}
			if ((Object)(object)_enemy != (Object)null && (int)_enemy.CurrentState == 11)
			{
				return false;
			}
			if (IsStunned())
			{
				return false;
			}
			State currentState = _bomb.currentState;
			if ((int)currentState != 5 && (int)currentState != 6)
			{
				return false;
			}
			if (!TryGetTarget(out var target))
			{
				return false;
			}
			if (requireDistance)
			{
				float num = Vector3.Distance(((Component)target).transform.position, ((Component)this).transform.position);
				float value = IllagerConfig.EvokerEngageRange.Value;
				if (num > value)
				{
					if (DebugOn)
					{
						Plugin.Debug($"[Evoker] {phaseLabel}:目标 {num:F2}m > 接敌范围 {value:F1}m");
					}
					return false;
				}
			}
			return true;
		}

		private bool TryGetTarget(out PlayerAvatar target)
		{
			target = (((Object)(object)_bomb != (Object)null) ? _bomb.playerTarget : null);
			if ((Object)(object)target == (Object)null || target.isDisabled)
			{
				target = null;
				return false;
			}
			if (target.deadSet)
			{
				target = null;
				return false;
			}
			return true;
		}

		private bool ReleaseSummon(float now)
		{
			if (!_summonAvailable)
			{
				WarnThrottled(ref _lastSummonWarnAt, "[Evoker] 召唤失败:VexSetup / VexPrefab 未配(技能已关闭)。");
				return false;
			}
			if (SummonPoints == null || SummonPoints.Length == 0)
			{
				WarnThrottled(ref _lastSummonWarnAt, "[Evoker] 召唤失败:SummonPoints 为空(G5 第 6 条)。");
				return false;
			}
			int num;
			try
			{
				num = EvokerSummons.SummonBatch(_parent, SummonPoints, 3, _spawnGeneration);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("[Evoker] 召唤抛异常,本次按失败处理(不消耗 CD):" + ex));
				}
				return false;
			}
			if (num <= 0)
			{
				WarnThrottled(ref _lastSummonWarnAt, $"[Evoker] 召唤失败:SummonBatch 返回 {num}(期望 {3})。" + "首次标记与 CD 都不消耗,会限频重试。");
				return false;
			}
			Plugin.Debug($"[Evoker] 召唤成功 {num}/{3} 只恼鬼(代次 {_spawnGeneration})");
			return true;
		}

		private bool ReleaseFang(float now, PlayerAvatar target)
		{
			//IL_0033: 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_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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: 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_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: 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_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			if (!_fangAvailable)
			{
				WarnThrottled(ref _lastFangWarnAt, "[Evoker] 地刺失败:FangPrefab 未配(技能已关闭)。");
				return false;
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(((Component)this).transform.position.x, 0f, ((Component)this).transform.position.z);
			Vector3 forward = ((Component)this).transform.forward;
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f)
			{
				forward = Vector3.forward;
			}
			else
			{
				((Vector3)(ref forward)).Normalize();
			}
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(((Component)target).transform.position.x, 0f, ((Component)target).transform.position.z);
			float baseYaw = YawOf(forward);
			float y;
			if (TryGetSurfaceBelowPoint(val2, ((Component)target).transform.position.y, out var ground))
			{
				y = ground.y;
			}
			else
			{
				y = ((Component)this).transform.position.y;
				if (DebugOn)
				{
					Plugin.Debug("[Evoker] 玩家脚下采样不到支撑面,基准层退回施法者 Y(本次落点可能仍落在施法者那一层)");
				}
			}
			List<EvokerFangShot> list = new List<EvokerFangShot>(10);
			int skipped = 0;
			_skipNoGround = 0;
			_skipBlocked = 0;
			_skipHeight = 0;
			float num = Vector3.Distance(val, val2);
			if (num <= 3f)
			{
				BuildCircle(val, baseYaw, y, list, ref skipped);
			}
			else
			{
				BuildLine(val, val2, forward, y, list, ref skipped);
			}
			if (skipped > 0)
			{
				string arg = $"{_skipNoGround} / 墙内 {_skipBlocked} / 高差 {_skipHeight}";
				Plugin.Debug($"[Evoker] 地刺批次:跳过 {skipped} 个候选点(无地面 {arg})" + ",保留原序号与延迟");
				WarnThrottled(ref _lastGroundWarnAt, $"[Evoker] 地刺批次有 {skipped} 个点没生成(无地面 {arg}),已跳过。" + $"高差那一档的阈值是 {2.5f:F1}m;" + "墙内那一档由 OccupancyRadius 控制。若整批都被跳过,检查 Scene 里地面层与采样搜索区间。");
			}
			if (list.Count == 0)
			{
				WarnThrottled(ref _lastFangWarnAt, "[Evoker] 地刺批次为空:全部候选点都没有有效地面,本次不生成(不消耗 CD)。");
				return false;
			}
			int casterViewId = (((Object)(object)_photonView != (Object)null) ? _photonView.ViewID : 0);
			int casterOwnerTag = PillagerNetwork.OwnerTagFromEnemyIndex(((Object)(object)_enemy != (Object)null) ? SemiFunc.EnemyGetIndex(_enemy) : (-1));
			bool flag;
			try
			{
				flag = EvokerNetwork.RaiseFangBatch(casterViewId, casterOwnerTag, _spawnGeneration, _castSequence + 1, list);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("[Evoker] 地刺批次广播抛异常,本次按失败处理(不消耗 CD):" + ex));
				}
				return false;
			}
			if (!flag)
			{
				WarnThrottled(ref _lastFangWarnAt, "[Evoker] 地刺批次未广播出去(网络未就绪 / 载荷非法):本批作废,不消耗 CD。");
				return false;
			}
			Plugin.Debug($"[Evoker] 地刺释放#{_castSequence + 1}:{list.Count} 根" + string.Format("({0},距离 {1:F2}m,", (num <= 3f) ? "圆阵" : "直线", num) + $"首延迟 {0.5f:F2}s)");
			return true;
		}

		private void BuildCircle(Vector3 center, float baseYaw, float groundLevelY, List<EvokerFangShot> shots, ref int skipped)
		{
			//IL_0004: 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)
			int startIndex = 0;
			startIndex = AppendRing(center, baseYaw, 6, 2f, 1f, startIndex, groundLevelY, shots, ref skipped);
			AppendRing(center, baseYaw, 10, 3f, -1f, startIndex, groundLevelY, shots, ref skipped);
		}

		private int AppendRing(Vector3 center, float baseYaw, int count, float radius, float direction, int startIndex, float groundLevelY, List<EvokerFangShot> shots, ref int skipped)
		{
			//IL_002f: 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_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_0046: Unknown result type (might be due to invalid IL or missing references)
			if (count <= 0)
			{
				return startIndex;
			}
			float num = 360f / (float)count;
			int num2 = startIndex;
			int num3 = 0;
			while (num3 < count)
			{
				float num4 = baseYaw + direction * num * (float)num3;
				Vector3 horizontalPoint = center + DirOfYaw(num4) * radius;
				TryAddFang(horizontalPoint, groundLevelY, num4, num2, shots, ref skipped);
				num3++;
				num2++;
			}
			return num2;
		}

		private void BuildLine(Vector3 casterHoriz, Vector3 targetHoriz, Vector3 forwardHoriz, float groundLevelY, List<EvokerFangShot> shots, ref int skipped)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_002a: 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)
			//IL_002c: 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_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = targetHoriz - casterHoriz;
			val.y = 0f;
			Vector3 val2;
			Vector3 val3;
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				val2 = forwardHoriz;
				val3 = casterHoriz + val2 * 4f;
			}
			else
			{
				val2 = ((Vector3)(ref val)).normalized;
				val3 = casterHoriz + val2 * 15f;
			}
			Vector3 val4 = casterHoriz + val2 * 1f;
			int num = 16;
			for (int i = 0; i < num; i++)
			{
				float num2 = ((num <= 1) ? 0f : ((float)i / (float)(num - 1)));
				Vector3 val5 = Vector3.Lerp(val4, val3, num2);
				Vector3 direction = val5 - casterHoriz;
				direction.y = 0f;
				float yaw = ((((Vector3)(ref direction)).sqrMagnitude < 0.0001f) ? YawOf(forwardHoriz) : YawOf(direction));
				TryAddFang(val5, groundLevelY, yaw, i, shots, ref skipped);
			}
		}

		private void TryAddFang(Vector3 horizontalPoint, float groundLevelY, float yaw, int index, List<EvokerFangShot> shots, ref int skipped)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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)
			if (!SampleGround(horizontalPoint, groundLevelY, out var ground, out var miss))
			{
				skipped++;
				switch (miss)
				{
				case GroundMiss.Blocked:
					_skipBlocked++;
					break;
				case GroundMiss.HeightTolerance:
					_skipHeight++;
					break;
				default:
					_skipNoGround++;
					break;
				}
			}
			else
			{
				shots.Add(new EvokerFangShot
				{
					Delay = 0.5f + (float)index * 0.05f,
					Position = ground,
					Yaw = yaw
				});
			}
		}

		private int GatherSupportCandidates(Vector3 horizontalPoint, float rayBaseY, out int blockedFiltered)
		{
			//IL_0006: 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_0024: 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)
			//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_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: 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_00c1: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			blockedFiltered = 0;
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(horizontalPoint.x, rayBaseY + 1.5f, horizontalPoint.z);
			float num = 4.5f;
			int num2 = Physics.RaycastNonAlloc(val, Vector3.down, _groundBuffer, num, LevelMask, (QueryTriggerInteraction)1);
			if (num2 <= 0)
			{
				return 0;
			}
			if (num2 >= _groundBuffer.Length)
			{
				WarnThrottled(ref _lastGroundWarnAt, $"[Evoker] 地面采样命中缓冲已满({num2}),该点跳过。房间结构过于密集时会看到这条日志。");
				return 0;
			}
			for (int i = 1; i < num2; i++)
			{
				RaycastHit val2 = _groundBuffer[i];
				int num3 = i - 1;
				while (num3 >= 0 && ((RaycastHit)(ref _groundBuffer[num3])).distance > ((RaycastHit)(ref val2)).distance)
				{
					_groundBuffer[num3 + 1] = _groundBuffer[num3];
					num3--;
				}
				_groundBuffer[num3 + 1] = val2;
			}
			int result = 0;
			for (int j = 0; j < num2; j++)
			{
				RaycastHit val3 = _groundBuffer[j];
				if (!((Object)(object)((RaycastHit)(ref val3)).collider == (Object)null) && !(((RaycastHit)(ref val3)).normal.y < 0.5f) && !IsMovableEntityCollider(((RaycastHit)(ref val3)).collider))
				{
					if (IsBlockedAt(((RaycastHit)(ref val3)).point, ((RaycastHit)(ref val3)).collider))
					{
						blockedFiltered++;
					}
					else
					{
						_groundBuffer[result++] = val3;
					}
				}
			}
			return result;
		}

		private bool TryGetSurfaceBelowPoint(Vector3 horizontalPoint, float rayBaseY, out Vector3 ground)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			ground = default(Vector3);
			int blockedFiltered;
			int num = GatherSupportCandidates(horizontalPoint, rayBaseY, out blockedFiltered);
			if (num <= 0)
			{
				return false;
			}
			float num2 = rayBaseY + 0.05f;
			for (int i = 0; i < num; i++)
			{
				if (!(((RaycastHit)(ref _groundBuffer[i])).point.y > num2))
				{
					ground = ((RaycastHit)(ref _groundBuffer[i])).point;
					return true;
				}
			}
			return false;
		}

		private bool SampleGround(Vector3 horizontalPoint, float groundLevelY, out Vector3 ground, out GroundMiss miss)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			ground = default(Vector3);
			miss = GroundMiss.None;
			int blockedFiltered;
			int num = GatherSupportCandidates(horizontalPoint, groundLevelY, out blockedFiltered);
			if (num <= 0)
			{
				miss = ((blockedFiltered <= 0) ? GroundMiss.NoGround : GroundMiss.Blocked);
				return false;
			}
			int num2 = -1;
			float num3 = float.MaxValue;
			for (int i = 0; i < num; i++)
			{
				float num4 = Mathf.Abs(((RaycastHit)(ref _groundBuffer[i])).point.y - groundLevelY);
				if (num4 < num3)
				{
					num3 = num4;
					num2 = i;
				}
			}
			if (num2 < 0)
			{
				miss = GroundMiss.NoGround;
				return false;
			}
			if (num3 >= 2.5f)
			{
				miss = GroundMiss.HeightTolerance;
				return false;
			}
			ground = ((RaycastHit)(ref _groundBuffer[num2])).point;
			return true;
		}

		private bool IsBlockedAt(Vector3 ground, Collider support)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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)
			Vector3 val = ground + Vector3.up * 0.5f;
			int num = Physics.OverlapSphereNonAlloc(val, 0.15f, _overlapBuffer, LevelMask, (QueryTriggerInteraction)1);
			if (num <= 0)
			{
				return false;
			}
			if (num >= _overlapBuffer.Length)
			{
				return true;
			}
			for (int i = 0; i < num; i++)
			{
				Collider val2 = _overlapBuffer[i];
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)support) && !IsMovableEntityCollider(val2))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsMovableEntityCollider(Collider c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return true;
			}
			if (((Component)c).gameObject.layer == PlayerLayer)
			{
				return true;
			}
			if ((Object)(object)((Component)c).GetComponentInParent<PlayerAvatar>() != (Object)null || (Object)(object)((Component)c).GetComponentInParent<PlayerHealth>() != (Object)null)
			{
				return true;
			}
			if ((Object)(object)((Component)c).GetComponentInParent<EnemyParent>() != (Object)null)
			{
				return true;
			}
			if ((Object)(object)((Component)c).GetComponentInParent<PhysGrabObject>() != (Object)null)
			{
				return true;
			}
			return false;
		}

		private static Vector3 DirOfYaw(float yawDegrees)
		{
			//IL_001a: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			float num = yawDegrees * (MathF.PI / 180f);
			return new Vector3(Mathf.Sin(num), 0f, Mathf.Cos(num));
		}

		private static float YawOf(Vector3 direction)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			return Mathf.Atan2(direction.x, direction.z) * 57.29578f;
		}

		private void UpdateVisualState()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			EvokerCastPhase displayPhase = DisplayPhase;
			EvokerVisualState evokerVisualState;
			if (displayPhase == EvokerCastPhase.Casting)
			{
				evokerVisualState = EvokerVisualState.Cast;
				goto IL_0079;
			}
			int num;
			if ((Object)(object)_navMesh != (Object)null && (Object)(object)_navMesh.Agent != (Object)null && ((Behaviour)_navMesh.Agent).enabled)
			{
				Vector3 velocity = _navMesh.Agent.velocity;
				if (((Vector3)(ref velocity)).sqrMagnitude > 0.010000001f)
				{
					num = 1;
					goto IL_0077;
				}
			}
			num = 0;
			goto IL_0077;
			IL_0077:
			evokerVisualState = (EvokerVisualState)num;
			goto IL_0079;
			IL_0079:
			if (evokerVisualState != _appliedVisual)
			{
				EvokerVisualState appliedVisual = _appliedVisual;
				_appliedVisual = evokerVisualState;
				Visuals?.ApplyState(evokerVisualState, (evokerVisualState == EvokerVisualState.Cast) ? CastNormalizedTime : 0f);
				if (evokerVisualState == EvokerVisualState.Cast)
				{
					Visuals?.ShowCastParticles(DisplaySpell);
				}
				else if (appliedVisual == EvokerVisualState.Cast)
				{
					Visuals?.HideCastParticles();
				}
			}
		}

		private void UpdateIdleVoice(float dt)
		{
			if ((Object)(object)_health != (Object)null && (_health.dead || _health.healthCurrent <= 0))
			{
				_idleVoiceTimer = 0f;
				return;
			}
			if ((Object)(object)_parent != (Object)null && !_parent.Spawned)
			{
				_idleVoiceTimer = 0f;
				return;
			}
			if (IsStunned())
			{
				_idleVoiceTimer = 0f;
				return;
			}
			if (DisplayPhase != EvokerCastPhase.None)
			{
				_idleVoiceTimer = Random.Range(4f, 7f);
				return;
			}
			_idleVoiceTimer -= dt;
			if (!(_idleVoiceTimer > 0f))
			{
				_idleVoiceTimer = Random.Range(4f, 7f);
				Visuals?.PlayVoice(EvokerVoice.Idle);
			}
		}

		private void UpdateDespawn()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			if (!_isAuthority || (Object)(object)_enemy == (Object)null)
			{
				return;
			}
			if ((int)_enemy.CurrentState != 11)
			{
				if (_despawning)
				{
					_despawning = false;
					_despawnTimer = 0f;
				}
				return;
			}
			EnemyParent val = (((Object)(object)_enemy.EnemyParent != (Object)null) ? _enemy.EnemyParent : _parent);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if (!_despawning)
			{
				_despawning = true;
				_despawnTimer = 1.17f;
				return;
			}
			_despawnTimer -= Time.deltaTime;
			if (!(_despawnTimer > 0f))
			{
				_despawning = false;
				if (val.Spawned && (!((Object)(object)_health != (Object)null) || _health.healthCurrent > 0))
				{
					Plugin.Debug("[Evoker] 到点补一次 EnemyParent.Despawn()(原版 Animator 已关,EventDespawn 不会触发)");
					val.Despawn();
				}
			}
		}

		private void EnsureMaxHealth()
		{
			int value = IllagerConfig.EvokerHealth.Value;
			if (!((Object)(object)_health == (Object)null) && _health.health != value)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)($"[Evoker] EnemyHealth.health {_health.health} → {value}" + "(来源:配置项 EvokerHealth)"));
				}
				_health.health = value;
			}
		}

		private void SyncHealthToClients()
		{
			if (!((Object)(object)_photonView == (Object)null) && SemiFunc.IsMultiplayer() && !((Object)(object)_health == (Object)null) && (_lastHealthMax != _health.health || _lastHealthCurrent != _health.healthCurrent))
			{
				_lastHealthMax = _health.health;
				_lastHealthCurrent = _health.healthCurrent;
				_photonView.RPC("SyncHealthRPC", (RpcTarget)1, new object[2] { _health.health, _health.healthCurrent });
			}
		}

		[PunRPC]
		private void SyncHealthRPC(int maxHealth, int currentHealth, PhotonMessageInfo info = default(PhotonMessageInfo))
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			if (SemiFunc.MasterOnlyRPC(info) && !((Object)(object)_health == (Object)null))
			{
				_health.health = maxHealth;
				_health.healthCurrent = currentHealth;
			}
		}

		[PunRPC]
		private void RequestSyncRPC(PhotonMessageInfo info = default(PhotonMessageInfo))
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				_lastHealthMax = -1;
				_lastHealthCurrent = -1;
				_lastSyncedCastPhase = (EvokerCastPhase)(-1);
				Plugin.Debug("[Evoker] 收到快照请求,重置广播标记");
			}
		}

		private void SyncCastPhaseToClients()
		{
			if (!((Object)(object)_photonView == (Object)null) && SemiFunc.IsMultiplayer() && _lastSyncedCastPhase != _phase)
			{
				_lastSyncedCastPhase = _phase;
				float num = Mathf.Max(0f, _actionEndsAt - Time.time);
				_photonView.RPC("CastPhaseRPC", (RpcTarget)1, new object[3]
				{
					(int)_phase,
					(int)_currentSpell,
					num
				});
			}
		}

		[PunRPC]
		private void CastPhaseRPC(int phase, int spell, float remainingSeconds, PhotonMessageInfo info = default(PhotonMessageInfo))
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			if (SemiFunc.MasterOnlyRPC(info) && !_isAuthority)
			{
				bool flag = phase == 2 && _netPhase != EvokerCastPhase.Casting;
				_netPhase = (EvokerCastPhase)phase;
				_netSpell = (EvokerSpell)spell;
				_netActionEndsAt = Time.time + Mathf.Max(0f, remainingSeconds);
				if (flag)
				{
					Visuals?.PlayVoice(EvokerVoice.Cast);
				}
				Plugin.Debug($"[Evoker] 收到施法阶段 {(EvokerCastPhase)phase} / 技能 {_netSpell}(剩余 {remainingSeconds:F2}s),客户端只演出");
			}
		}

		private void BindHealthEvents()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			if (!_healthEventsBound && !((Object)(object)_health == (Object)null))
			{
				_healthEventsBound = true;
				EnemyHealth health = _health;
				if (health.onHurt == null)
				{
					health.onHurt = new UnityEvent();
				}
				_health.onHurt.AddListener(new UnityAction(OnHurtCallback));
				health = _health;
				if (health.onDeath == null)
				{
					health.onDeath = new UnityEvent();
				}
				_health.onDeath.AddListener(new UnityAction(OnDeathCallback));
			}
		}

		private void OnHurtCallback()
		{
			Visuals?.PlayVoice(EvokerVoice.Hurt);
			Visuals?.FlashHurt();
		}

		private void OnDeathCallback()
		{
			Visuals?.PlayVoice(EvokerVoice.Death);
			if (!SemiFunc.IsMasterClientOrSingleplayer() || _deathDropDone)
			{
				return;
			}
			_deathDropDone = true;
			if ((Object)(object)TotemPrefab == (Object)null)
			{
				return;
			}
			try
			{
				bool flag = EvokerSummons.TryDropTotem(_parent);
				Plugin.Debug($"[Evoker] 死亡掉落掷签(D1 {IllagerConfig.TotemDropChance.Value:P0}):" + (flag ? "生成图腾" : "未掉落"));
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("[Evoker] 图腾掉落异常(不影响死亡流程):" + ex));
				}
			}
		}

		private void VerifyVisionDots()
		{
			EnemyVision component = ((Component)this).GetComponent<EnemyVision>();
			if ((Object)(object)component == (Object)null)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"[Evoker] 找不到 EnemyVision:无法核对视野配置(G2)。");
				}
			}
			else if (component.VisionDotStanding < 0f || component.VisionDotCrouch < 0f || component.VisionDotCrawl < 0f || component.PhysObjectVisionDot < 0f)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("[Evoker] 视野 Dot 仍是负数(全向感知)—— prefab `Enemy - Evoker` 的 `Controller > Enemy Vision` 没配(基线 Bomb 是四个 -1)。G2 期望 Standing/Crouch/Crawl = 0.4/0.6/0.9、PhysObjectVisionDot = 0.4;当前 = " + $"{component.VisionDotStanding:F2}/{component.VisionDotCrouch:F2}/" + $"{component.VisionDotCrawl:F2}/{component.PhysObjectVisionDot:F2}。"));
				}
			}
			else
			{
				Plugin.Debug($"[Evoker] 视野 Dot(来自 prefab):{component.VisionDotStanding:F2}/" + $"{component.VisionDotCrouch:F2}/{component.VisionDotCrawl:F2},物理对象 = {component.PhysObjectVisionDot:F2}");
			}
		}

		private void DisableLegacyHurtCollider()
		{
			Transform val = (((Object)(object)_parent != (Object)null) ? ((Component)_parent).transform : ((Component)this).transform);
			HurtCollider[] componentsInChildren = ((Component)val).GetComponentsInChildren<HurtCollider>(true);
			int num = 0;
			foreach (HurtCollider val2 in componentsInChildren)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					((Behaviour)val2).enabled = false;
					((Component)val2).gameObject.SetActive(false);
					num++;
					Collider[] components = ((Component)val2).GetComponents<Collider>();
					for (int j = 0; j < components.Length; j++)
					{
						components[j].enabled = false;
					}
				}
			}
			if (num == 0)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"[Evoker] 找不到任何 HurtCollider(预期是 Enable/Visuals 下的 Hurt Collider 节点):G1 第 4 条要求它 inactive 且组件 / 专用 Collider 都取消 enabled,GUI 里请核对。");
				}
			}
			else
			{
				Plugin.Debug($"[Evoker] 已关闭 {num} 个原版 HurtCollider(含同一节点上的专用 Collider)。");
			}
		}

		private void MuteLegacySounds()
		{
			EnemyBombThrowerAnim val = (((Object)(object)_bomb != (Object)null) ? _bomb.anim : null);
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)this).GetComponentInChildren<EnemyBombThrowerAnim>(true);
			}
			if ((Object)(object)val == (Object)null)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"[Evoker] 找不到 EnemyBombThrowerAnim:无法静音原版炸弹音(T1)。");
				}
				return;
			}
			FieldInfo[] fields = typeof(EnemyBombThrowerAnim).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			int num = MuteSoundFields(val, fields);
			EnemyBombThrowerHead val2 = (((Object)(object)_bomb != (Object)null) ? _bomb.head : null);
			if ((Object)(object)val2 != (Object)null)
			{
				num += MuteSoundFields(val2, typeof(EnemyBombThrowerHead).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
			}
			if (num > 0)
			{
				Plugin.Debug($"[Evoker] 已静音 {num} 条原版炸弹音(清空 Sound.Sounds)。");
			}
		}

		private static int MuteSoundFields(object target, FieldInfo[] fields)
		{
			if (target == null || fields == null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < fields.Length; i++)
			{
				if (!(fields[i].FieldType != typeof(Sound)))
				{
					object? value = fields[i].GetValue(target);
					Sound val = (Sound)((value is Sound) ? value : null);
					if (val != null && (val.Sounds == null || val.Sounds.Length != 0))
					{
						val.Sounds = (AudioClip[])(object)new AudioClip[0];
						num++;
					}
				}
			}
			return num;
		}

		private void SuppressLegacyParticles()
		{
			EnemyBombThrowerAnim val = (((Object)(object)_bomb != (Object)null) ? _bomb.anim : null);
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)this).GetComponentInChildren<EnemyBombThrowerAnim>(true);
			}
			if (!((Object)(object)val == (Object)null))
			{
				int num = 0;
				num += StopAll(val.teleportParticles);
				num += StopAll(val.headDetachParticles);
				num += StopAll(val.deathParticles);
				val.teleportParticles = (ParticleSystem[])(object)new ParticleSystem[0];
				val.headDetachParticles = (ParticleSystem[])(object)new ParticleSystem[0];
				val.deathParticles = (ParticleSystem[])(object)new ParticleSystem[0];
				if (num > 0)
				{
					Plugin.Debug($"[Evoker] 已停掉 {num} 个原版炸弹粒子并清空 anim 的粒子数组。");
				}
			}
		}

		private static int StopAll(ParticleSystem[] systems)
		{
			if (systems == null)
			{
				return 0;
			}
			int num = 0;
			foreach (ParticleSystem val in systems)
			{
				if (!((Object)(object)val == (Object)null))
				{
					val.Stop(true, (ParticleSystemStopBehavior)0);
					((Component)val).gameObject.SetActive(false);
					num++;
				}
			}
			return num;
		}

		private void ResetLegacyHead()
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Invalid comparison between Unknown and I4
			if (!((Object)(object)_bomb == (Object)null))
			{
				if (!_bomb.headGrown)
				{
					_bomb.headGrown = true;
				}
				if ((Object)(object)_bomb.headCollider != (Object)null && !((Component)_bomb.headCollider).gameObject.activeSelf)
				{
					((Component)_bomb.headCollider).gameObject.SetActive(true);
				}
				EnemyBombThrowerHead head = _bomb.head;
				if ((Object)(object)head != (Object)null && (int)head.currentState > 0)
				{
					head.UpdateState((State)0);
				}
			}
		}

		private bool IsStunned()
		{
			if ((Object)(object)_enemy == (Object)null)
			{
				return false;
			}
			if (_enemy.IsStunned())
			{
				return true;
			}
			return (Object)(object)_stunState != (Object)null && _stunState.stunTimer > 0f;
		}

		private void WarnThrottled(ref float lastAt, string message)
		{
			if (!(Time.time - lastAt < 10f))
			{
				lastAt = Time.time;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)message);
				}
			}
		}
	}
	internal static class EvokerBombThrowerPatches
	{
	}
	[HarmonyPatch(typeof(EnemyBombThrower), "MeleeSetLogic")]
	internal static class PatchEvokerNoMelee
	{
		private static bool Prefix(EnemyBombThrower __instance, ref bool __result)
		{
			if ((Object)(object)EvokerBehaviour.FindOn(__instance) == (Object)null)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrower), "AttackSetLogic")]
	internal static class PatchEvokerNoHeadAttack
	{
		private static bool Prefix(EnemyBombThrower __instance, ref bool __result)
		{
			if ((Object)(object)EvokerBehaviour.FindOn(__instance) == (Object)null)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrower), "HeadLogic")]
	internal static class PatchEvokerNoHeadLogic
	{
		private static bool Prefix(EnemyBombThrower __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOn(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerHead), "UpdateState")]
	internal static class PatchEvokerNoHeadDetach
	{
		private static bool Prefix(EnemyBombThrowerHead __instance, State _state)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			if ((Object)(object)EvokerBehaviour.FindOnHead(__instance) == (Object)null)
			{
				return true;
			}
			return (int)_state == 0;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerHead), "OnHurtColliderHit")]
	internal static class PatchEvokerNoHeadImpact
	{
		private static bool Prefix(EnemyBombThrowerHead __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnHead(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrower), "TorsoHeadBreakerLogic")]
	internal static class PatchEvokerNoTorsoHeadBreaker
	{
		private static bool Prefix(EnemyBombThrower __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOn(__instance) == (Object)null;
		}
	}
	internal static class EvokerCastHold
	{
		internal static void Apply(EnemyBombThrower bomb)
		{
			try
			{
				EvokerBehaviour evokerBehaviour = EvokerBehaviour.FindOn(bomb);
				if (!((Object)(object)evokerBehaviour == (Object)null) && evokerBehaviour.CastInFlight)
				{
					Enemy val = (((Object)(object)bomb != (Object)null) ? bomb.enemy : null);
					EnemyNavMeshAgent val2 = (((Object)(object)val != (Object)null && (Object)(object)val.NavMeshAgent != (Object)null) ? val.NavMeshAgent : ((Component)bomb).GetComponent<EnemyNavMeshAgent>());
					if (!((Object)(object)val2 == (Object)null))
					{
						val2.Stop(Mathf.Max(0.05f, evokerBehaviour.CastRemainingSeconds) + 0.5f);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("[Evoker] 施法站定异常(本帧不清导航):" + ex));
				}
			}
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrower), "StateGotoPlayer")]
	internal static class PatchEvokerCastHoldGoto
	{
		private static bool Prefix(EnemyBombThrower __instance)
		{
			EvokerCastHold.Apply(__instance);
			return true;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrower), "StateBackAwayPlayer")]
	internal static class PatchEvokerCastHoldBackAway
	{
		private static bool Prefix(EnemyBombThrower __instance)
		{
			EvokerCastHold.Apply(__instance);
			return true;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrower), "BackAwayLogic")]
	internal static class PatchEvokerHoldStillWhileCasting
	{
		private static bool Prefix(EnemyBombThrower __instance)
		{
			EvokerBehaviour evokerBehaviour = EvokerBehaviour.FindOn(__instance);
			return (Object)(object)evokerBehaviour == (Object)null || !evokerBehaviour.CastInFlight;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventAttack")]
	internal static class PatchEvokerNoEventAttack
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventDeath")]
	internal static class PatchEvokerNoEventDeath
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventHurt")]
	internal static class PatchEvokerNoEventHurt
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventMelee")]
	internal static class PatchEvokerNoEventMelee
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventMeleeEnd")]
	internal static class PatchEvokerNoEventMeleeEnd
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventLand")]
	internal static class PatchEvokerNoEventLand
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventJump")]
	internal static class PatchEvokerNoEventJump
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventTorsoHeadGrow01")]
	internal static class PatchEvokerNoEventTorsoHeadGrow01
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventTorsoHeadGrow02")]
	internal static class PatchEvokerNoEventTorsoHeadGrow02
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventTorsoHeadGrow03")]
	internal static class PatchEvokerNoEventTorsoHeadGrow03
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventTorsoHeadsScream")]
	internal static class PatchEvokerNoEventTorsoHeadsScream
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventTorsoHeadsScreamTrigger")]
	internal static class PatchEvokerNoEventTorsoHeadsScreamTrigger
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventTorsoHeadsMeleeTrigger")]
	internal static class PatchEvokerNoEventTorsoHeadsMeleeTrigger
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventTorsoHeadsBreakerShake")]
	internal static class PatchEvokerNoEventTorsoHeadsBreakerShake
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventFootstepSkinLight")]
	internal static class PatchEvokerNoEventFootstepSkinLight
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventFootstepSkinHeavy")]
	internal static class PatchEvokerNoEventFootstepSkinHeavy
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventFootstepBootLight")]
	internal static class PatchEvokerNoEventFootstepBootLight
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventFootstepBootHeavy")]
	internal static class PatchEvokerNoEventFootstepBootHeavy
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventTeleportIn")]
	internal static class PatchEvokerNoEventTeleportIn
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrowerAnim), "EventTeleportOut")]
	internal static class PatchEvokerNoEventTeleportOut
	{
		private static bool Prefix(EnemyBombThrowerAnim __instance)
		{
			return (Object)(object)EvokerBehaviour.FindOnAnim(__instance) == (Object)null;
		}
	}
	[HarmonyPatch(typeof(EnemyBombThrower), "StateNotice")]
	internal static class PatchEvokerShortNotice
	{
		private const float NoticeSeconds = 1f;

		private static void Postfix(EnemyBombThrower __instance)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			if ((int)__instance.currentState == 4 && !((Object)(object)EvokerBehaviour.FindOn(__instance) == (Object)null))
			{
				FieldRef<EnemyBombThrower, float> val = BombThrowerStateTimer.Get();
				if (val != null && val.Invoke(__instance) > 1f)
				{
					val.Invoke(__instance) = 1f;
				}
			}
		}
	}
	internal static class BombThrowerStateTimer
	{
		private static FieldRef<EnemyBombThrower, float> _ref;

		private static bool _failed;

		internal static FieldRef<EnemyBombThrower, float> Get()
		{
			if (_failed)
			{
				return null;
			}
			if (_ref != null)
			{
				return _ref;
			}
			try
			{
				_ref = AccessTools.FieldRefAccess<EnemyBombThrower, float>("stateTimer");
			}
			catch (Exception ex)
			{
				_failed = true;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)("[Evoker] 找不到 EnemyBombThrower.stateTimer,相关补丁将保持原版行为:" + ex.Message));
				}
			}
			return _ref;
		}
	}
	public enum EvokerCastPhase
	{
		None,
		Gap,
		Casting
	}
	public enum EvokerSpell
	{
		None,
		Summon,
		Fang
	}
	public enum EvokerVisualState
	{
		Stand,
		Walk,
		Cast
	}
	public enum EvokerVoice
	{
		Idle,
		Cast,
		Hurt,
		Death
	}
	public static class EvokerTuning
	{
		public const int MaxHealth = 250;

		public const float EngageRange = 10f;

		public const int SummonCount = 3;

		public const float SummonCooldown = 20f;

		public const float FangCooldown = 5f;

		public const float PreCastGap = 1.5f;

		public const float CastDuration = 1f;

		public const float NearFormationRange = 3f;

		public const int InnerRingCount = 6;

		public const int OuterRingCount = 10;

		public const float OuterRingRadius = 3f;

		public const float InnerRingRadius = 2f;

		public const int LineFangCount = 16;

		public const float FarReachMeters = 15f;

		public const float FangGroundHeightTolerance = 2.5f;

		public const float FangDelay = 0.5f;

		public const float FangInterval = 0.05f;

		public const int FangDamage = 50;

		public const int FangEnemyDamage = 50;

		public const float FangHitCooldownSeconds = 0.2f;

		public const float FangRiseSeconds = 0.25f;

		public const float FangHoldSeconds = 0.4f;

		public const float FangRetractSeconds = 0.3f;

		public const float FangHitVolumeYOffset = 0.5f;

		public const float TotemDropChance = 0.2f;

		public const float TotemRangeMultiplier = 5f;

		public const float TotemReviveSeconds = 2.5f;

		private static readonly float[] TotemHealPercentByMoon = new float[5] { 0.5f, 0.45f, 0.4f, 0.3f, 0.25f };

		private static readonly string[] TotemHealMoonNames = new string[5] { "无月亮", "新月", "半月", "满月", "超级月亮" };

		public const string AnimStateStand = "Stand";

		public const string AnimStateWalk = "Walk";

		public const string AnimStateCast = "Cast";

		private static int ClampMoonLevel(int moonLevel)
		{
			if (moonLevel < 0)
			{
				return 0;
			}
			if (moonLevel >= TotemHealPercentByMoon.Length)
			{
				return TotemHealPercentByMoon.Length - 1;
			}
			return moonLevel;
		}

		public static float TotemHealPercentForMoon(int moonLevel)
		{
			return TotemHealPercentByMoon[ClampMoonLevel(moonLevel)];
		}

		public static string TotemHealMoonName(int moonLevel)
		{
			return TotemHealMoonNames[ClampMoonLevel(moonLevel)];
		}

		public static string TotemHealPreview()
		{
			string text = "";
			for (int i = 0; i < TotemHealPercentByMoon.Length; i++)
			{
				if (i > 0)
				{
					text += " / ";
				}
				text = text + TotemHealMoonNames[i] + " " + TotemHealPercentByMoon[i].ToString("P0");
			}
			return text;
		}

		public static string Preview()
		{
			int value = IllagerConfig.EvokerHealth.Value;
			float value2 = IllagerConfig.EvokerEngageRange.Value;
			float value3 = IllagerConfig.EvokerSummonCooldown.Value;
			float value4 = IllagerConfig.EvokerFangCooldown.Value;
			int value5 = IllagerConfig.EvokerFangDamage.Value;
			float value6 = IllagerConfig.TotemDropChance.Value;
			float value7 = IllagerConfig.TotemRangeMultiplier.Value;
			return $"血量={value},接敌≤{value2:F1}m" + $",节拍={1.5f:F1}s 空档 + {1f:F1}s 施法(CD:召唤 {value3:F0}s / 地刺 {value4:F0}s)" + $",召唤={3}只" + $",地刺={value5}(对玩家与对怪同源;延迟 {0.5f:F1}s + {0.05f:F2}s×n,受击跨根冷却 {0.2f:F1}s" + $";近圆阵 R{2f:F1}/{3f:F1} {6}+{10} 根" + $" / 远直线 {16} 根 {15f:F0}m,分界 {3f:F1}m)" + $",图腾掉落={value6:P0}(范围×{value7:F0},复活充能 {2.5f:F1}s)" + ",回血=按关卡月亮(" + TotemHealPreview() + ")" + $",协议序(Phase/Spell)={0}/{1}/{2}" + $" · {0}/{1}/{2}";
		}
	}
	public static class EvokerEventNames
	{
		public const string FangBatchEventName = "Illagers.Evoker.FangBatch";
	}
	public static class EvokerFangPayload
	{
		public const int HeaderCount = 6;

		public const int FieldsPerFang = 5;

		public const int IdxLevelGeneration = 0;

		public const int IdxCasterViewId = 1;

		public const int IdxCasterOwnerTag = 2;

		public const int IdxSpawnGeneration = 3;

		public const int IdxCastSequence = 4;

		public const int IdxFangCount = 5;

		public const int FangDelay = 0;

		public const int FangX = 1;

		public const int FangY = 2;

		public const int FangZ = 3;

		public const int FangYaw = 4;

		public static int ExpectedLength(int fangCount)
		{
			return 6 + fangCount * 5;
		}
	}
	public sealed class EvokerFang : MonoBehaviour
	{
		[Header("表现(G5 第 2/3 条)")]
		[Tooltip("模型实例的根。以模型地面基准建立,+Y 向上;闭合/埋地状态为初始姿态。")]
		public Transform VisualRoot;

		[Tooltip("伤害体积(专用 Trigger / 几何描述)。设 Trigger,不产生物理推挤。有效窗口与显示高度对齐,见 EvokerTuning.FangHitVolumeYOffset。")]
		public BoxCollider HitVolume;

		[Header("音效(G5 第 5 条:地刺声音挂自身,不依赖施法者存活)")]
		public AudioSource AudioSource;

		[Tooltip("entity.evoker_fangs.attack.fangs。留空则静默,不报错。")]
		public AudioClip AttackClip;

		[Header("粒子(破土扬起)")]
		[Tooltip("地刺「破土升起」时播放的粒子。**必须关掉 Play On Awake** —— 生命周期由本文件按时间线显式启停(升起段 Play、收回段停发射),自动播会和协程抢时机。留空则静默跳过,不报错。注意:这是地刺自己的资产,与唤魔者的施法粒子是**不同的**两份资产,不要复用同一个引用。")]
		public ParticleSystem RiseParticles;

		private const float DefaultRiseHeight = 1f;

		private static readonly Vector3 DefaultHitHalfExtents = new Vector3(0.5f, 0.5f, 0.5f);

		private static int _targetMask = int.MinValue;

		private static int _playerLayer = int.MinValue;

		private bool _configured;

		private bool _authoritative;

		private int _index;

		private int _enemyIndex;

		private float _delay;

		private Vector3 _buriedLocal;

		private Vector3 _risenLocal;

		private bool _posesCaptured;

		private readonly HashSet<Component> _hitTargets = new HashSet<Component>();

		private static readonly Dictionary<Component, float> _lastHitAt = new Dictionary<Component, float>();

		private const float LastHitMemorySeconds = 5f;

		private const int LastHitPruneThreshold = 16;

		private bool _warnedSampleError;

		private static int TargetMask
		{
			get
			{
				//IL_0012: 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)
				if (_targetMask == int.MinValue)
				{
					LayerMask val = SemiFunc.LayerMaskGetPlayersAndPhysObjects();
					int num = ((LayerMask)(ref val)).value;
					if (num == 0)
					{
						num = LayerMask.GetMask(new string[1] { "Player" });
					}
					int mask = LayerMask.GetMask(new string[1] { "Enemy" });
					if (mask != 0)
					{
						num |= mask;
					}
					_targetMask = num;
				}
				return _targetMask;
			}
		}

		private static int PlayerLayer
		{
			get
			{
				if (_playerLayer == int.MinValue)
				{
					_playerLayer = LayerMask.NameToLayer("Player");
				}
				return _playerLayer;
			}
		}

		internal void Configure(Vector3 groundPosition, float yaw, int index, float delay, bool authoritative, int enemyIndex)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: 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_00a6: 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)
			if (!_configured)
			{
				_configured = true;
				_index = index;
				_delay = Mathf.Max(0f, delay);
				_authoritative = authoritative;
				_enemyIndex = enemyIndex;
				((Component)this).transform.position = groundPosition;
				((Component)this).transform.rotation = Quaternion.Euler(0f, yaw, 0f);
				CapturePoses();
				ApplyLocalPose(_buriedLocal);
				Plugin.Debug($"[Evoker] 地刺#{_index} 生成于 ({groundPosition.x:F2},{groundPosition.y:F2},{groundPosition.z:F2})" + $",延迟 {_delay:F2}s,权威={_authoritative},升程 {_risenLocal.y - _buriedLocal.y:F2}m。");
				((MonoBehaviour)this).StartCoroutine(Lifecycle());
			}
		}

		private IEnumerator Lifecycle()
		{
			if (_delay > 0f)
			{
				yield return (object)new WaitForSeconds(_delay);
			}
			PlayAttackSound();
			PlayRiseParticles();
			yield return MovePose(_buriedLocal, _risenLocal, 0.25f);
			yield return HoldWindow();
			StopRiseParticles();
			yield return MovePose(_risenLocal, _buriedLocal, 0.3f);
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}

		private IEnumerator MovePose(Vector3 from, Vector3 to, float duration)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: 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)
			if ((Object)(object)VisualRoot == (Object)null)
			{
				if (duration > 0f)
				{
					yield return (object)new WaitForSeconds(duration);
				}
				yield break;
			}
			if (duration <= 0f)
			{
				ApplyLocalPose(to);
				yield break;
			}
			float elapsed = 0f;
			while (elapsed < duration)
			{
				elapsed += Time.deltaTime;
				ApplyLocalPose(Vector3.Lerp(from, to, Mathf.Clamp01(elapsed / duration)));
				yield return null;
			}
			ApplyLocalPose(to);
		}

		private IEnumerator HoldWindow()
		{
			float elapsed = 0f;
			SampleAndDamage();
			while (elapsed < 0.4f)
			{
				elapsed += Time.deltaTime;
				if (elapsed < 0.4f)
				{
					SampleAndDamage();
				}
				yield return null;
			}
		}

		private void ApplyLocalPose(Vector3 localPosition)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)VisualRoot == (Object)null))
			{
				VisualRoot.localPosition = localPosition;
			}
		}

		private void PlayAttackSound()
		{
			if (!((Object)(object)AudioSource == (Object)null) && !((Object)(object)AttackClip == (Object)null))
			{
				AudioSource.PlayOneShot(AttackClip);
			}
		}

		private void PlayRiseParticles()
		{
			if ((Object)(object)RiseParticles == (Object)null)
			{
				return;
			}
			try
			{
				RiseParticles.Play(true);
			}
			catch (Exception ex)
			{
				Plugin.Debug($"[Evoker] 地刺#{_index} 破土粒子播放失败(时间线不受影响):{ex.Message}");
			}
		}

		private void StopRiseParticles()
		{
			if ((Object)(object)RiseParticles == (Object)null)
			{
				return;
			}
			try
			{
				RiseParticles.Stop(true, (ParticleSystemStopBehavior)1);
			}
			catch (Exception ex)
			{
				Plugin.Debug($"[Evoker] 地刺#{_index} 破土粒子停止失败(时间线不受影响):{ex.Message}");
			}
		}

		private void CapturePoses()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			if (_posesCaptured)
			{
				return;
			}
			_posesCaptured = true;
			if ((Object)(object)VisualRoot == (Object)null)
			{
				return;
			}
			_buriedLocal = VisualRoot.localPosition;
			float num = 1f;
			if ((Object)(object)VisualRoot.parent != (Object)null)
			{
				num = Mathf.Abs(VisualRoot.parent.lossyScale.y);
				if (num < 0.0001f)
				{
					num = 1f;
				}
			}
			_risenLocal = _buriedLocal + Vector3.up * (MeasureRiseHeight() / num);
		}

		private float MeasureRiseHeight()
		{
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//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)
			//IL_007c: 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_0091: 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)
			if ((Object)(object)VisualRoot != (Object)null)
			{
				Renderer[] componentsInChildren = ((Component)VisualRoot).GetComponentsInChildren<Renderer>(true);
				bool flag = false;
				float num = float.MaxValue;
				float num2 = float.MinValue;
				foreach (Renderer val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && (val is MeshRenderer || val is SkinnedMeshRenderer))
					{
						Bounds bounds = val.bounds;
						if (((Bounds)(ref bounds)).min.y < num)
						{
							num = ((Bounds)(ref bounds)).min.y;
						}
						if (((Bounds)(ref bounds)).max.y > num2)
						{
							num2 = ((Bounds)(ref bounds)).max.y;
						}
						flag = true;
					}
				}
				if (flag && num2 - num > 0.01f)
				{
					return num2 - num;
				}
			}
			if ((Object)(object)HitVolume != (Object)null)
			{
				float num3 = Mathf.Abs(HitVolume.size.y * ((Component)HitVolume).transform.lossyScale.y);
				if (num3 > 0.01f)
				{
					return num3;
				}
			}
			return 1f;
		}

		private void SampleAndDamage()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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)
			if (!_authoritative)
			{
				return;
			}
			try
			{
				Vector3 val = ((Component)this).transform.position + Vector3.up * 0.5f;
				Vector3 val2 = HitHalfExtents();
				Collider[] array = Physics.OverlapBox(val, val2, ((Component)this).transform.rotation, TargetMask, (QueryTriggerInteraction)2);
				foreach (Collider val3 in array)
				{
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					PlayerAvatar val4 = ResolvePlayer(val3, val);
					if ((Object)(object)val4 != (Object)null)
					{
						TryDamage(val4);
						continue;
					}
					Enemy val5 = ResolveEnemy(val3);
					if ((Object)(object)val5 != (Object)null)
					{
						TryDamageEnemy(val5, val);
					}
				}
			}
			catch (Exception arg)
			{
				if (!_warnedSampleError)
				{
					_warnedSampleError = true;
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogError((object)$"[Evoker] 地刺#{_index} 命中采样异常(本根后续仍会尝试,只是不刷屏):{arg}");
					}
				}
			}
		}

		private Vector3 HitHalfExtents()
		{
			//IL_0023: 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)
			//IL_0034: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: 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_00be: 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)
			if ((Object)(object)HitVolume == (Object)null)
			{
				return DefaultHitHalfExtents;
			}
			Vector3 size = HitVolume.size;
			Vector3 lossyScale = ((Component)HitVolume).transform.lossyScale;
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(Mathf.Abs(size.x * lossyScale.x), Mathf.Abs(size.y * lossyScale.y), Mathf.Abs(size.z * lossyScale.z));
			if (val.x <= 0.01f || val.y <= 0.01f || val.z <= 0.01f)
			{
				return DefaultHitHalfExtents;
			}
			return val * 0.5f;
		}

		private static PlayerAvatar ResolvePlayer(Collider collider, Vector3 sampleCenter)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			PlayerTumble componentInParent = ((Component)collider).GetComponentInParent<PlayerTumble>();
			if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.playerAvatar != (Object)null)
			{
				return componentInParent.playerAvatar;
			}
			if (((Component)collider).gameObject.layer == PlayerLayer)
			{
				Bounds bounds = collider.bounds;
				return NearestPlayer(((Bounds)(ref bounds)).center);
			}
			return ((Component)collider).GetComponentInParent<PlayerAvatar>();
		}

		private static PlayerAvatar NearestPlayer(Vector3 point)
		{
			//IL_0058: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			GameDirector instance = GameDirector.instance;
			List<PlayerAvatar> list = (((Object)(object)instance != (Object)null) ? instance.PlayerList : null);
			if (list == null)
			{
				return null;
			}
			PlayerAvatar result = null;
			float num = float.MaxValue;
			for (int i = 0; i < list.Count; i++)
			{
				PlayerAvatar val = list[i];
				if (!((Object)(object)val == (Object)null))
				{
					Vector3 val2 = ((Component)val).transform.position - point;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						result = val;
					}
				}
			}
			return result;
		}

		private static Enemy ResolveEnemy(Collider collider)
		{
			Enemy val = ((Component)collider).GetComponentInParent<Enemy>();
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			EnemyParent componentInParent = ((Component)collider).GetComponentInParent<EnemyParent>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				val = ((Component)componentInParent).GetComponentInChildren<Enemy>(true);
			}
			return val;
		}

		private void TryDamage(PlayerAvatar player)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			if (IsPlayerHittable(player) && _hitTargets.Add((Component)(object)player))
			{
				float time = Time.time;
				if (!_lastHitAt.TryGetValue((Component)(object)player, out var value) || !(time - value < 0.2f))
				{
					RecordLastHit((Component)(object)player, time);
					int value2 = IllagerConfig.EvokerFangDamage.Value;
					player.playerHealth.HurtOther(value2, ((Component)player).transform.position, true, _enemyIndex, false);
					Plugin.Debug($"[Evoker] 地刺#{_index} 命中 {((Object)player).name}:{value2} 点" + $"(归因 enemyIndex={_enemyIndex})。");
				}
			}
		}

		private void TryDamageEnemy(Enemy enemy, Vector3 sampleCenter)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			if (!IsEnemyHittable(enemy) || !_hitTargets.Add((Component)(object)enemy))
			{
				return;
			}
			float time = Time.time;
			if (!_lastHitAt.TryGetValue((Component)(object)enemy, out var value) || !(time - value < 0.2f))
			{
				RecordLastHit((Component)(object)enemy, time);
				Vector3 val = ((Component)enemy).transform.position - sampleCenter;
				val.y = 0f;
				if (((Vector3)(ref val)).sqrMagnitude < 1E-06f)
				{
					val = Vector3.forward;
				}
				else
				{
					((Vector3)(ref val)).Normalize();
				}
				int value2 = IllagerConfig.EvokerFangDamage.Value;
				enemy.Health.Hurt(value2, val);
				Plugin.Debug($"[Evoker] 地刺#{_index} 命中怪物 {((Object)enemy).name}:{value2} 点" + $"(击退方向 ({val.x:F2},{val.y:F2},{val.z:F2}),归因 enemyIndex={_enemyIndex} —— 敌人侧无归因参数)。");
			}
		}

		private bool IsEnemyHittable(Enemy enemy)
		{
			if ((Object)(object)enemy == (Object)null)
			{
				return false;
			}
			EnemyHealth health = enemy.Health;
			if ((Object)(object)health == (Object)null || !enemy.HasHealth)
			{
				return false;
			}
			if (health.dead || health.healthCurrent <= 0)
			{
				return false;
			}
			return !IsExcludedEnemy(enemy);
		}

		private bool IsExcludedEnemy(Enemy enemy)
		{
			if (_enemyIndex >= 0 && SemiFunc.EnemyGetIndex(enemy) == _enemyIndex)
			{
				return true;
			}
			if ((Object)(object)((Component)enemy).GetComponent<VexBehaviour>() != (Object)null)
			{
				return true;
			}
			return false;
		}

		private static void RecordLastHit(Component target, float now)
		{
			_lastHitAt[target] = now;
			if (_lastHitAt.Count <= 16)
			{
				return;
			}
			float num = now - 5f;
			List<Component> list = null;
			foreach (KeyValuePair<Component, float> item in _lastHitAt)
			{
				if ((Object)(object)item.Key == (Object)null || item.Value < num)
				{
					if (list == null)
					{
						list = new List<Component>();
					}
					list.Add(item.Key);
				}
			}
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					_lastHitAt.Remove(list[i]);
				}
			}
		}

		internal static void ClearLevelState()
		{
			_lastHitAt.Clear();
		}

		private static bool IsPlayerHittable(PlayerAvatar player)
		{
			return (Object)(object)player != (Object)null && !player.isDisabled && !player.deadSet && (Object)(object)player.playerHealth != (Object)null;
		}
	}
	internal struct EvokerFangShot
	{
		public float Delay;

		public Vector3 Position;

		public float Yaw;
	}
	internal static class EvokerNetwork
	{
		private const int MaxFangsPerBatch = 64;

		private const float MaxAbsCoordinate = 100000f;

		private const float MaxFangDelay = 10f;

		private const float FinishedBatchMemory = 20f;

		private const int FinishedBatchCap = 128;

		private static NetworkedEvent _fangEvent;

		private static bool _inited;

		private static GameObject _fangPrefab;

		private static readonly List<EvokerFang> _activeFangs = new List<EvokerFang>();

		private static readonly Dictionary<string, float> _finished = new Dictionary<string, float>();

		private static readonly List<string> _scratchKeys = new List<string>();

		private static readonly List<EvokerFangShot> _parsedShots = new List<EvokerFangShot>();

		private static bool _warnedNoPrefab;

		private static bool _warnedBadPayload;

		private static bool _warnedNotInited;

		private static bool _warnedNoIdentity;

		private static bool _warnedNoComponent;

		private static bool _warnedTooMany;

		internal static void Init()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			if (_inited)
			{
				return;
			}
			_inited = true;
			_fangEvent = new NetworkedEvent("Illagers.Evoker.FangBatch", (Action<EventData>)OnFangBatch);
			if (_fangEvent.EventCode == 0)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)"[Evoker] 网络事件注册失败(事件码为 0):地刺不会联机同步,本地仍可运行。");
				}
			}
			else
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("[Evoker] 网络事件已注册:Illagers.Evoker.FangBatch(code=" + _fangEvent.EventCode + ")"));
				}
			}
		}

		internal static void SetFangPrefab(GameObject fangPrefab)
		{
			if ((Object)(object)fangPrefab == (Object)null)
			{
				return;
			}
			bool flag = _fangPrefab != fangPrefab;
			_fangPrefab = fangPrefab;
			if (flag)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[Evoker] 资源就绪:地刺 Prefab = " + ((Object)fangPrefab).name));
				}
			}
		}

		internal static bool RaiseFangBatch(int casterViewId, int casterOwnerTag, int spawnGeneration, int castSequence, IList<EvokerFangShot> shots)
		{
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			if (!IsAuthority())
			{
				return false;
			}
			if (shots == null || shots.Count == 0)
			{
				return false;
			}
			if (shots.Count > 64)
			{
				WarnOnce(ref _warnedTooMany, "[Evoker] 地刺批次被丢弃:根数超过上限(" + 64 + "),疑似载荷或阵型计算异常。");
				return false;
			}
			int num = IdentityOf(casterViewId, casterOwnerTag);
			if (num <= 0)
			{
				WarnOnce(ref _warnedNoIdentity, "[Evoker] 地刺批次被丢弃:施法者拿不到 PhotonView.ViewID,也算不出本地合成标识(本批作废)。");
				return false;
			}
			if (_fangEvent == null || _fangEvent.EventCode == 0)
			{
				WarnOnce(ref _warnedNotInited, "[Evoker] 地刺批次被丢弃:网络事件尚未就绪(Init 未成功)。");
				return false;
			}
			int count = shots.Count;
			object[] array = new object[EvokerFangPayload.ExpectedLength(count)];
			array[0] = LevelGeneration();
			array[1] = casterViewId;
			array[2] = casterOwnerTag;
			array[3] = spawnGeneration;
			array[4] = castSequence;
			array[5] = count;
			for (int i = 0; i < count; i++)
			{
				EvokerFangShot evokerFangShot = shots[i];
				int num2 = 6 + i * 5;
				array[num2] = (IsFinite(evokerFangShot.Delay) ? evokerFangShot.Delay : 0f);
				array[num2 + 1] = evokerFangShot.Position.x;
				array[num2 + 2] = evokerFangShot.Position.y;
				array[num2 + 3] = evokerFangShot.Position.z;
				array[num2 + 4] = (IsFinite(evokerFangShot.Yaw) ? evokerFangShot.Yaw : 0f);
			}
			_fangEvent.RaiseEvent((object)array, NetworkingEvents.RaiseAll, SendOptions.SendReliable);
			return true;
		}

		internal static void ClearLevel()
		{
			for (int i = 0; i < _activeFangs.Count; i++)
			{
				EvokerFang evokerFang = _activeFangs[i];
				if ((Object)(object)evokerFang != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)evokerFang).gameObject);
				}
			}
			_activeFangs.Clear();
			_finished.Clear();
			_parsedShots.Clear();
			EvokerFang.ClearLevelState();
			_warnedNoPrefab = false;
			_warnedBadPayload = false;
			_warnedNotInited = false;
			_warnedNoIdentity = false;
			_warnedNoComponent = false;
			_warnedTooMany = false;
			Plugin.Debug("[Evoker] ClearLevel:已清理活跃地刺与批次缓存。");
		}

		private static void OnFangBatch(EventData data)
		{
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			if (!IsFromAuthority(data))
			{
				return;
			}
			if (!(data.CustomData is object[] array) || array.Length < 6)
			{
				WarnOnce(ref _warnedBadPayload, "[Evoker] 丢弃字段数不符的地刺批次包(协议不一致?)。");
			}
			else
			{
				if (!TryInt(array[0], out var value) || value != LevelGeneration() || !TryInt(array[1], out var value2) || !TryInt(array[2], out var value3) || !TryInt(array[3], out var value4) || !TryInt(array[4], out var value5) || value5 <= 0)
				{
					return;
				}
				if (!TryInt(array[5], out var value6) || value6 <= 0 || value6 > 64 || array.Length != EvokerFangPayload.ExpectedLength(value6))
				{
					WarnOnce(ref _warnedBadPayload, "[Evoker] 丢弃根数非法 / 长度不符的地刺批次包(协议不一致?)。");
					return;
				}
				int num = IdentityOf(value2, value3);
				if (num <= 0)
				{
					return;
				}
				string text = KeyOf(value, num, value4, value5);
				if (_finished.ContainsKey(text))
				{
					return;
				}
				if ((Object)(object)_fangPrefab == (Object)null)
				{
					WarnOnce(ref _warnedNoPrefab, "[Evoker] 收到地刺批次但地刺 Prefab 尚未就绪,本批被丢弃。请检查 G5:EvokerBehaviour 的 FangPrefab 是否已拖引用。");
				}
				else
				{
					if (!TryParseShots(array, value6))
					{
						return;
					}
					bool flag = IsAuthority();
					int enemyIndex = (flag ? ResolveEnemyIndex(value2, value3) : (-1));
					MarkFinished(text);
					int num2 = 0;
					int num3 = 0;
					for (int i = 0; i < _parsedShots.Count; i++)
					{
						Vector3 position = _parsedShots[i].Position;
						float yaw = _parsedShots[i].Yaw;
						float delay = _parsedShots[i].Delay;
						if (!IsFinite(position.x) || !IsFinite(position.y) || !IsFinite(position.z))
						{
							num3++;
							continue;
						}
						GameObject val = null;
						try
						{
							val = Object.Instantiate<GameObject>(_fangPrefab, position, Quaternion.Euler(0f, yaw, 0f));
							EvokerFang component = val.GetComponent<EvokerFang>();
							if ((Object)(object)component == (Object)null)
							{
								Object.Destroy((Object)(object)val);
								WarnOnce(ref _warnedNoComponent, "[Evoker] 地刺 Prefab 根上没有 EvokerFang,本批地刺未被生成(G5 第 3 条)。");
								break;
							}
							component.Configure(position, yaw, i, delay, flag, enemyIndex);
							_activeFangs.Add(component);
							num2++;
						}
						catch (Exception ex)
						{
							WarnOnce(ref _warnedNoComponent, "[Evoker] 生成地刺时出错,本批剩余地刺不再生成:" + ex.Message);
							break;
						}
					}
					Plugin.Debug("[Evoker] 地刺批次 " + text + ":生成 " + num2 + " 根" + ((num3 > 0) ? (",跳过 " + num3 + " 个失效点") : "") + ",权威=" + flag + ",enemyIndex=" + enemyIndex);
				}
			}
		}

		private static bool TryParseShots(object[] p, int fangCount)
		{
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			_parsedShots.Clear();
			for (int i = 0; i < fangCount; i++)
			{
				int num = 6 + i * 5;
				if (!TryFloat(p[num], out var value) || value < 0f || value > 10f)
				{
					return false;
				}
				if (!TryFloatRaw(p[num + 1], out var value2) || !TryFloatRaw(p[num + 2], out var value3) || !TryFloatRaw(p[num + 3], out var value4))
				{
					return false;
				}
				if (!TryFloat(p[num + 4], out var value5))
				{
					return false;
				}
				if ((IsFinite(value2) && Mathf.Abs(value2) > 100000f) || (IsFinite(value3) && Mathf.Abs(value3) > 100000f) || (IsFinite(value4) && Mathf.Abs(value4) > 100000f))
				{
					return false;
				}
				_parsedShots.Add(new EvokerFangShot
				{
					Delay = value,
					Position = new Vector3(value2, value3, value4),
					Yaw = value5
				});
			}
			return true;
		}

		private static int IdentityOf(int casterViewId, int casterOwnerTag)
		{
			return (casterViewId > 0) ? casterViewId : casterOwnerTag;
		}

		private static string KeyOf(int levelGen, int identity, int spawnGeneration, int castSequence)
		{
			return levelGen + ":" + identity + ":" + spawnGeneration + ":" + castSequence;
		}

		private static void MarkFinished(string key)
		{
			_finished[key] = Time.time;
			if (_finished.Count > 128)
			{
				PruneFinished();
			}
		}

		private static void PruneFinished()
		{
			float num = Time.time - 20f;
			_scratchKeys.Clear();
			foreach (KeyValuePair<string, float> item in _finished)
			{
				if (item.Value < num)
				{
					_scratchKeys.Add(item.Key);
				}
			}
			for (int i = 0; i < _scratchKeys.Count; i++)
			{
				_finished.Remove(_scratchKeys[i]);
			}
			if (_finished.Count > 128)
			{
				_scratchKeys.Clear();
				_scratchKeys.AddRange(_finished.Keys);
				_scratchKeys.Sort((string a, string b) => _finished[a].CompareTo(_finished[b]));
				int num2 = _finished.Count - 128;
				for (int num3 = 0; num3 < num2 && num3 < _scratchKeys.Count; num3++)
				{
					_finished.Remove(_scratchKeys[num3]);
				}
			}
		}

		private static int ResolveEnemyIndex(int casterViewId, int casterOwnerTag)
		{
			try
			{
				if (casterViewId > 0)
				{
					PhotonView val = PhotonView.Find(casterViewId);
					if ((Object)(object)val != (Object)null)
					{
						Enemy val2 = ((Component)val).GetComponent<Enemy>();
						if ((Object)(object)val2 == (Object)null)
						{
							EnemyParent componentInParent = ((Component)val).GetComponentInParent<EnemyParent>();
							val2 = (((Object)(object)componentInParent != (Object)null) ? componentInParent.Enemy : null);
						}
						if ((Object)(object)val2 != (Object)null)
						{
							int num = SemiFunc.EnemyGetIndex(val2);
							if (num >= 0)
							{
								return num;
							}
						}
					}
				}
				if (casterOwnerTag >= 1000000)
				{
					int num2 = casterOwnerTag - 1000000;
					EnemyDirector instance = EnemyDirector.instance;
					if ((Object)(object)instance != (Object)null && instance.enemiesSpawned != null)
					{
						List<EnemyParent> enemiesSpawned = instance.enemiesSpawned;
						for (int i = 0; i < enemiesSpawned.Count; i++)
						{
							EnemyParent val3 = enemiesSpawned[i];
							if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3.Enemy == (Object)null) && SemiFunc.EnemyGetIndex(val3.Enemy) == num2)
							{
								return num2;
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Debug("[Evoker] 解析施法者敌人索引失败(本次不结