补全 CombatNode 逻辑 + 重构 EnemyManager

- 添加了关卡内的难度系数与掉落
    - 难度系数:每循环一轮波次敌人血量翻倍
    - 道具掉落:按照掉落概率曲线根据波次计算当前爆率,并从新增的道具池里选择道具
    - `CombatResourceManager`:统一维护关卡内资源状态(硬币/金币/道具掉落)
- 重构 `EnemyManager`:
    - `EnemyManager`:编排子服务,不承载具体业务细节
    - `EnemySpawnDirector`:管理刷怪时序
    - `SpawnerResolver`:管理出生点与路径
    - `EnemyLifecycleTracker`:追踪敌人生命周期
    - `EnemyConfigService`:管理敌人配置与倍率
This commit is contained in:
SepComet
2026-03-02 17:23:34 +08:00
parent c576224991
commit 5ba94828a8
49 changed files with 1763 additions and 1047 deletions
@@ -28,10 +28,15 @@ namespace GeometryTD.DataTable
public string Name { get; private set; }
/// <summary>
/// 获取底座组件攻击速度数组
/// 获取底座组件攻击速度数组(秒/次)
/// </summary>
public float[] AttackSpeed { get; private set; }
/// <summary>
/// 获取底座组件每级提升攻击速度(值为负数)
/// </summary>
public float AttackSpeedPerLevel { get; private set; }
/// <summary>
/// 获取攻击属性
/// </summary>
@@ -62,6 +67,7 @@ namespace GeometryTD.DataTable
EntityId = int.Parse(columnStrings[index++]);
Name = columnStrings[index++];
AttackSpeed = GenerateAttackSpeed(columnStrings[index++]);
AttackSpeedPerLevel = float.Parse(columnStrings[index++]);
AttackPropertyType = EnumUtility<AttackPropertyType>.Get(columnStrings[index++]);
Constraint = columnStrings[index++];
PossibleTag = GeneratePossibleTag(columnStrings[index++]);
@@ -32,11 +32,21 @@ namespace GeometryTD.DataTable
/// </summary>
public float[] RotateSpeed { get; private set; }
/// <summary>
/// 获取轴承组件每级提升旋转速度
/// </summary>
public float RotateSpeedPerLevel { get; private set; }
/// <summary>
/// 获取攻击范围
/// </summary>
public float[] AttackRange { get; private set; }
/// <summary>
/// 获取每级提升攻击范围
/// </summary>
public float AttackRangePerLevel { get; private set; }
/// <summary>
/// 获取属性约束
/// </summary>
@@ -62,7 +72,9 @@ namespace GeometryTD.DataTable
EntityId = int.Parse(columnStrings[index++]);
Name = columnStrings[index++];
RotateSpeed = GenerateFloatArray(columnStrings[index++]);
RotateSpeedPerLevel = float.Parse(columnStrings[index++]);
AttackRange = GenerateFloatArray(columnStrings[index++]);
AttackRangePerLevel = float.Parse(columnStrings[index++]);
Constraint = columnStrings[index++];
PossibleTag = GeneratePossibleTag(columnStrings[index++]);
@@ -32,6 +32,11 @@ namespace GeometryTD.DataTable
/// </summary>
public int[] AttackDamage { get; private set; }
/// <summary>
/// 获取枪口组件每级提升攻击伤害值
/// </summary>
public int AttackDamagePerLevel { get; private set; }
/// <summary>
/// 获取攻击伤害浮动
/// </summary>
@@ -67,6 +72,7 @@ namespace GeometryTD.DataTable
EntityId = int.Parse(columnStrings[index++]);
Name = columnStrings[index++];
AttackDamage = GenerateAttackDamage(columnStrings[index++]);
AttackDamagePerLevel = int.Parse(columnStrings[index++]);
DamageRandomRate = float.Parse(columnStrings[index++]);
AttackMethodType = EnumUtility<AttackMethodType>.Get(columnStrings[index++]);
Constraint = columnStrings[index++];
@@ -0,0 +1,72 @@
using GeometryTD.CustomUtility;
using GeometryTD.Definition;
using UnityGameFramework.Runtime;
namespace GeometryTD.DataTable
{
/// <summary>
/// 局外道具掉落池配置表。
/// </summary>
public class DROutGameDropPool : DataRowBase
{
private int m_Id = 0;
public override int Id => m_Id;
public LevelThemeType LevelThemeType { get; private set; }
public RarityType Rarity { get; private set; }
public string ItemType { get; private set; }
public int ItemId { get; private set; }
public int Weight { get; private set; }
public int MinPhase { get; private set; }
public int MaxPhase { get; private set; }
public override bool ParseDataRow(string dataRowString, object userData)
{
string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators);
for (int i = 0; i < columnStrings.Length; i++)
{
columnStrings[i] = columnStrings[i].Trim(DataTableExtension.DataTrimSeparators);
}
int index = 0;
index++;
m_Id = int.Parse(columnStrings[index++]);
index++;
LevelThemeType = EnumUtility<LevelThemeType>.Get(columnStrings[index++]);
Rarity = EnumUtility<RarityType>.Get(columnStrings[index++]);
ItemType = columnStrings[index++];
ItemId = ParseIntOrDefault(columnStrings[index++], 0);
Weight = ParseIntOrDefault(columnStrings[index++], 1);
MinPhase = ParseIntOrDefault(columnStrings[index++], 1);
MaxPhase = ParseIntOrDefault(columnStrings[index++], int.MaxValue);
if (Weight <= 0)
{
Weight = 1;
}
if (MinPhase <= 0)
{
MinPhase = 1;
}
if (MaxPhase < MinPhase)
{
MaxPhase = MinPhase;
}
return true;
}
private static int ParseIntOrDefault(string raw, int fallbackValue)
{
if (int.TryParse(raw, out int parsed))
{
return parsed;
}
return fallbackValue;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a06af49028f1aa47acede2e0651f2c3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: