feat(skill): add round end skill trigger system
新增回合结束(FEnd)类型的技能触发机制,包括: 1. 新增SkillTriggerType.FEnd枚举与相关配置映射 2. 为英雄、怪物属性组件添加回合结束技能的运行时解析与存储 3. 实现关卡节间流转时触发全队英雄回合结束技能的逻辑 4. 完善战力计算、技能图标检索等配套支持逻辑
This commit is contained in:
@@ -199,6 +199,7 @@ export enum SkillTriggerType {
|
|||||||
Call = "call", // 召唤后触发 降临
|
Call = "call", // 召唤后触发 降临
|
||||||
Dead = "dead", // 死亡后触发 遗志
|
Dead = "dead", // 死亡后触发 遗志
|
||||||
FStart = "fstart", // 战斗开始 先手
|
FStart = "fstart", // 战斗开始 先手
|
||||||
|
FEnd = "fend", // 回合结束 收官
|
||||||
Field = "field", // 驻场 光环
|
Field = "field", // 驻场 光环
|
||||||
Atking = "atking", // 攻击后触发 追击
|
Atking = "atking", // 攻击后触发 追击
|
||||||
Atked = "atked", // 受击后触发 反击
|
Atked = "atked", // 受击后触发 反击
|
||||||
@@ -209,6 +210,7 @@ export const SkillTriggerName = {
|
|||||||
[SkillTriggerType.Call]: "召唤",
|
[SkillTriggerType.Call]: "召唤",
|
||||||
[SkillTriggerType.Dead]: "死亡后",
|
[SkillTriggerType.Dead]: "死亡后",
|
||||||
[SkillTriggerType.FStart]: "回合开始",
|
[SkillTriggerType.FStart]: "回合开始",
|
||||||
|
[SkillTriggerType.FEnd]: "回合结束",
|
||||||
[SkillTriggerType.Field]: "光环",
|
[SkillTriggerType.Field]: "光环",
|
||||||
[SkillTriggerType.Atking]: "攻击",
|
[SkillTriggerType.Atking]: "攻击",
|
||||||
[SkillTriggerType.Atked]: "受伤",
|
[SkillTriggerType.Atked]: "受伤",
|
||||||
@@ -219,6 +221,7 @@ export const SkillTriggerDesc = {
|
|||||||
[SkillTriggerType.Call]: "召唤时",
|
[SkillTriggerType.Call]: "召唤时",
|
||||||
[SkillTriggerType.Dead]: "死亡时",
|
[SkillTriggerType.Dead]: "死亡时",
|
||||||
[SkillTriggerType.FStart]: "战斗开始时",
|
[SkillTriggerType.FStart]: "战斗开始时",
|
||||||
|
[SkillTriggerType.FEnd]: "回合结束时",
|
||||||
[SkillTriggerType.Field]: "光环",
|
[SkillTriggerType.Field]: "光环",
|
||||||
[SkillTriggerType.Atking]: "攻击n次",
|
[SkillTriggerType.Atking]: "攻击n次",
|
||||||
[SkillTriggerType.Atked]: "受伤n次",
|
[SkillTriggerType.Atked]: "受伤n次",
|
||||||
@@ -351,7 +354,7 @@ export function getHeroSkillIcon(hero: heroInfo | undefined, skillUuid: number,
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
// 自动定位:再各触发组,最后光环
|
// 自动定位:再各触发组,最后光环
|
||||||
for (const type of [SkillTriggerType.Call, SkillTriggerType.Dead, SkillTriggerType.FStart, SkillTriggerType.Atking, SkillTriggerType.Atked]) {
|
for (const type of [SkillTriggerType.Call, SkillTriggerType.Dead, SkillTriggerType.FStart, SkillTriggerType.FEnd, SkillTriggerType.Atking, SkillTriggerType.Atked]) {
|
||||||
const grouped = hero[type] as TriggerGrouped | undefined;
|
const grouped = hero[type] as TriggerGrouped | undefined;
|
||||||
const entry = grouped?.[skillUuid]?.[0];
|
const entry = grouped?.[skillUuid]?.[0];
|
||||||
if (entry) return resolveTriggerSkillIcon(entry, skillUuid);
|
if (entry) return resolveTriggerSkillIcon(entry, skillUuid);
|
||||||
@@ -395,6 +398,7 @@ export interface heroInfo {
|
|||||||
[SkillTriggerType.Call]?: TriggerGrouped; // 召唤后触发的技能配置(按 s_uuid 分组,每组按 lv 升序)
|
[SkillTriggerType.Call]?: TriggerGrouped; // 召唤后触发的技能配置(按 s_uuid 分组,每组按 lv 升序)
|
||||||
[SkillTriggerType.Dead]?: TriggerGrouped; // 死亡后触发的技能配置
|
[SkillTriggerType.Dead]?: TriggerGrouped; // 死亡后触发的技能配置
|
||||||
[SkillTriggerType.FStart]?: TriggerGrouped; // 战斗开始时释放的技能配置
|
[SkillTriggerType.FStart]?: TriggerGrouped; // 战斗开始时释放的技能配置
|
||||||
|
[SkillTriggerType.FEnd]?: TriggerGrouped; // 回合结束时释放的技能配置
|
||||||
[SkillTriggerType.Field]?: LvFieldEntry[]; // 驻场光环(按 lv 升序,运行时取 ≤英雄等级 的最大档)
|
[SkillTriggerType.Field]?: LvFieldEntry[]; // 驻场光环(按 lv 升序,运行时取 ≤英雄等级 的最大档)
|
||||||
[SkillTriggerType.Atking]?: TriggerGrouped; // 普通攻击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的普攻次数
|
[SkillTriggerType.Atking]?: TriggerGrouped; // 普通攻击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的普攻次数
|
||||||
[SkillTriggerType.Atked]?: TriggerGrouped; // 受击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的受击次数
|
[SkillTriggerType.Atked]?: TriggerGrouped; // 受击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的受击次数
|
||||||
@@ -1235,6 +1239,7 @@ export const HERO_POWER_WEIGHTS = {
|
|||||||
W_atking: 1.0,
|
W_atking: 1.0,
|
||||||
W_atked: 0.8,
|
W_atked: 0.8,
|
||||||
W_fstart: 1.0,
|
W_fstart: 1.0,
|
||||||
|
W_fend: 1.0,
|
||||||
W_dead: 0.6,
|
W_dead: 0.6,
|
||||||
W_field_alive: 1.0,
|
W_field_alive: 1.0,
|
||||||
W_revive: 1.0,
|
W_revive: 1.0,
|
||||||
@@ -1407,6 +1412,7 @@ function getTriggerCount(
|
|||||||
case SkillTriggerType.Atked:
|
case SkillTriggerType.Atked:
|
||||||
return getHitCount(seconds) / t_num;
|
return getHitCount(seconds) / t_num;
|
||||||
case SkillTriggerType.FStart:
|
case SkillTriggerType.FStart:
|
||||||
|
case SkillTriggerType.FEnd:
|
||||||
return getWaveCount(seconds);
|
return getWaveCount(seconds);
|
||||||
case SkillTriggerType.Dead:
|
case SkillTriggerType.Dead:
|
||||||
// 死亡触发次数 = 回合数 × (1 + 最大复活次数),复活从 dead 配置中提取
|
// 死亡触发次数 = 回合数 × (1 + 最大复活次数),复活从 dead 配置中提取
|
||||||
@@ -1548,6 +1554,7 @@ export interface HeroPowerBreakdown {
|
|||||||
atking: number;
|
atking: number;
|
||||||
atked: number;
|
atked: number;
|
||||||
fstart: number;
|
fstart: number;
|
||||||
|
fend: number;
|
||||||
dead: number;
|
dead: number;
|
||||||
field: number;
|
field: number;
|
||||||
revive: number;
|
revive: number;
|
||||||
@@ -1596,6 +1603,9 @@ function calcTriggerPowerDetailed(
|
|||||||
case SkillTriggerType.FStart:
|
case SkillTriggerType.FStart:
|
||||||
weight = weights.W_fstart;
|
weight = weights.W_fstart;
|
||||||
break;
|
break;
|
||||||
|
case SkillTriggerType.FEnd:
|
||||||
|
weight = weights.W_fend;
|
||||||
|
break;
|
||||||
case SkillTriggerType.Dead:
|
case SkillTriggerType.Dead:
|
||||||
weight = weights.W_dead;
|
weight = weights.W_dead;
|
||||||
break;
|
break;
|
||||||
@@ -1693,6 +1703,7 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco
|
|||||||
const atking = hero.atking ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.atking, targetLv), hero, targetLv, SkillTriggerType.Atking, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
const atking = hero.atking ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.atking, targetLv), hero, targetLv, SkillTriggerType.Atking, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
||||||
const atked = hero.atked ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.atked, targetLv), hero, targetLv, SkillTriggerType.Atked, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
const atked = hero.atked ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.atked, targetLv), hero, targetLv, SkillTriggerType.Atked, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
||||||
const fstart = hero.fstart ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.fstart, targetLv), hero, targetLv, SkillTriggerType.FStart, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
const fstart = hero.fstart ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.fstart, targetLv), hero, targetLv, SkillTriggerType.FStart, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
||||||
|
const fend = hero.fend ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.fend, targetLv), hero, targetLv, SkillTriggerType.FEnd, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
||||||
const dead = hero.dead ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.dead, targetLv), hero, targetLv, SkillTriggerType.Dead, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
const dead = hero.dead ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.dead, targetLv), hero, targetLv, SkillTriggerType.Dead, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
||||||
const fieldEntries = resolveFieldByLv(hero.field, targetLv);
|
const fieldEntries = resolveFieldByLv(hero.field, targetLv);
|
||||||
const field = fieldEntries.length > 0 ? calcFieldPowerDetailed(fieldEntries, hero, targetLv, seconds) : { total: 0, items: [] as FieldSkillEval[] };
|
const field = fieldEntries.length > 0 ? calcFieldPowerDetailed(fieldEntries, hero, targetLv, seconds) : { total: 0, items: [] as FieldSkillEval[] };
|
||||||
@@ -1708,7 +1719,7 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const total = hpValue + dpsValue + atking.total + atked.total + fstart.total + dead.total + field.total + reviveValue;
|
const total = hpValue + dpsValue + atking.total + atked.total + fstart.total + fend.total + dead.total + field.total + reviveValue;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
total,
|
total,
|
||||||
@@ -1718,11 +1729,12 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco
|
|||||||
atking: atking.total,
|
atking: atking.total,
|
||||||
atked: atked.total,
|
atked: atked.total,
|
||||||
fstart: fstart.total,
|
fstart: fstart.total,
|
||||||
|
fend: fend.total,
|
||||||
dead: dead.total,
|
dead: dead.total,
|
||||||
field: field.total,
|
field: field.total,
|
||||||
revive: reviveValue,
|
revive: reviveValue,
|
||||||
},
|
},
|
||||||
triggerSkills: [...atking.items, ...atked.items, ...fstart.items, ...dead.items],
|
triggerSkills: [...atking.items, ...atked.items, ...fstart.items, ...fend.items, ...dead.items],
|
||||||
fieldSkills: field.items,
|
fieldSkills: field.items,
|
||||||
normalAttack,
|
normalAttack,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ export class Hero extends ecs.Entity {
|
|||||||
model.runtime_call = resolveTriggerByLv(model.call, hero_lv);
|
model.runtime_call = resolveTriggerByLv(model.call, hero_lv);
|
||||||
model.runtime_dead = resolveTriggerByLv(model.dead, hero_lv);
|
model.runtime_dead = resolveTriggerByLv(model.dead, hero_lv);
|
||||||
model.runtime_fstart = resolveTriggerByLv(model.fstart, hero_lv);
|
model.runtime_fstart = resolveTriggerByLv(model.fstart, hero_lv);
|
||||||
|
model.runtime_fend = resolveTriggerByLv(model.fend, hero_lv);
|
||||||
model.runtime_atking = resolveTriggerByLv(model.atking, hero_lv);
|
model.runtime_atking = resolveTriggerByLv(model.atking, hero_lv);
|
||||||
model.runtime_atked = resolveTriggerByLv(model.atked, hero_lv);
|
model.runtime_atked = resolveTriggerByLv(model.atked, hero_lv);
|
||||||
model.runtime_field = resolveFieldByLv(model.field, hero_lv);
|
model.runtime_field = resolveFieldByLv(model.field, hero_lv);
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export class HeroAttrsComp extends ecs.Comp {
|
|||||||
[SkillTriggerType.Call]?: TriggerGrouped;
|
[SkillTriggerType.Call]?: TriggerGrouped;
|
||||||
[SkillTriggerType.Dead]?: TriggerGrouped;
|
[SkillTriggerType.Dead]?: TriggerGrouped;
|
||||||
[SkillTriggerType.FStart]?: TriggerGrouped;
|
[SkillTriggerType.FStart]?: TriggerGrouped;
|
||||||
|
[SkillTriggerType.FEnd]?: TriggerGrouped;
|
||||||
[SkillTriggerType.Atking]?: TriggerGrouped;
|
[SkillTriggerType.Atking]?: TriggerGrouped;
|
||||||
[SkillTriggerType.Atked]?: TriggerGrouped;
|
[SkillTriggerType.Atked]?: TriggerGrouped;
|
||||||
/** 驻场光环(按 lv 解锁,存活期间全局生效) */
|
/** 驻场光环(按 lv 解锁,存活期间全局生效) */
|
||||||
@@ -71,6 +72,8 @@ export class HeroAttrsComp extends ecs.Comp {
|
|||||||
runtime_dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
runtime_dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||||
/** 当前等级下生效的战前触发条目 */
|
/** 当前等级下生效的战前触发条目 */
|
||||||
runtime_fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
runtime_fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||||
|
/** 当前等级下生效的回合结束触发条目 */
|
||||||
|
runtime_fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||||
/** 当前等级下生效的驻场光环词条列表 */
|
/** 当前等级下生效的驻场光环词条列表 */
|
||||||
runtime_field?: FieldEntry[];
|
runtime_field?: FieldEntry[];
|
||||||
|
|
||||||
@@ -725,6 +728,7 @@ export class HeroAttrsComp extends ecs.Comp {
|
|||||||
this.call = undefined;
|
this.call = undefined;
|
||||||
this.dead = undefined;
|
this.dead = undefined;
|
||||||
this.fstart = undefined;
|
this.fstart = undefined;
|
||||||
|
this.fend = undefined;
|
||||||
this.atking = undefined;
|
this.atking = undefined;
|
||||||
this.atked = undefined;
|
this.atked = undefined;
|
||||||
this.field = undefined;
|
this.field = undefined;
|
||||||
@@ -734,6 +738,7 @@ export class HeroAttrsComp extends ecs.Comp {
|
|||||||
this.runtime_call = undefined;
|
this.runtime_call = undefined;
|
||||||
this.runtime_dead = undefined;
|
this.runtime_dead = undefined;
|
||||||
this.runtime_fstart = undefined;
|
this.runtime_fstart = undefined;
|
||||||
|
this.runtime_fend = undefined;
|
||||||
this.runtime_field = undefined;
|
this.runtime_field = undefined;
|
||||||
this.critical = 0;
|
this.critical = 0;
|
||||||
this.critical_res = 0;
|
this.critical_res = 0;
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ export class Monster extends ecs.Entity {
|
|||||||
model.runtime_call = resolveTriggerByLv(model.call, mon_lv);
|
model.runtime_call = resolveTriggerByLv(model.call, mon_lv);
|
||||||
model.runtime_dead = resolveTriggerByLv(model.dead, mon_lv);
|
model.runtime_dead = resolveTriggerByLv(model.dead, mon_lv);
|
||||||
model.runtime_fstart = resolveTriggerByLv(model.fstart, mon_lv);
|
model.runtime_fstart = resolveTriggerByLv(model.fstart, mon_lv);
|
||||||
|
model.runtime_fend = resolveTriggerByLv(model.fend, mon_lv);
|
||||||
model.runtime_atking = resolveTriggerByLv(model.atking, mon_lv);
|
model.runtime_atking = resolveTriggerByLv(model.atking, mon_lv);
|
||||||
model.runtime_atked = resolveTriggerByLv(model.atked, mon_lv);
|
model.runtime_atked = resolveTriggerByLv(model.atked, mon_lv);
|
||||||
// 复活已纳入 dead 触发组,无需单独解析 revive
|
// 复活已纳入 dead 触发组,无需单独解析 revive
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ export class SkillTriggerHelper {
|
|||||||
case SkillTriggerType.FStart:
|
case SkillTriggerType.FStart:
|
||||||
this.handleArrayTrigger(model.runtime_fstart, model, view, type);
|
this.handleArrayTrigger(model.runtime_fstart, model, view, type);
|
||||||
break;
|
break;
|
||||||
|
case SkillTriggerType.FEnd:
|
||||||
|
this.handleArrayTrigger(model.runtime_fend, model, view, type);
|
||||||
|
break;
|
||||||
case SkillTriggerType.Atking:
|
case SkillTriggerType.Atking:
|
||||||
this.handleAtking(model, view);
|
this.handleAtking(model, view);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -565,6 +565,10 @@ export class MissionComp extends CCComp {
|
|||||||
|
|
||||||
// 通关判定:Boss 节(末节)胜利即视为本关通关
|
// 通关判定:Boss 节(末节)胜利即视为本关通关
|
||||||
smc.vmdata.scores.passed_wave_20 = true;
|
smc.vmdata.scores.passed_wave_20 = true;
|
||||||
|
|
||||||
|
// 触发回合结束技能(fend):仅节间流转时触发;
|
||||||
|
// 末节结算/失败时 pause 已置位,跳过避免结算瞬间白播施法表现
|
||||||
|
this.triggerHeroWaveEndSkills();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 战斗结束阶段,按 FightSet.WAVE_HEAL_RATE 恢复所有英雄血量
|
// 战斗结束阶段,按 FightSet.WAVE_HEAL_RATE 恢复所有英雄血量
|
||||||
@@ -676,6 +680,20 @@ export class MissionComp extends CCComp {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发英雄的回合结束技能(fend)
|
||||||
|
* 每回合(节)切入 BattleEnd 时对全队存活英雄各触发 1 次(无光环加成),
|
||||||
|
* 先于节间回血 healAllHeroes 结算,保证治疗/护盾类 fend 能吃到节末血线
|
||||||
|
*/
|
||||||
|
private triggerHeroWaveEndSkills() {
|
||||||
|
ecs.query(this.heroAttrsMatcher).forEach(entity => {
|
||||||
|
const attrs = entity.get(HeroAttrsComp);
|
||||||
|
const view = entity.get(HeroViewComp);
|
||||||
|
if (!attrs || !view || attrs.is_dead || attrs.fac !== FacSet.HERO) return;
|
||||||
|
SkillTriggerHelper.trigger(SkillTriggerType.FEnd, attrs, view);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 战斗结束阶段治疗所有英雄(节间回血),按 FightSet.WAVE_HEAL_RATE 恢复最大生命值比例
|
* 战斗结束阶段治疗所有英雄(节间回血),按 FightSet.WAVE_HEAL_RATE 恢复最大生命值比例
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user