Start Tag System
This commit is contained in:
@@ -21,8 +21,13 @@ namespace GeometryTD.Entity
|
||||
private readonly List<Vector3> _pathPoints = new();
|
||||
private int _pathPointIndex;
|
||||
private bool _isDespawnRequested;
|
||||
private readonly EnemyTagStatusRuntime _tagStatusRuntime = new();
|
||||
|
||||
public static IReadOnlyList<EnemyEntity> ActiveEnemies => _activeEnemies;
|
||||
public int CurrentHealth => _currentHealth;
|
||||
public int MaxHealth => _maxHealth;
|
||||
public bool HasSlowStatus => _tagStatusRuntime.GetMoveSpeedMultiplier() < 0.999f;
|
||||
public float MoveSpeedMultiplier => _tagStatusRuntime.GetMoveSpeedMultiplier();
|
||||
|
||||
public static bool TryConsumeKilledFlag(int entityId)
|
||||
{
|
||||
@@ -46,6 +51,7 @@ namespace GeometryTD.Entity
|
||||
_isDespawnRequested = false;
|
||||
_maxHealth = 1;
|
||||
_currentHealth = 1;
|
||||
_tagStatusRuntime.Reset();
|
||||
if (userData is EnemyData enemyData)
|
||||
{
|
||||
_speed = enemyData.Speed;
|
||||
@@ -74,6 +80,7 @@ namespace GeometryTD.Entity
|
||||
protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
base.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
_tagStatusRuntime.Tick(elapseSeconds, ApplyDamageFromStatus);
|
||||
|
||||
if (_pathPoints.Count > 0)
|
||||
{
|
||||
@@ -90,6 +97,7 @@ namespace GeometryTD.Entity
|
||||
_isDespawnRequested = false;
|
||||
_maxHealth = 0;
|
||||
_currentHealth = 0;
|
||||
_tagStatusRuntime.Reset();
|
||||
_activeEnemies.Remove(this);
|
||||
|
||||
base.OnHide(isShutdown, userData);
|
||||
@@ -100,26 +108,24 @@ namespace GeometryTD.Entity
|
||||
_activeEnemies.Remove(this);
|
||||
}
|
||||
|
||||
public void TakeDamage(int damage, AttackPropertyType attackPropertyType)
|
||||
public void TakeDamage(AttackPayload attackPayload)
|
||||
{
|
||||
_ = attackPropertyType;
|
||||
if (_isDespawnRequested || damage <= 0 || _currentHealth <= 0)
|
||||
if (_isDespawnRequested || _currentHealth <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int previousHealth = _currentHealth;
|
||||
_currentHealth = Mathf.Max(0, _currentHealth - damage);
|
||||
if (_maxHealth > 0)
|
||||
HitContext hitContext = TagEffectResolver.ResolveBeforeHit(
|
||||
attackPayload,
|
||||
_currentHealth,
|
||||
_maxHealth,
|
||||
HasSlowStatus);
|
||||
ApplyDirectDamage(hitContext.FinalDamage);
|
||||
TagEffectResolver.ApplyOnHit(hitContext);
|
||||
TagEffectResolver.ApplyAfterHit(hitContext.AttackPayload, _tagStatusRuntime);
|
||||
if (hitContext.IsKilled)
|
||||
{
|
||||
GameEntry.HPBar?.ShowHPBar(this, (float)previousHealth / _maxHealth,
|
||||
(float)_currentHealth / _maxHealth);
|
||||
}
|
||||
|
||||
if (_currentHealth <= 0)
|
||||
{
|
||||
_killedEnemyEntityIds.Add(Id);
|
||||
RequestDespawn();
|
||||
TagEffectResolver.ApplyOnKill(hitContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +161,7 @@ namespace GeometryTD.Entity
|
||||
return;
|
||||
}
|
||||
|
||||
_movementComponent.SetSpeedMultiplier(_tagStatusRuntime.GetMoveSpeedMultiplier());
|
||||
_movementComponent.SetMove(true);
|
||||
_movementComponent.SetDirection(direction);
|
||||
_movementComponent.OnUpdate(elapseSeconds, realElapseSeconds);
|
||||
@@ -188,5 +195,34 @@ namespace GeometryTD.Entity
|
||||
_movementComponent.SetMove(false);
|
||||
GameEntry.Entity.HideEntity(Entity);
|
||||
}
|
||||
|
||||
private void ApplyDamageFromStatus(int damage)
|
||||
{
|
||||
ApplyDirectDamage(damage);
|
||||
}
|
||||
|
||||
private void ApplyDirectDamage(int damage)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
if (_currentHealth > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_killedEnemyEntityIds.Add(Id);
|
||||
RequestDespawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.Entity
|
||||
{
|
||||
public sealed class EnemyTagStatusRuntime
|
||||
{
|
||||
private readonly Dictionary<TagType, EnemyStatusTagStateBase> _statesByTag = new();
|
||||
private readonly List<TagType> _activeTags = new();
|
||||
|
||||
public bool HasStatus(TagType tagType)
|
||||
{
|
||||
return _statesByTag.ContainsKey(tagType);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_statesByTag.Clear();
|
||||
_activeTags.Clear();
|
||||
}
|
||||
|
||||
public void Activate(TagType tagType)
|
||||
{
|
||||
if (_activeTags.Contains(tagType))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_activeTags.Add(tagType);
|
||||
}
|
||||
|
||||
public TState GetState<TState>(TagType tagType) where TState : EnemyStatusTagStateBase
|
||||
{
|
||||
if (!_statesByTag.TryGetValue(tagType, out EnemyStatusTagStateBase state))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.Assert(state is TState);
|
||||
return state as TState;
|
||||
}
|
||||
|
||||
public TState GetOrCreateState<TState>(TagType tagType) where TState : EnemyStatusTagStateBase, new()
|
||||
{
|
||||
if (_statesByTag.TryGetValue(tagType, out EnemyStatusTagStateBase existingState))
|
||||
{
|
||||
Debug.Assert(existingState is TState);
|
||||
return existingState as TState;
|
||||
}
|
||||
|
||||
TState state = new TState();
|
||||
_statesByTag[tagType] = state;
|
||||
return state;
|
||||
}
|
||||
|
||||
public void Tick(float deltaTime, Action<int> applyDamage)
|
||||
{
|
||||
float resolvedDeltaTime = Mathf.Max(0f, deltaTime);
|
||||
if (resolvedDeltaTime <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = _activeTags.Count - 1; i >= 0; i--)
|
||||
{
|
||||
TagType tagType = _activeTags[i];
|
||||
Debug.Assert(EnemyStatusTagRegistry.TryGetEffect(tagType, out IEnemyStatusTagEffect effect));
|
||||
|
||||
if (effect.Tick(this, resolvedDeltaTime, applyDamage))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_statesByTag.Remove(tagType);
|
||||
_activeTags.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
public float GetMoveSpeedMultiplier()
|
||||
{
|
||||
float multiplier = 1f;
|
||||
for (int i = 0; i < _activeTags.Count; i++)
|
||||
{
|
||||
TagType tagType = _activeTags[i];
|
||||
Debug.Assert(EnemyStatusTagRegistry.TryGetEffect(tagType, out IEnemyStatusTagEffect effect));
|
||||
multiplier = Mathf.Min(multiplier, effect.GetMoveSpeedMultiplier(this));
|
||||
}
|
||||
|
||||
return multiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe0965914fd71e34ea696bfd546efc66
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user