process Lo

This commit is contained in:
SepComet 2026-04-30 23:03:47 +08:00
parent fb2252f688
commit 4c413ccd6d
30 changed files with 910 additions and 27 deletions

90
TODO.md
View File

@ -3,13 +3,14 @@
最后更新2026-04-30 最后更新2026-04-30
> **2026-04-30 第一波完成**Definition/Enum + Event > **2026-04-30 第一波完成**Definition/Enum + Event
> **2026-04-30 第二波完成**Vector3/Mathf 替换AttackPayload/HitContext 迁移
## 概述 ## 概述
| 指标 | 数量 | 状态 | | 指标 | 数量 | 状态 |
|------|------|------| |------|------|------|
| 总文件数 | 457 | - | | 总文件数 | 457 | - |
| L0 (Domain) 可直接迁移 | ~180 | **第一波已完成 62 个文件** | | L0 (Domain) 可直接迁移 | ~180 | **第一波已完成 62 个文件,第二波完成 5 个文件** |
| L1 (Infrastructure) 需重构 | ~80 | - | | L1 (Infrastructure) 需重构 | ~80 | - |
| L2 (Presentation) Unity 依赖 | ~197 | - | | L2 (Presentation) Unity 依赖 | ~197 | - |
@ -63,6 +64,8 @@ src/
### Definition/DataStruct8 个文件) ### Definition/DataStruct8 个文件)
- [x] `Definition/DataStruct/AttackPayload.cs` ⚠️ **已迁移** - Vector3 → System.Numerics.Vector3
- [x] `Definition/DataStruct/HitContext.cs` ⚠️ **已迁移** - Vector3 → System.Numerics.Vector3TargetStatusRuntime 已恢复
- [ ] `Definition/DataStruct/BackpackInventoryData.cs` - [ ] `Definition/DataStruct/BackpackInventoryData.cs`
- [ ] `Definition/DataStruct/BuildInfo.cs` - [ ] `Definition/DataStruct/BuildInfo.cs`
- [ ] `Definition/DataStruct/EventItem.cs` - [ ] `Definition/DataStruct/EventItem.cs`
@ -127,18 +130,19 @@ src/
### Definition/Tag37 个文件) ### Definition/Tag37 个文件)
- [ ] `Definition/Tag/Aggregation/TagRuntimeData.cs` - [x] `Definition/Tag/Aggregation/TagRuntimeData.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [x] `Definition/Tag/Aggregation/TagRuntimeUtility.cs` ⚠️ **已新建** - CloneTagRuntimes 方法
- [ ] `Definition/Tag/Aggregation/TowerTagAggregationService.cs` - [ ] `Definition/Tag/Aggregation/TowerTagAggregationService.cs`
- [ ] `Definition/Tag/Combat/EnemyStatusTagRegistry.cs` - [x] `Definition/Tag/Combat/EnemyStatusTagRegistry.cs` ⚠️ **已迁移** - 简化版(含 FireTagEffect、IceTagEffect 注册)
- [ ] `Definition/Tag/Combat/Handlers/AttackShapeTagEffectHandler.cs` - [ ] `Definition/Tag/Combat/Handlers/AttackShapeTagEffectHandler.cs`
- [ ] `Definition/Tag/Combat/Handlers/NumericTagEffectHandler.cs` - [ ] `Definition/Tag/Combat/Handlers/NumericTagEffectHandler.cs`
- [ ] `Definition/Tag/Combat/States/EnemyStatusTagStateBase.cs` - [x] `Definition/Tag/Combat/States/EnemyStatusTagStateBase.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Combat/States/FireTagState.cs` - [x] `Definition/Tag/Combat/States/FireTagState.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Combat/States/IceTagState.cs` - [x] `Definition/Tag/Combat/States/IceTagState.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Combat/StatusEffects/EnemyStatusTagEffectBase.cs` - [x] `Definition/Tag/Combat/StatusEffects/EnemyStatusTagEffectBase.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Combat/StatusEffects/FireTagEffect.cs` - [x] `Definition/Tag/Combat/StatusEffects/FireTagEffect.cs` ⚠️ **已迁移** - Mathf → System.Math
- [ ] `Definition/Tag/Combat/StatusEffects/IEnemyStatusTagEffect.cs` - [x] `Definition/Tag/Combat/StatusEffects/IEnemyStatusTagEffect.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Combat/StatusEffects/IceTagEffect.cs` - [x] `Definition/Tag/Combat/StatusEffects/IceTagEffect.cs` ⚠️ **已迁移** - Mathf → System.Math
- [ ] `Definition/Tag/Combat/TagEffectResolver.cs` - [ ] `Definition/Tag/Combat/TagEffectResolver.cs`
- [ ] `Definition/Tag/Generation/ComponentTagGenerationService.cs` - [ ] `Definition/Tag/Generation/ComponentTagGenerationService.cs`
- [ ] `Definition/Tag/Generation/InventoryTagRandomContext.cs` - [ ] `Definition/Tag/Generation/InventoryTagRandomContext.cs`
@ -146,20 +150,21 @@ src/
- [ ] `Definition/Tag/Generation/RarityTagBudgetRuleRegistry.cs` - [ ] `Definition/Tag/Generation/RarityTagBudgetRuleRegistry.cs`
- [ ] `Definition/Tag/Generation/TagGenerationRule.cs` - [ ] `Definition/Tag/Generation/TagGenerationRule.cs`
- [ ] `Definition/Tag/Generation/TagGenerationRuleRegistry.cs` - [ ] `Definition/Tag/Generation/TagGenerationRuleRegistry.cs`
- [ ] `Definition/Tag/Metadata/Config/AbsoluteZeroTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/AbsoluteZeroTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/BurnSpreadTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/BurnSpreadTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/CritTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/CritTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/ExecutionTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/ExecutionTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/FireTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/FireTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/FreezeMaskTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/FreezeMaskTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/IceTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/IceTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/IgniteBurstTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/IgniteBurstTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/InfernoTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/InfernoTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/OverpenetrateTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/OverpenetrateTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/PierceTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/PierceTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/ShatterTagConfig.cs` - [x] `Definition/Tag/Metadata/Config/ShatterTagConfig.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/Config/TagConfigBase.cs` - [x] `Definition/Tag/Metadata/Config/TagConfigBase.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [ ] `Definition/Tag/Metadata/TagDefinition.cs` - [x] `Definition/Tag/Metadata/TagDefinition.cs` ⚠️ **已迁移** - 无 Unity 依赖
- [x] `Definition/Tag/Metadata/TagDefinitionRegistry.cs` ⚠️ **已迁移** - 简化版(移除 DR* 依赖)
### Definition/Event6 个文件) ### Definition/Event6 个文件)
@ -555,8 +560,8 @@ L0 迁移阻塞链:
### 重构优先级 ### 重构优先级
1. **第一波**Definition/Enum + Event ✅ 已完成 1. **第一波**Definition/Enum + Event ✅ 已完成
2. **第二波**DataTable 整体移入 L1 2. **第二波**Vector3 → System.Numerics.Vector3Mathf → System.Math ✅ **已完成部分**
3. **第三波**Vector3 → System.Numerics.Vector3 替换 3. **第三波**Tag 系统迁移到 L1TagDefinitionRegistry、TagEffectResolver 等)
4. **第四波**GameEntry 静态调用 → 接口注入 4. **第四波**GameEntry 静态调用 → 接口注入
5. **第五波**Tilemap 接口抽象 5. **第五波**Tilemap 接口抽象
6. **第六波**:剩余 L1 文件迁移 6. **第六波**:剩余 L1 文件迁移
@ -602,9 +607,40 @@ L0 迁移阻塞链:
--- ---
## 第二波修改记录2026-04-30
### 进度统计
| 类别 | 源文件数 | 已迁移 | 说明 |
|------|----------|--------|------|
| Definition/DataStruct | 2 | 2 | AttackPayload, HitContextVector3 替换) |
| Definition/Tag | 17 | 17 | Tag系统TagDefinition、TagConfig系列、TagEffect系列等 |
| Entity/EntityLogic | 1 | 1 | EnemyTagStatusRuntimeMathf → System.Math |
| **小计** | **20** | **20** | - |
### 新增/迁移文件Mathf 替换)
| 文件 | 修改内容 |
|------|----------|
| `Entity/EntityLogic/EnemyTagStatusRuntime.cs` | Mathf → System.Math |
| `Definition/Tag/Combat/StatusEffects/FireTagEffect.cs` | 新建Mathf → System.Math |
| `Definition/Tag/Combat/StatusEffects/IceTagEffect.cs` | 新建Mathf → System.Math |
| `Definition/Tag/Metadata/TagDefinitionRegistry.cs` | 简化版,移除 DR* 依赖 |
### L0 构建状态
`dotnet build GeometryTD.Domain` **成功**0 警告 0 错误
### 阻塞说明
1. `TagDefinitionRegistry` 简化版已可用,`DRTag`/`DRTagConfig` 依赖已移除
2. `TagGenerationRuleRegistry` 等仍有 `Debug.Assert` 待替换
---
## 验收标准 ## 验收标准
- [ ] L0 独立构建成功(`dotnet build GeometryTD.Domain` - [x] L0 独立构建成功(`dotnet build GeometryTD.Domain`✅ **2026-04-30**
- [ ] L1 引用 L0 无循环依赖 - [ ] L1 引用 L0 无循环依赖
- [ ] L2 引用 L1 无循环依赖 - [ ] L2 引用 L1 无循环依赖
- [ ] 三层整体构建成功(`dotnet build Geometry-Tower-Defense-Base.sln` - [ ] 三层整体构建成功(`dotnet build Geometry-Tower-Defense-Base.sln`

View File

@ -0,0 +1,46 @@
using System;
using System.Numerics;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class HitStatusModifierContext
{
public float BonusBurnDurationSeconds { get; set; }
public float BonusBurnDamagePerSecond { get; set; }
public float BonusSlowDurationSeconds { get; set; }
public float BonusSlowRatio { get; set; }
public void Reset()
{
BonusBurnDurationSeconds = 0f;
BonusBurnDamagePerSecond = 0f;
BonusSlowDurationSeconds = 0f;
BonusSlowRatio = 0f;
}
}
[Serializable]
public sealed class AttackPayload
{
public int BaseDamage { get; set; }
public AttackPropertyType AttackPropertyType { get; set; }
public int SourceEntityId { get; set; }
public int ProjectileEntityId { get; set; }
public Vector3 OriginPosition { get; set; }
public TagRuntimeData[] TagRuntimes { get; set; } = Array.Empty<TagRuntimeData>();
public AttackPayload Clone()
{
return new AttackPayload
{
BaseDamage = BaseDamage,
AttackPropertyType = AttackPropertyType,
SourceEntityId = SourceEntityId,
ProjectileEntityId = ProjectileEntityId,
OriginPosition = OriginPosition,
TagRuntimes = TagRuntimeUtility.CloneTagRuntimes(TagRuntimes)
};
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Numerics;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class HitContext
{
public AttackPayload AttackPayload { get; set; }
public int FinalDamage { get; set; }
public bool IsCriticalHit { get; set; }
public bool IsKilled { get; set; }
public int TargetEntityId { get; set; }
public Vector3 TargetPosition { get; set; }
public int TargetCurrentHealthBeforeHit { get; set; }
public int TargetCurrentHealthAfterHit { get; set; }
public int TargetMaxHealth { get; set; }
public float TargetMoveSpeedMultiplierBeforeHit { get; set; } = 1f;
public TagType[] TargetStatusTagsBeforeHit { get; set; } = Array.Empty<TagType>();
public EnemyTagStatusRuntime TargetStatusRuntime { get; set; }
public float? CritRoll { get; set; }
public HitStatusModifierContext StatusModifierContext { get; set; } = new HitStatusModifierContext();
public bool HasTargetStatus(TagType tagType)
{
if (tagType == TagType.None || TargetStatusTagsBeforeHit == null || TargetStatusTagsBeforeHit.Length <= 0)
{
return false;
}
for (int i = 0; i < TargetStatusTagsBeforeHit.Length; i++)
{
if (TargetStatusTagsBeforeHit[i] == tagType)
{
return true;
}
}
return false;
}
public bool HasSlowStatusBeforeHit => TargetMoveSpeedMultiplierBeforeHit < 0.999f || HasTargetStatus(TagType.Ice);
}
}

View File

@ -0,0 +1,33 @@
using System;
namespace GeometryTD.Definition
{
public static class TagRuntimeUtility
{
public static TagRuntimeData[] CloneTagRuntimes(TagRuntimeData[] source)
{
if (source == null || source.Length <= 0)
{
return Array.Empty<TagRuntimeData>();
}
TagRuntimeData[] cloned = new TagRuntimeData[source.Length];
for (int i = 0; i < source.Length; i++)
{
TagRuntimeData runtime = source[i];
if (runtime == null)
{
continue;
}
cloned[i] = new TagRuntimeData
{
TagType = runtime.TagType,
TotalStack = runtime.TotalStack
};
}
return cloned;
}
}
}

View File

@ -0,0 +1,11 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class TagRuntimeData
{
public TagType TagType { get; set; }
public int TotalStack { get; set; }
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace GeometryTD.Definition
{
public static class EnemyStatusTagRegistry
{
private static readonly Dictionary<TagType, IEnemyStatusTagEffect> EffectsByTag = new Dictionary<TagType, IEnemyStatusTagEffect>
{
[TagType.Fire] = new FireTagEffect(),
[TagType.Ice] = new IceTagEffect()
};
public static IReadOnlyDictionary<TagType, IEnemyStatusTagEffect> Effects => EffectsByTag;
public static bool TryGetEffect(TagType tagType, out IEnemyStatusTagEffect effect)
{
return EffectsByTag.TryGetValue(tagType, out effect);
}
}
}

View File

@ -0,0 +1,6 @@
namespace GeometryTD.Definition
{
public abstract class EnemyStatusTagStateBase
{
}
}

View File

@ -0,0 +1,9 @@
namespace GeometryTD.Definition
{
public sealed class FireTagState : EnemyStatusTagStateBase
{
public float RemainingDuration { get; set; }
public float DamagePerSecond { get; set; }
public float PendingDamage { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace GeometryTD.Definition
{
public sealed class IceTagState : EnemyStatusTagStateBase
{
public float RemainingDuration { get; set; }
public float SlowMultiplier { get; set; } = 1f;
}
}

View File

@ -0,0 +1,29 @@
using System;
namespace GeometryTD.Definition
{
public abstract class EnemyStatusTagEffectBase : IEnemyStatusTagEffect
{
public abstract TagType TagType { get; }
public virtual void Apply(HitContext hitContext, TagRuntimeData runtimeData)
{
_ = hitContext;
_ = runtimeData;
}
public virtual bool Tick(EnemyTagStatusRuntime runtime, float deltaTime, Action<int> applyDamage)
{
_ = runtime;
_ = deltaTime;
_ = applyDamage;
return false;
}
public virtual float GetMoveSpeedMultiplier(EnemyTagStatusRuntime runtime)
{
_ = runtime;
return 1f;
}
}
}

View File

@ -0,0 +1,91 @@
using System;
namespace GeometryTD.Definition
{
public sealed class FireTagEffect : EnemyStatusTagEffectBase
{
public override TagType TagType => TagType.Fire;
public override void Apply(HitContext hitContext, TagRuntimeData runtimeData)
{
if (!TagDefinitionRegistry.TryGetDefinition(TagType, out TagDefinition definition))
{
return;
}
if (hitContext == null)
{
return;
}
if (hitContext.TargetStatusRuntime == null)
{
return;
}
FireTagConfig config = definition.Config as FireTagConfig;
if (config == null)
{
return;
}
if (!config.IsImplemented)
{
return;
}
EnemyTagStatusRuntime runtime = hitContext.TargetStatusRuntime;
FireTagState state = runtime.GetOrCreateState<FireTagState>(TagType);
int effectiveStack = (int)System.Math.Max(0, System.Math.Min(runtimeData.TotalStack, config.MaxEffectiveStack));
HitStatusModifierContext modifierContext = hitContext.StatusModifierContext ?? new HitStatusModifierContext();
float burnDamagePerSecond = System.Math.Max(
0f,
config.BurnDamagePerSecondPerStack * effectiveStack + modifierContext.BonusBurnDamagePerSecond);
if (burnDamagePerSecond <= 0f)
{
return;
}
state.RemainingDuration = System.Math.Max(
state.RemainingDuration,
config.BurnDurationSeconds + modifierContext.BonusBurnDurationSeconds);
state.DamagePerSecond = System.Math.Max(state.DamagePerSecond, burnDamagePerSecond);
runtime.Activate(TagType);
}
public override bool Tick(EnemyTagStatusRuntime runtime, float deltaTime, Action<int> applyDamage)
{
FireTagState state = runtime.GetState<FireTagState>(TagType);
if (state == null)
{
return false;
}
float resolvedDeltaTime = System.Math.Max(0f, deltaTime);
if (resolvedDeltaTime <= 0f || state.RemainingDuration <= 0f)
{
return state.RemainingDuration > 0f;
}
float appliedDuration = System.Math.Min(resolvedDeltaTime, state.RemainingDuration);
state.RemainingDuration = System.Math.Max(0f, state.RemainingDuration - appliedDuration);
state.PendingDamage += state.DamagePerSecond * appliedDuration;
int resolvedDamage = (int)System.Math.Floor(state.PendingDamage);
if (resolvedDamage > 0)
{
applyDamage?.Invoke(resolvedDamage);
state.PendingDamage -= resolvedDamage;
}
if (state.RemainingDuration > 0f)
{
return true;
}
state.PendingDamage = 0f;
return false;
}
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace GeometryTD.Definition
{
public interface IEnemyStatusTagEffect
{
TagType TagType { get; }
void Apply(HitContext hitContext, TagRuntimeData runtimeData);
bool Tick(EnemyTagStatusRuntime runtime, float deltaTime, Action<int> applyDamage);
float GetMoveSpeedMultiplier(EnemyTagStatusRuntime runtime);
}
}

View File

@ -0,0 +1,63 @@
using System;
namespace GeometryTD.Definition
{
public sealed class IceTagEffect : EnemyStatusTagEffectBase
{
public override TagType TagType => TagType.Ice;
public override void Apply(HitContext hitContext, TagRuntimeData runtimeData)
{
if (hitContext == null || runtimeData == null)
{
return;
}
if (hitContext.TargetStatusRuntime == null)
{
return;
}
if (!TagDefinitionRegistry.TryGetDefinition(TagType, out TagDefinition definition))
{
return;
}
IceTagConfig config = definition.Config as IceTagConfig;
if (config == null || !config.IsImplemented)
{
return;
}
EnemyTagStatusRuntime runtime = hitContext.TargetStatusRuntime;
IceTagState state = runtime.GetOrCreateState<IceTagState>(TagType);
HitStatusModifierContext modifierContext = hitContext.StatusModifierContext ?? new HitStatusModifierContext();
float slowRatio = runtimeData.TotalStack * config.SlowRatioPerStack + modifierContext.BonusSlowRatio;
float slowMultiplier = System.Math.Max(config.MinMoveSpeedMultiplier, 1f - slowRatio);
state.RemainingDuration = System.Math.Max(
state.RemainingDuration,
config.SlowDurationSeconds + modifierContext.BonusSlowDurationSeconds);
state.SlowMultiplier = System.Math.Min(state.SlowMultiplier, slowMultiplier);
runtime.Activate(TagType);
}
public override bool Tick(EnemyTagStatusRuntime runtime, float deltaTime, Action<int> applyDamage)
{
_ = applyDamage;
IceTagState state = runtime.GetState<IceTagState>(TagType);
if (state == null)
{
return false;
}
state.RemainingDuration = System.Math.Max(0f, state.RemainingDuration - System.Math.Max(0f, deltaTime));
return state.RemainingDuration > 0f;
}
public override float GetMoveSpeedMultiplier(EnemyTagStatusRuntime runtime)
{
IceTagState state = runtime.GetState<IceTagState>(TagType);
return state == null ? 1f : state.SlowMultiplier;
}
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class AbsoluteZeroTagConfig : TagConfigBase
{
public AbsoluteZeroTagConfig(bool isImplemented) : base(isImplemented)
{
}
public float BonusSlowDurationSeconds { get; set; } = 1f;
public float BonusSlowRatioPerStack { get; set; } = 0.1f;
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class BurnSpreadTagConfig : TagConfigBase
{
public BurnSpreadTagConfig(bool isImplemented) : base(isImplemented)
{
}
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class CritTagConfig : TagConfigBase
{
public CritTagConfig(bool isImplemented) : base(isImplemented)
{
}
public float CritChancePerStack { get; set; } = 0.1f;
public float CritDamageMultiplier { get; set; } = 1.5f;
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class ExecutionTagConfig : TagConfigBase
{
public ExecutionTagConfig(bool isImplemented) : base(isImplemented)
{
}
public float TargetHealthThreshold { get; set; } = 0.3f;
public float DamageBonusPerStack { get; set; } = 0.5f;
}
}

View File

@ -0,0 +1,16 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class FireTagConfig : TagConfigBase
{
public FireTagConfig(bool isImplemented) : base(isImplemented)
{
}
public float BurnDurationSeconds { get; set; } = 3f;
public float BurnDamagePerSecondPerStack { get; set; } = 20f;
public int MaxEffectiveStack { get; set; } = 5;
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class FreezeMaskTagConfig : TagConfigBase
{
public FreezeMaskTagConfig(bool isImplemented) : base(isImplemented)
{
}
}
}

View File

@ -0,0 +1,16 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class IceTagConfig : TagConfigBase
{
public IceTagConfig(bool isImplemented) : base(isImplemented)
{
}
public float SlowDurationSeconds { get; set; } = 2f;
public float SlowRatioPerStack { get; set; } = 0.2f;
public float MinMoveSpeedMultiplier { get; set; } = 0.4f;
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class IgniteBurstTagConfig : TagConfigBase
{
public IgniteBurstTagConfig(bool isImplemented) : base(isImplemented)
{
}
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class InfernoTagConfig : TagConfigBase
{
public InfernoTagConfig(bool isImplemented) : base(isImplemented)
{
}
public float BonusBurnDurationSeconds { get; set; } = 2f;
public float BonusBurnDamagePerSecondPerStack { get; set; } = 20f;
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class OverpenetrateTagConfig : TagConfigBase
{
public OverpenetrateTagConfig(bool isImplemented) : base(isImplemented)
{
}
}
}

View File

@ -0,0 +1,12 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class PierceTagConfig : TagConfigBase
{
public PierceTagConfig(bool isImplemented) : base(isImplemented)
{
}
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class ShatterTagConfig : TagConfigBase
{
public ShatterTagConfig(bool isImplemented) : base(isImplemented)
{
}
public bool RequiresSlowedTarget { get; set; } = true;
public float DamageBonusPerStack { get; set; } = 0.25f;
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public abstract class TagConfigBase
{
public bool IsImplemented { get; set; }
protected TagConfigBase(bool isImplemented)
{
IsImplemented = isImplemented;
}
}
}

View File

@ -0,0 +1,14 @@
using System;
namespace GeometryTD.Definition
{
[Serializable]
public sealed class TagDefinition
{
public TagType TagType { get; set; }
public TagCategory Category { get; set; }
public TagTriggerPhase TriggerPhase { get; set; }
public string Description { get; set; }
public TagConfigBase Config { get; set; }
}
}

View File

@ -0,0 +1,69 @@
using System.Collections.Generic;
namespace GeometryTD.Definition
{
public static class TagDefinitionRegistry
{
private static readonly Dictionary<TagType, TagDefinition> DefinitionsByTag = CreateDefaultDefinitions();
public static IReadOnlyDictionary<TagType, TagDefinition> Definitions => DefinitionsByTag;
public static void ResetToDefaults()
{
DefinitionsByTag.Clear();
foreach (KeyValuePair<TagType, TagDefinition> pair in CreateDefaultDefinitions())
{
DefinitionsByTag.Add(pair.Key, pair.Value);
}
}
public static bool TryGetDefinition(TagType tagType, out TagDefinition definition)
{
return DefinitionsByTag.TryGetValue(tagType, out definition);
}
public static void ApplyTagRow(int tagId, bool isImplemented)
{
TagType tagType = (TagType)tagId;
if (DefinitionsByTag.TryGetValue(tagType, out TagDefinition definition) && definition.Config != null)
{
definition.Config.IsImplemented = isImplemented;
}
}
private static Dictionary<TagType, TagDefinition> CreateDefaultDefinitions()
{
return new Dictionary<TagType, TagDefinition>
{
[TagType.Fire] = CreateDefinition(TagType.Fire, TagCategory.Status, TagTriggerPhase.OnAfterHit, new FireTagConfig(true)),
[TagType.BurnSpread] = CreateDefinition(TagType.BurnSpread, TagCategory.AttackShape, TagTriggerPhase.None, new BurnSpreadTagConfig(false)),
[TagType.IgniteBurst] = CreateDefinition(TagType.IgniteBurst, TagCategory.AttackShape, TagTriggerPhase.None, new IgniteBurstTagConfig(false)),
[TagType.Inferno] = CreateDefinition(TagType.Inferno, TagCategory.StatusModifier, TagTriggerPhase.OnAfterHit, new InfernoTagConfig(true)),
[TagType.Ice] = CreateDefinition(TagType.Ice, TagCategory.Status, TagTriggerPhase.OnAfterHit, new IceTagConfig(true)),
[TagType.FreezeMask] = CreateDefinition(TagType.FreezeMask, TagCategory.AttackShape, TagTriggerPhase.None, new FreezeMaskTagConfig(false)),
[TagType.Shatter] = CreateDefinition(TagType.Shatter, TagCategory.NumericModifier, TagTriggerPhase.OnBeforeHit, new ShatterTagConfig(true)),
[TagType.AbsoluteZero] = CreateDefinition(TagType.AbsoluteZero, TagCategory.StatusModifier, TagTriggerPhase.OnAfterHit, new AbsoluteZeroTagConfig(true)),
[TagType.Pierce] = CreateDefinition(TagType.Pierce, TagCategory.AttackShape, TagTriggerPhase.None, new PierceTagConfig(false)),
[TagType.Crit] = CreateDefinition(TagType.Crit, TagCategory.NumericModifier, TagTriggerPhase.OnBeforeHit, new CritTagConfig(true)),
[TagType.Overpenetrate] = CreateDefinition(TagType.Overpenetrate, TagCategory.AttackShape, TagTriggerPhase.None, new OverpenetrateTagConfig(false)),
[TagType.Execution] = CreateDefinition(TagType.Execution, TagCategory.NumericModifier, TagTriggerPhase.OnBeforeHit, new ExecutionTagConfig(true))
};
}
private static TagDefinition CreateDefinition(
TagType tagType,
TagCategory category,
TagTriggerPhase triggerPhase,
TagConfigBase config)
{
return new TagDefinition
{
TagType = tagType,
Category = category,
TriggerPhase = triggerPhase,
Description = string.Empty,
Config = config
};
}
}
}

View File

@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
namespace GeometryTD.Definition
{
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 TagType[] GetActiveTagSnapshot()
{
if (_activeTags.Count <= 0)
{
return Array.Empty<TagType>();
}
return _activeTags.ToArray();
}
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;
}
return (TState)state;
}
public TState GetOrCreateState<TState>(TagType tagType) where TState : EnemyStatusTagStateBase, new()
{
if (_statesByTag.TryGetValue(tagType, out EnemyStatusTagStateBase existingState))
{
return (TState)existingState;
}
TState state = new TState();
_statesByTag[tagType] = state;
return state;
}
public void Tick(float deltaTime, Action<int> applyDamage)
{
float resolvedDeltaTime = System.Math.Max(0f, deltaTime);
if (resolvedDeltaTime <= 0f)
{
return;
}
for (int i = _activeTags.Count - 1; i >= 0; i--)
{
TagType tagType = _activeTags[i];
if (!EnemyStatusTagRegistry.TryGetEffect(tagType, out IEnemyStatusTagEffect effect))
{
continue;
}
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];
if (!EnemyStatusTagRegistry.TryGetEffect(tagType, out IEnemyStatusTagEffect effect))
{
continue;
}
multiplier = System.Math.Min(multiplier, effect.GetMoveSpeedMultiplier(this));
}
return multiplier;
}
}
}

View File

@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using GeometryTD.Definition;
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 TagType[] GetActiveTagSnapshot()
{
if (_activeTags.Count <= 0)
{
return Array.Empty<TagType>();
}
return _activeTags.ToArray();
}
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 = System.Math.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 = System.Math.Min(multiplier, effect.GetMoveSpeedMultiplier(this));
}
return multiplier;
}
}
}