Start Tag System

This commit is contained in:
2026-03-10 13:11:30 +08:00
parent 34ef001ef3
commit 52f9e212b9
107 changed files with 1850 additions and 50 deletions
@@ -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: