2026-05-10 11:47:55 -04:00
|
|
|
using System;
|
2025-11-25 08:19:33 -05:00
|
|
|
using System.Collections.Generic;
|
2026-07-18 03:16:20 -04:00
|
|
|
using System.Collections.ObjectModel;
|
2025-11-25 08:19:33 -05:00
|
|
|
using Cielonos.MainGame.Characters;
|
2026-07-18 03:16:20 -04:00
|
|
|
using Sirenix.OdinInspector;
|
|
|
|
|
using SLSUtilities.FunctionalAnimation;
|
2025-11-25 08:19:33 -05:00
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
namespace Cielonos.MainGame
|
|
|
|
|
{
|
2026-05-23 08:27:50 -04:00
|
|
|
public partial class CombatManager
|
2025-11-25 08:19:33 -05:00
|
|
|
{
|
2026-05-23 08:27:50 -04:00
|
|
|
public partial class EnemySubmodule : SubmoduleBase<CombatManager>
|
2025-11-25 08:19:33 -05:00
|
|
|
{
|
|
|
|
|
public enum SortingType
|
|
|
|
|
{
|
|
|
|
|
Nearest,
|
|
|
|
|
Farthest
|
|
|
|
|
}
|
2026-05-10 11:47:55 -04:00
|
|
|
|
2026-07-18 03:16:20 -04:00
|
|
|
private readonly List<Enemy> _activeEnemiesList;
|
|
|
|
|
private readonly ReadOnlyCollection<Enemy> _activeEnemies;
|
|
|
|
|
|
|
|
|
|
public IReadOnlyList<Enemy> ActiveEnemies => _activeEnemies;
|
|
|
|
|
public int ActiveEnemyCount => _activeEnemiesList.Count;
|
|
|
|
|
private int TargetingVersion { get; set; }
|
2026-05-10 11:47:55 -04:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 敌人从活跃列表中移除时触发(即敌人死亡/离场)。
|
|
|
|
|
/// 参数为被移除的敌人实例。
|
|
|
|
|
/// </summary>
|
2026-05-26 00:21:27 -04:00
|
|
|
public event Action<Enemy> OnEnemyRemoved;
|
2026-05-10 11:47:55 -04:00
|
|
|
|
2026-05-23 08:27:50 -04:00
|
|
|
public EnemySubmodule(CombatManager owner) : base(owner)
|
2025-11-25 08:19:33 -05:00
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
_activeEnemiesList = new List<Enemy>();
|
|
|
|
|
_activeEnemies = _activeEnemiesList.AsReadOnly();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 将敌人加入活跃列表。所有敌人注册都应经过这里,避免外部直接修改索敌源。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void RegisterEnemy(Enemy enemy)
|
|
|
|
|
{
|
|
|
|
|
if (enemy == null || _activeEnemiesList.Contains(enemy)) return;
|
|
|
|
|
|
|
|
|
|
_activeEnemiesList.Add(enemy);
|
|
|
|
|
TargetingVersion++;
|
2025-11-25 08:19:33 -05:00
|
|
|
}
|
2026-05-10 11:47:55 -04:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 将敌人从活跃列表中移除并触发 OnEnemyRemoved 事件。
|
2026-07-18 03:16:20 -04:00
|
|
|
/// 由 Automata.Die() 调用,替代直接操作活跃敌人列表。
|
2026-05-10 11:47:55 -04:00
|
|
|
/// </summary>
|
2026-05-26 00:21:27 -04:00
|
|
|
public void RemoveEnemy(Enemy enemy)
|
2026-05-10 11:47:55 -04:00
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
if (!_activeEnemiesList.Remove(enemy)) return;
|
|
|
|
|
|
|
|
|
|
TargetingVersion++;
|
2026-05-10 11:47:55 -04:00
|
|
|
OnEnemyRemoved?.Invoke(enemy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 清空活跃敌人列表(房间重置时调用)。不触发逐条 OnEnemyRemoved 事件。
|
|
|
|
|
/// </summary>
|
2026-02-13 09:22:11 -05:00
|
|
|
public void Reset()
|
|
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
if (_activeEnemiesList.Count == 0) return;
|
|
|
|
|
|
|
|
|
|
_activeEnemiesList.Clear();
|
|
|
|
|
TargetingVersion++;
|
2026-02-13 09:22:11 -05:00
|
|
|
}
|
|
|
|
|
|
2026-07-18 03:16:20 -04:00
|
|
|
private List<Enemy> CollectEnemiesInRadius(Vector3 origin, float radius, SortingType sortingType = SortingType.Nearest)
|
2025-11-25 08:19:33 -05:00
|
|
|
{
|
2026-05-26 00:21:27 -04:00
|
|
|
List<Enemy> enemiesInRadius = new List<Enemy>();
|
2026-07-18 03:16:20 -04:00
|
|
|
foreach (Enemy enemy in _activeEnemiesList)
|
2025-11-25 08:19:33 -05:00
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
if (IsEnemyInRadius(enemy, origin, radius))
|
2025-11-25 08:19:33 -05:00
|
|
|
{
|
|
|
|
|
enemiesInRadius.Add(enemy);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sortingType == SortingType.Nearest)
|
|
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
enemiesInRadius.Sort((a, b) =>
|
|
|
|
|
GetSurfaceDistance(origin, a).CompareTo(GetSurfaceDistance(origin, b)));
|
2025-11-25 08:19:33 -05:00
|
|
|
}
|
|
|
|
|
else if (sortingType == SortingType.Farthest)
|
|
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
enemiesInRadius.Sort((a, b) =>
|
|
|
|
|
GetSurfaceDistance(origin, b).CompareTo(GetSurfaceDistance(origin, a)));
|
2025-11-25 08:19:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return enemiesInRadius;
|
|
|
|
|
}
|
2026-07-18 03:16:20 -04:00
|
|
|
|
|
|
|
|
public bool IsEnemyInRadius(Enemy enemy, Vector3 origin, float radius)
|
2026-04-28 15:46:32 -04:00
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
if (enemy == null || enemy.statusSm.isDead) return false;
|
|
|
|
|
|
|
|
|
|
return GetSurfaceDistance(origin, enemy) <= radius;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Button("Debug: Force GetHit All Enemies", ButtonSizes.Medium)]
|
|
|
|
|
[GUIColor(1f, 0.55f, 0.35f)]
|
|
|
|
|
private void DebugForceGetHitAllEnemies()
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < _activeEnemiesList.Count; i++)
|
2026-04-28 15:46:32 -04:00
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
Enemy enemy = _activeEnemiesList[i];
|
2026-04-28 15:46:32 -04:00
|
|
|
if (enemy == null || enemy.statusSm.isDead) continue;
|
2026-07-18 03:16:20 -04:00
|
|
|
|
|
|
|
|
Vector3 direction = Vector3.zero;
|
|
|
|
|
Player player = MainGameManager.Player;
|
|
|
|
|
if (player != null)
|
2026-04-28 15:46:32 -04:00
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
direction = enemy.transform.position - player.transform.position;
|
2026-04-28 15:46:32 -04:00
|
|
|
}
|
2026-07-18 03:16:20 -04:00
|
|
|
|
|
|
|
|
enemy.GetHit(
|
|
|
|
|
Breakthrough.Type.Forced,
|
|
|
|
|
out _,
|
|
|
|
|
DisruptionType.ForcedExternal,
|
|
|
|
|
direction);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
[Button("Debug: Force Deplete All Enemies", ButtonSizes.Medium)]
|
|
|
|
|
[GUIColor(1f, 0.55f, 0.35f)]
|
|
|
|
|
private void DebugForceDepleteAllEnemies()
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < _activeEnemiesList.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
Enemy enemy = _activeEnemiesList[i];
|
|
|
|
|
if (enemy == null || enemy.statusSm.isDead) continue;
|
|
|
|
|
Player player = MainGameManager.Player;
|
|
|
|
|
enemy.TakeDepletion(player, 999999, false);
|
2026-04-28 15:46:32 -04:00
|
|
|
}
|
2026-07-18 03:16:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private float GetSurfaceDistance(Vector3 origin, Enemy enemy)
|
|
|
|
|
{
|
|
|
|
|
if (enemy == null) return float.MaxValue;
|
|
|
|
|
|
|
|
|
|
float colliderRadius = enemy.collisionSc != null ? enemy.collisionSc.colliderRadius : 0f;
|
|
|
|
|
return Mathf.Max(0f, Vector3.Distance(origin, enemy.transform.position) - colliderRadius);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取所有可见敌人
|
|
|
|
|
/// </summary>
|
|
|
|
|
public List<Enemy> GetVisibleEnemies(float radius = 50f)
|
|
|
|
|
{
|
|
|
|
|
return Query(radius).OnlyVisible().Candidates();
|
2026-04-28 15:46:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取按屏幕X坐标从左到右排序的可见敌人列表
|
|
|
|
|
/// </summary>
|
2026-05-26 00:21:27 -04:00
|
|
|
public List<Enemy> GetVisibleEnemiesSortedByScreenX(float radius = 50f)
|
2026-04-28 15:46:32 -04:00
|
|
|
{
|
2026-05-26 00:21:27 -04:00
|
|
|
List<Enemy> visibleEnemies = GetVisibleEnemies(radius);
|
2026-04-28 15:46:32 -04:00
|
|
|
|
|
|
|
|
visibleEnemies.Sort((a, b) =>
|
|
|
|
|
{
|
2026-07-18 03:16:20 -04:00
|
|
|
Vector3 screenA = MainGameManager.Player.viewSc.playerCamera.WorldToScreenPoint(a.CenterPoint.position);
|
|
|
|
|
Vector3 screenB = MainGameManager.Player.viewSc.playerCamera.WorldToScreenPoint(b.CenterPoint.position);
|
2026-04-28 15:46:32 -04:00
|
|
|
return screenA.x.CompareTo(screenB.x);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return visibleEnemies;
|
|
|
|
|
}
|
2025-11-25 08:19:33 -05:00
|
|
|
}
|
|
|
|
|
}
|
2025-12-24 16:58:51 -05:00
|
|
|
}
|