feat(hero): 新增英雄职业定位系统,优化编队站位逻辑

1. 新增HRole枚举与角色定位配置,解耦成长类型与阵容职能
2. 重构移动系统的编队优先级计算,基于职业定位重新排序站位
3. 重写单行编队站位算法,根据队伍人数自动适配紧凑/松散排布
4. 为所有预设英雄配置对应职业定位
This commit is contained in:
pan
2026-08-12 11:04:59 +08:00
parent 426dc24899
commit 4594f673b4
4 changed files with 141 additions and 91 deletions

View File

@@ -128,6 +128,29 @@ export const HERO_GROW_COEF: Record<HGrowType, { hp: number; ap: number }> = {
[HGrowType.Support]: { hp: 1.1, ap: 0.7 },
};
/**
* 英雄位置类型(职业定位)。
* Why: grow_type 仅驱动数值成长曲线,无法表达"这个英雄在阵容里干什么"
* role 面向 UI 展示与阵容推荐(如 HeroComboSet 的铁三角校验),与成长解耦。
*/
export enum HRole {
Tank = 0, // 坦克:前排承伤,受击发动机
Warrior = 1, // 战士:前排输出/献祭位
Assassin = 2, // 刺客近战爆发主C
Archer = 3, // 射手远程物理主C
Mage = 4, // 法师:远程先手增益
Support = 5, // 辅助:光环/治疗
}
export const HRoleName: Record<HRole, string> = {
[HRole.Tank]: "坦克",
[HRole.Warrior]: "战士",
[HRole.Assassin]: "刺客",
[HRole.Archer]: "射手",
[HRole.Mage]: "法师",
[HRole.Support]: "辅助",
};
/**
* 计算英雄在目标等级下的累计属性成长增量(线性:每级加一份"基础属性×系数")。
* 公式:累计 = 基础属性 × 类型系数 × (lv - 1)。
@@ -282,6 +305,7 @@ export interface heroInfo {
card_lv?: number; // 卡牌等级(驱动 bg_node 颜色1=green 2=blue 3=purple 4=red 5=yellow
type: HType; // 攻击定位(近战/中程/远程)
grow_type?: HGrowType; // 成长类型(决定每级 hp/ap 标准增量,默认 Warrior
role?: HRole; // 位置类型/职业定位(英雄专用,怪物用 monType 表达)
monType?: MonType; // 怪物专属类型
hp: number; // 生命值上限
ap: number; // 攻击力
@@ -378,7 +402,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5001 铁壁战士 — atked 护盾标杆原5011
5001: {
uuid: 5001, name: "铁壁战士", path: "hk1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, hp: 300, ap: 28,
uuid: 5001, name: "铁壁战士", path: "hk1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 300, ap: 28,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
atked: {
6301: [
@@ -391,7 +415,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5002 影袭刺客 — atking 自身攻击
5002: {
uuid: 5002, name: "影袭刺客", path: "hc1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, hp: 300, ap: 28,
uuid: 5002, name: "影袭刺客", path: "hc1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 300, ap: 28,
skills: { 6003: { uuid: 6003, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast2].cd, ccd: 0 } },
atking: {
6401: [
@@ -404,7 +428,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5003 遗志将军 — dead 全队攻击
5003: {
uuid: 5003, name: "遗志将军", path: "hk2", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, hp: 300, ap: 28,
uuid: 5003, name: "遗志将军", path: "hk2", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 300, ap: 28,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
dead: {
6401: [
@@ -417,7 +441,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5004 破晓先知 — fstart 全队攻击
5004: {
uuid: 5004, name: "破晓先知", path: "hm1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, hp: 143, ap: 40,
uuid: 5004, name: "破晓先知", path: "hm1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 143, ap: 40,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } },
fstart: {
6401: [
@@ -430,7 +454,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5005 战歌法师 — field 攻击光环
5005: {
uuid: 5005, name: "战歌法师", path: "hm2", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, hp: 143, ap: 40,
uuid: 5005, name: "战歌法师", path: "hm2", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 143, ap: 40,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } },
field: [
{ lv: 1, uuids: [{ uuid: 7002, value: 0.1 }] },
@@ -444,7 +468,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5101 不死斗士 — atked 回血(更高数值)
5101: {
uuid: 5101, name: "不死斗士", path: "hk3", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, hp: 600, ap: 57,
uuid: 5101, name: "不死斗士", path: "hk3", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 600, ap: 57,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
atked: {
6302: [
@@ -457,7 +481,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5102 致命杀手 — atking 暴击(更高数值)
5102: {
uuid: 5102, name: "致命杀手", path: "hc2", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, hp: 600, ap: 57,
uuid: 5102, name: "致命杀手", path: "hc2", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 600, ap: 57,
skills: { 6003: { uuid: 6003, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast2].cd, ccd: 0 } },
atking: {
6403: [
@@ -470,7 +494,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5103 殉道勇士 — dead 全队护盾(更高数值)
5103: {
uuid: 5103, name: "殉道勇士", path: "hk4", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, hp: 600, ap: 57,
uuid: 5103, name: "殉道勇士", path: "hk4", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 600, ap: 57,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
dead: {
6301: [
@@ -483,7 +507,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5104 圣光祭司 — fstart 全队护盾(更高数值)
5104: {
uuid: 5104, name: "圣光祭司", path: "hm3", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, hp: 287, ap: 80,
uuid: 5104, name: "圣光祭司", path: "hm3", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 287, ap: 80,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow1].cd, ccd: 0 } },
fstart: {
6301: [
@@ -496,7 +520,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5105 风怒先知 — field 攻速光环
5105: {
uuid: 5105, name: "风怒先知", path: "hm1", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, hp: 287, ap: 80,
uuid: 5105, name: "风怒先知", path: "hm1", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 287, ap: 80,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
field: [
{ lv: 1, uuids: [{ uuid: 7006, value: 0.1 }] },
@@ -507,7 +531,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5106 破甲射手 — atking 自身穿透伤害计时buff增强
5106: {
uuid: 5106, name: "破甲射手", path: "ha1", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, hp: 287, ap: 80,
uuid: 5106, name: "破甲射手", path: "ha1", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 287, ap: 80,
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
atking: {
6512: [
@@ -523,7 +547,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5201 怒血勇士 — atked 全队攻击(范围扩大)
5201: {
uuid: 5201, name: "怒血勇士", path: "hk5", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, hp: 900, ap: 85,
uuid: 5201, name: "怒血勇士", path: "hk5", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 900, ap: 85,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
atked: {
6401: [
@@ -536,7 +560,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5202 援护射手 — atking 队友攻击(范围扩大)
5202: {
uuid: 5202, name: "援护射手", path: "ha1", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, hp: 430, ap: 120,
uuid: 5202, name: "援护射手", path: "ha1", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 430, ap: 120,
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal2].cd, ccd: 0 } },
atking: {
6401: [
@@ -549,7 +573,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5203 传承卫士 — dead 全队生命(范围扩大)
5203: {
uuid: 5203, name: "传承卫士", path: "hk1", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, hp: 900, ap: 85,
uuid: 5203, name: "传承卫士", path: "hk1", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 900, ap: 85,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
dead: {
6402: [
@@ -562,7 +586,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5204 晨光贤者 — fstart 全队暴击(范围扩大)
5204: {
uuid: 5204, name: "晨光贤者", path: "hm2", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, hp: 430, ap: 120,
uuid: 5204, name: "晨光贤者", path: "hm2", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 430, ap: 120,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } },
fstart: {
6403: [
@@ -575,7 +599,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5205 精准术士 — field 暴击光环(范围扩大)
5205: {
uuid: 5205, name: "精准术士", path: "hm3", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, hp: 430, ap: 120,
uuid: 5205, name: "精准术士", path: "hm3", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 430, ap: 120,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
field: [
{ lv: 1, uuids: [{ uuid: 7004, value: 0.1 }] },
@@ -589,7 +613,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5301 复仇守护者 — atked 全队暴击(范围+数值)
5301: {
uuid: 5301, name: "复仇守护者", path: "hk2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, hp: 1200, ap: 113,
uuid: 5301, name: "复仇守护者", path: "hk2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 1200, ap: 113,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow1].cd, ccd: 0 } },
atked: {
6403: [
@@ -602,7 +626,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5302 破灭游侠 — atking 队友暴伤(范围+数值)
5302: {
uuid: 5302, name: "破灭游侠", path: "ha2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, hp: 573, ap: 160,
uuid: 5302, name: "破灭游侠", path: "ha2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 573, ap: 160,
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal1].cd, ccd: 0 } },
atking: {
6404: [
@@ -615,7 +639,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5303 亡语刺客 — dead 全队暴击+暴伤(双效果)
5303: {
uuid: 5303, name: "亡语刺客", path: "hc3", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, hp: 1200, ap: 113,
uuid: 5303, name: "亡语刺客", path: "hc3", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 1200, ap: 113,
skills: { 6003: { uuid: 6003, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast3].cd, ccd: 0 } },
dead: {
6403: [
@@ -633,7 +657,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5304 战吼术士 — fstart 全队攻击+生命(双效果)
5304: {
uuid: 5304, name: "战吼术士", path: "hm1", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, hp: 573, ap: 160,
uuid: 5304, name: "战吼术士", path: "hm1", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 573, ap: 160,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid1].cd, ccd: 0 } },
fstart: {
6401: [
@@ -651,7 +675,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5305 领域贤者 — field 暴伤+攻速光环(双效果)
5305: {
uuid: 5305, name: "领域贤者", path: "hm2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, hp: 573, ap: 160,
uuid: 5305, name: "领域贤者", path: "hm2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 573, ap: 160,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
field: [
{ lv: 1, uuids: [{ uuid: 7005, value: 0.2 }, { uuid: 7006, value: 0.1 }] },
@@ -665,7 +689,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5401 不灭战魂 — atked 攻击 + revive双技能
5401: {
uuid: 5401, name: "不灭战魂", path: "hk3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, hp: 1500, ap: 142,
uuid: 5401, name: "不灭战魂", path: "hk3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 1500, ap: 142,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
atked: {
6401: [
@@ -683,7 +707,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5402 战歌游侠 — atking 攻击 + field 攻速光环(双技能)
5402: {
uuid: 5402, name: "战歌游侠", path: "ha3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, hp: 717, ap: 200,
uuid: 5402, name: "战歌游侠", path: "ha3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 717, ap: 200,
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast3].cd, ccd: 0 } },
atking: {
6401: [
@@ -701,7 +725,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5403 凤凰祭司 — dead 攻击+护盾 + revive双技能
5403: {
uuid: 5403, name: "凤凰祭司", path: "hk4", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, hp: 1500, ap: 142,
uuid: 5403, name: "凤凰祭司", path: "hk4", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 1500, ap: 142,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
dead: {
6401: [
@@ -724,7 +748,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5404 涅槃贤者 — fstart 护盾+攻击 + revive双技能
5404: {
uuid: 5404, name: "涅槃贤者", path: "hm3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, hp: 717, ap: 200,
uuid: 5404, name: "涅槃贤者", path: "hm3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 717, ap: 200,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal3].cd, ccd: 0 } },
fstart: {
6301: [
@@ -747,7 +771,7 @@ export const HeroInfo: Record<number, heroInfo> = {
},
// 5405 圣言诗人 — atking 治疗 + field 生命光环(双技能)
5405: {
uuid: 5405, name: "圣言诗人", path: "hh1", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Support, hp: 717, ap: 200,
uuid: 5405, name: "圣言诗人", path: "hh1", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Support, role: HRole.Support, hp: 717, ap: 200,
skills: { 6015: { uuid: 6015, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal2].cd, ccd: 0 } },
atking: {
6302: [

View File

@@ -120,6 +120,7 @@ export class Hero extends ecs.Entity {
model.base_card_lv = hero.card_lv ?? 1;
model.type = hero.type;
model.grow_type = hero.grow_type;
model.role = hero.role;
model.fac = FacSet.HERO;
model.dis = hero.dis ?? 720;
model.posIndex = posIndex;

View File

@@ -1,5 +1,5 @@
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { HeroDisVal, HeroInfo, HSkillInfo, HType, SkillTriggerType, TriggerGrouped, LvFieldEntry, LvReviveEntry, LvBonusEntry, FieldEntry } from "../common/config/heroSet";
import { HeroDisVal, HeroInfo, HSkillInfo, HType, SkillTriggerType, TriggerGrouped, LvFieldEntry, LvReviveEntry, LvBonusEntry, FieldEntry, HRole } from "../common/config/heroSet";
import { mLogger } from "../common/Logger";
import { FacSet, FightSet } from "../common/config/GameSet";
import { FieldSkillSet, FieldSkillType, SkillOverrides } from "../common/config/SkillSet";
@@ -29,6 +29,7 @@ export class HeroAttrsComp extends ecs.Comp {
type: number = 0; // 0近战 1远程 2辅助
fac: number = 0; // 0:hero 1:monster
grow_type?: number; // 成长类型HGrowType驱动每级 hp/ap 线性成长
role?: HRole; // 位置类型/职业定位(驱动编队站位优先级)
// ==================== 基础属性(有初始值) ====================
base_ap: number = 0; // 原始基础攻击(无任何加成)
base_hp: number = 0; // 原始基础血量(无任何加成)
@@ -563,6 +564,8 @@ export class HeroAttrsComp extends ecs.Comp {
this.lv = 1;
this.type = 0;
this.fac = 0;
this.grow_type = undefined;
this.role = undefined;
this.base_ap = 0;
this.base_hp = 0;
this.ap = 0;

View File

@@ -3,7 +3,7 @@ import { HeroViewComp } from "./HeroViewComp";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { smc } from "../common/SingletonModuleComp";
import { BoxSet, FacSet } from "../common/config/GameSet";
import { HeroDisVal, HType } from "../common/config/heroSet";
import { HeroDisVal, HType, HRole } from "../common/config/heroSet";
import { BoxCollider2D, Node } from "cc";
import { MonMoveComp } from "./MonMoveComp";
@@ -46,8 +46,6 @@ interface MoveFacConfig {
retreatBackX: number;
}
import { MissionHeroComp } from "../map/MissionHeroComp";
@ecs.register('MoveSystem')
export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
private readonly heroFrontAnchorX = -200;
@@ -106,7 +104,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
update(e: ecs.Entity) {
/** 战斗未开始/暂停时不驱动移动 */
if (!smc.mission.play || smc.mission.pause) return;
const model = e.get(HeroAttrsComp);
const move = e.get(MoveComp);
const view = e.get(HeroViewComp);
@@ -125,55 +123,37 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
move.baseY = slot.targetY;
move.targetX = slot.targetX;
// 战斗阶段不移动
const canMove = !smc.mission.in_fight;
// 2. 平滑 Y 轴换路
// 2. 平滑 Y 轴换路(不分阶段,立即响应编队变化)
let isChangingLane = false;
if (canMove) {
if (Math.abs(view.node.position.y - move.baseY) > 2) {
const currentY = view.node.position.y;
const deltaY = move.baseY - currentY;
const step = 400 * this.dt; // 换路速度
const newY = currentY + Math.sign(deltaY) * Math.min(Math.abs(deltaY), step);
view.node.setPosition(view.node.position.x, newY, 0);
isChangingLane = true;
} else {
view.node.setPosition(view.node.position.x, move.baseY, 0);
}
if (Math.abs(view.node.position.y - move.baseY) > 2) {
const currentY = view.node.position.y;
const deltaY = move.baseY - currentY;
const step = 400 * this.dt; // 换路速度
const newY = currentY + Math.sign(deltaY) * Math.min(Math.abs(deltaY), step);
view.node.setPosition(view.node.position.x, newY, 0);
isChangingLane = true;
} else {
view.node.setPosition(view.node.position.x, move.baseY, 0);
}
// 渲染层级重排
this.updateRenderOrder();
const nearestEnemy = this.findNearestEnemy(e);
if (nearestEnemy) {
/** 有敌人:进入战斗位移逻辑 */
if (canMove) {
this.processCombatLogic(e, move, view, model, nearestEnemy);
} else {
model.is_atking = true; // 战斗中不移动,但保持攻击状态
}
/** 有敌人:进入战斗位移逻辑(立即向编队目标移动) */
this.processCombatLogic(e, move, view, model, nearestEnemy);
this.syncCombatTarget(model, view, nearestEnemy);
} else {
/** 无敌人:清目标并回归编队站位 */
this.clearCombatTarget(model);
if (canMove) {
this.moveToSlot(view, move, model, move.targetX);
}
this.moveToSlot(view, move, model, move.targetX);
model.is_atking = false;
}
if (canMove) {
// 如果只在 Y 轴移动,也要播放 move 动画
if (isChangingLane && view.status !== "move" && view.status !== "atk") {
view.status_change("move");
}
} else {
// 战斗阶段如果不移动,确保不在攻击时恢复 idle 状态
if (!model.is_atking && view.status === "move") {
view.status_change("idle");
}
// 如果只在 Y 轴移动,也要播放 move 动画
if (isChangingLane && view.status !== "move" && view.status !== "atk") {
view.status_change("move");
}
}
@@ -252,14 +232,10 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
allAllies.sort((a, b) => {
const attrsA = a.get(HeroAttrsComp);
const attrsB = b.get(HeroAttrsComp);
const priorityA = attrsA ? this.getCombatPriority(attrsA) : 0;
const priorityB = attrsB ? this.getCombatPriority(attrsB) : 0;
const priorityA = attrsA ? this.getFormationPriority(attrsA) : 0;
const priorityB = attrsB ? this.getFormationPriority(attrsB) : 0;
if (priorityA !== priorityB) return priorityB - priorityA;
const deadA = (attrsA?.dead && attrsA.dead.length > 0) ? 1 : 0;
const deadB = (attrsB?.dead && attrsB.dead.length > 0) ? 1 : 0;
if (deadA !== deadB) return deadB - deadA;
const lvA = attrsA?.lv ?? 1;
const lvB = attrsB?.lv ?? 1;
if (lvA !== lvB) return lvB - lvA;
@@ -271,20 +247,48 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
return a.eid - b.eid;
});
const slotIndex = Math.max(0, allAllies.findIndex(entity => entity === self));
// 优先中前(1) -> 上前(0) -> 下前(2) -> 中后(4) -> 上后(3) -> 下后(5)
const slotPriority = [1, 0, 2, 4, 3, 5];
const posIndex = slotPriority[slotIndex % 6];
const pos = MissionHeroComp.HERO_POSITIONS[posIndex];
return { targetX: pos.x, targetY: pos.y };
// allAllies 按优先级降序(索引 0 = 最高优先级 = 最靠右/最先接敌),
// 反转为"从左侧起的排位"后再分配 X最高优先级 → 最右
const priorityRank = Math.max(0, allAllies.findIndex(entity => entity === self));
const rankFromLeft = allAllies.length - 1 - priorityRank;
const targetX = this.calcSlotX(rankFromLeft, allAllies.length);
return { targetX, targetY: BoxSet.GAME_LINE };
}
// ==================== 编队站位(方案 A单行横向分布 ====================
/** 编队左端起点(最靠后/最后接敌) */
private static readonly FORMATION_LEFT_X = -320;
/** 编队右端终点(最靠前/最先接敌) */
private static readonly FORMATION_RIGHT_X = 100;
/** 固定步进间距(人数较少时按此从左侧逐位右排) */
private static readonly FORMATION_STEP_X = 60;
/**
* 计算某个槽位的目标 X。
* Why: 单行站位避免横版拥挤——人少时按 60 固定步进从 -320 起步逐位右排(保持松散),
* 人多到 60 步进装不下时,改为在 [-320, 100] 区间均分(利用全宽)。
* @param rankFromLeft 从左侧起的排位0=最靠左/最后,越大越靠右/越先接敌)
* @param total 当前存活英雄总数
*/
private calcSlotX(rankFromLeft: number, total: number): number {
const left = MoveSystem.FORMATION_LEFT_X;
const right = MoveSystem.FORMATION_RIGHT_X;
const step = MoveSystem.FORMATION_STEP_X;
if (total <= 1) return left;
// 固定步进能容纳的最大人数:相邻 60且最右不超过 right
const maxStepCount = Math.floor((right - left) / step) + 1;
if (total <= maxStepCount) {
return left + rankFromLeft * step;
}
// 超出后均分整段区间(含左右端点)
return left + (right - left) * (rankFromLeft / (total - 1));
}
private moveToSlot(view: HeroViewComp, move: MoveComp, model: HeroAttrsComp, targetX: number) {
const currentX = view.node.position.x;
const currentY = view.node.position.y;
// 当 X 和 Y 都到达目标时,才算真正到达
if (Math.abs(currentX - targetX) <= 2 && Math.abs(currentY - move.baseY) <= 2) {
view.node.setPosition(targetX, move.baseY, 0);
@@ -307,7 +311,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
const moveMaxX = Math.max(cfg.moveBackX, cfg.moveFrontX);
const currentX = view.node.position.x;
const currentY = view.node.position.y;
const delta = speed * this.dt * direction;
let newX = view.node.position.x + delta;
if (currentX < moveMinX && direction < 0) {
@@ -345,12 +349,30 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
view.status_change("move");
}
private getCombatPriority(model: HeroAttrsComp): number {
/** 数值越大越靠前:近战 > 中程 > 远程 */
/**
* 编队站位优先级(数值越大越靠前/越靠右)。
* Why: 阵容职能决定承伤顺序——死亡触发英雄需最先阵亡以触发遗志,
* 其次坦克承伤,再次战士/刺客,远程射手/法师/辅助依次靠后保护。
* 排序档位:死亡触发 > 坦克 > 战士 > 刺客 > 射手 > 法师 > 辅助
*/
private getFormationPriority(model: HeroAttrsComp): number {
// 死亡触发英雄最高优先:需要先死以发动遗志/献祭
if (model.dead && Object.keys(model.dead).length > 0) return 100;
if (model.role !== undefined) {
switch (model.role) {
case HRole.Tank: return 90;
case HRole.Warrior: return 80;
case HRole.Assassin: return 70;
case HRole.Archer: return 60;
case HRole.Mage: return 50;
case HRole.Support: return 40;
}
}
// 兜底:未配置 role 时按攻击定位(近战 > 中程 > 远程)
const rangeType = model.type as HType.Melee | HType.Mid | HType.Long;
if (rangeType === HType.Melee) return 3;
if (rangeType === HType.Mid) return 2;
return 1;
if (rangeType === HType.Melee) return 30;
if (rangeType === HType.Mid) return 20;
return 10;
}
private resolveCombatRange(model: HeroAttrsComp, defaultMin: number, defaultMax: number): [number, number] {
@@ -364,13 +386,13 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null {
const currentView = entity.get(HeroViewComp);
if (!currentView?.node) return null;
const currentPos = currentView.node.position;
const myFac = entity.get(HeroAttrsComp).fac;
let nearest: HeroViewComp | null = null;
let minDis = Infinity;
/** 遍历筛出最近敌人:以 X 轴距离为主Y 轴距离作为同排的决胜权重,使角色优先攻击同路的敌人 */
ecs.query(this.getHeroViewMatcher()).forEach(e => {
const m = e.get(HeroAttrsComp);
@@ -405,7 +427,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
const actorView = e.get(HeroViewComp);
if (!attrs || !actorView?.node || attrs.is_dead) return;
if (attrs.fac !== FacSet.HERO && attrs.fac !== FacSet.MON) return;
if (actorView.node.parent !== actorRoot) {
actorView.node.parent = actorRoot;
}