195 lines
5.9 KiB
C#
195 lines
5.9 KiB
C#
using Components;
|
|
using System.Collections.Generic;
|
|
using GeometryTD.Definition;
|
|
using GeometryTD.Entity.EntityData;
|
|
using UnityEngine;
|
|
using UnityGameFramework.Runtime;
|
|
|
|
namespace GeometryTD.Entity
|
|
{
|
|
public class EnemyEntity : EntityBase, IDamageReceiver
|
|
{
|
|
private const float WaypointReachDistance = 0.05f;
|
|
private static readonly List<EnemyEntity> _activeEnemies = new();
|
|
private static readonly HashSet<int> _killedEnemyEntityIds = new();
|
|
|
|
private Transform _target;
|
|
private float _speed;
|
|
private int _maxHealth;
|
|
private int _currentHealth;
|
|
private MovementComponent _movementComponent;
|
|
private readonly List<Vector3> _pathPoints = new();
|
|
private int _pathPointIndex;
|
|
private bool _isDespawnRequested;
|
|
|
|
public static IReadOnlyList<EnemyEntity> ActiveEnemies => _activeEnemies;
|
|
|
|
public static bool TryConsumeKilledFlag(int entityId)
|
|
{
|
|
return _killedEnemyEntityIds.Remove(entityId);
|
|
}
|
|
|
|
protected override void OnInit(object userData)
|
|
{
|
|
base.OnInit(userData);
|
|
|
|
_movementComponent = GetComponent<MovementComponent>();
|
|
}
|
|
|
|
protected override void OnShow(object userData)
|
|
{
|
|
base.OnShow(userData);
|
|
|
|
_target = null;
|
|
_pathPoints.Clear();
|
|
_pathPointIndex = 0;
|
|
_isDespawnRequested = false;
|
|
_maxHealth = 1;
|
|
_currentHealth = 1;
|
|
if (userData is EnemyData enemyData)
|
|
{
|
|
_speed = enemyData.Speed;
|
|
_target = enemyData.Player;
|
|
_maxHealth = Mathf.Max(1, enemyData.MaxHealth);
|
|
_currentHealth = _maxHealth;
|
|
if (enemyData.HasPath)
|
|
{
|
|
IReadOnlyList<Vector3> pathPoints = enemyData.PathPoints;
|
|
for (int i = 0; i < pathPoints.Count; i++)
|
|
{
|
|
_pathPoints.Add(pathPoints[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
_movementComponent.OnInit(_speed, CachedTransform);
|
|
_movementComponent.SetMove(true);
|
|
_killedEnemyEntityIds.Remove(Id);
|
|
if (!_activeEnemies.Contains(this))
|
|
{
|
|
_activeEnemies.Add(this);
|
|
}
|
|
}
|
|
|
|
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
{
|
|
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
|
|
|
if (_pathPoints.Count > 0)
|
|
{
|
|
UpdatePathMovement(elapseSeconds, realElapseSeconds);
|
|
return;
|
|
}
|
|
}
|
|
|
|
protected override void OnHide(bool isShutdown, object userData)
|
|
{
|
|
_movementComponent.SetMove(false);
|
|
_pathPoints.Clear();
|
|
_pathPointIndex = 0;
|
|
_isDespawnRequested = false;
|
|
_maxHealth = 0;
|
|
_currentHealth = 0;
|
|
_activeEnemies.Remove(this);
|
|
|
|
base.OnHide(isShutdown, userData);
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
_activeEnemies.Remove(this);
|
|
}
|
|
|
|
public void TakeDamage(int damage, AttackPropertyType attackPropertyType)
|
|
{
|
|
_ = attackPropertyType;
|
|
if (_isDespawnRequested || damage <= 0 || _currentHealth <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int previousHealth = _currentHealth;
|
|
_currentHealth = Mathf.Max(0, _currentHealth - damage);
|
|
if (_maxHealth > 0)
|
|
{
|
|
GameEntry.HPBar?.ShowHPBar(this, (float)previousHealth / _maxHealth,
|
|
(float)_currentHealth / _maxHealth);
|
|
Log.Info($"ShowBar: {_currentHealth}/{_maxHealth}");
|
|
}
|
|
|
|
if (_currentHealth <= 0)
|
|
{
|
|
Log.Info("Enemy Dead");
|
|
_killedEnemyEntityIds.Add(Id);
|
|
RequestDespawn();
|
|
}
|
|
}
|
|
|
|
private void UpdatePathMovement(float elapseSeconds, float realElapseSeconds)
|
|
{
|
|
if (_isDespawnRequested)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (_pathPointIndex >= _pathPoints.Count)
|
|
{
|
|
DespawnOnReachHouse();
|
|
return;
|
|
}
|
|
|
|
Vector3 direction = GetDirectionToPathPoint(_pathPoints[_pathPointIndex], out float distanceSquared);
|
|
if (distanceSquared <= WaypointReachDistance * WaypointReachDistance)
|
|
{
|
|
_pathPointIndex++;
|
|
if (_pathPointIndex >= _pathPoints.Count)
|
|
{
|
|
DespawnOnReachHouse();
|
|
return;
|
|
}
|
|
|
|
direction = GetDirectionToPathPoint(_pathPoints[_pathPointIndex], out _);
|
|
}
|
|
|
|
if (direction.sqrMagnitude <= Mathf.Epsilon)
|
|
{
|
|
_movementComponent.SetMove(false);
|
|
return;
|
|
}
|
|
|
|
_movementComponent.SetMove(true);
|
|
_movementComponent.SetDirection(direction);
|
|
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
|
|
}
|
|
|
|
private Vector3 GetDirectionToPathPoint(Vector3 worldPoint, out float distanceSquared)
|
|
{
|
|
Vector3 delta = worldPoint - CachedTransform.position;
|
|
distanceSquared = delta.sqrMagnitude;
|
|
if (distanceSquared <= Mathf.Epsilon)
|
|
{
|
|
return Vector3.zero;
|
|
}
|
|
|
|
return delta.normalized;
|
|
}
|
|
|
|
private void DespawnOnReachHouse()
|
|
{
|
|
RequestDespawn();
|
|
}
|
|
|
|
private void RequestDespawn()
|
|
{
|
|
if (_isDespawnRequested)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_isDespawnRequested = true;
|
|
_movementComponent.SetMove(false);
|
|
GameEntry.Entity.HideEntity(Entity);
|
|
}
|
|
}
|
|
}
|