feat(skill): add timed buff support for skills

1. 新增timed_buff_id配置项到SkillConfig和SkillOverrides接口
2. 实现计时buff逻辑:配置该id时通过buffManager添加限时buff,跳过永久buff逻辑
3. 优化代码格式和空行规范,修复部分代码细节问题
This commit is contained in:
pan
2026-07-23 16:25:08 +08:00
parent 9a5093d2a4
commit 2381812d06
3 changed files with 9619 additions and 9324 deletions

View File

@@ -14,6 +14,7 @@ import { SkillTriggerType } from "../common/config/heroSet";
import { SkillTriggerHelper } from "./SkillTriggerHelper";
import { MissionEconomy } from "../map/MissionEconomy";
import { MissionHeroComp } from "../map/MissionHeroComp";
import { buffManager } from "./BuffManager";
/**
* ==================== 自动施法系统 ====================
@@ -44,18 +45,18 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
oops.message.off(GameEvent.TriggerSkill, this.onTriggerSkill, this);
}
private onTriggerSkill(event: string, args: {
s_uuid: number,
heroAttrs?: HeroAttrsComp,
heroView?: HeroViewComp,
private onTriggerSkill(event: string, args: {
s_uuid: number,
heroAttrs?: HeroAttrsComp,
heroView?: HeroViewComp,
triggerType?: string,
isCardSkill?: boolean,
card_lv?: number,
targetPos?: Vec3,
overrides?: any
overrides?: any
}) {
if (!args || !args.s_uuid) return;
// 卡牌技能直接触发
if (args.isCardSkill) {
this.forceCastCardSkill(args.s_uuid, args.card_lv || 1, args.targetPos || new Vec3(FightSet.CSKILL_START_X, FightSet.CSKILL_START_Y, 0), args.overrides);
@@ -94,12 +95,12 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
// 构造一个模拟的 HeroAttrsComp 用于数值计算,只包含基础卡牌伤害计算所需的属性
const mockAttrs = new HeroAttrsComp();
// 动态计算卡牌的虚拟攻击力:
// 1. 根据卡牌等级给予基础成长(同英雄升级公式,基准设为 100
let baseAp = 100 * Math.pow(FightSet.HERO_LV_MULTIPLIER, cardLv - 1);
let highestAp = baseAp;
// 2. 获取场上最高攻击力的英雄,保证后期奶量/增益绝对够用
for (const eid of smc.mission.heroGrid) {
if (eid >= 0) {
@@ -113,7 +114,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
}
mockAttrs.ap = highestAp;
mockAttrs.ap = highestAp;
mockAttrs.critical = 0;
mockAttrs.freeze_chance = 0;
mockAttrs.stun_chance = 0;
@@ -121,7 +122,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
mockAttrs.fac = FacSet.HERO;
mockAttrs.type = HType.Long; // 假定为远程,拥有较长索敌范围
mockAttrs.dis = 2000; // 给予全屏以上的索敌范围
let targetPos: Vec3 | null = null;
if (!isFriendly) {
// 伪造一个 view 供找敌逻辑使用,位置为 spawnPos
@@ -140,7 +141,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
return;
}
}
console.log("[SCastSystem] forceCastCardSkill: casting skill", s_uuid, "castTimes", castTimes, "targetPos", targetPos);
for (let i = 0; i < castTimes; i++) {
if (isFriendly) {
@@ -165,7 +166,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
const skill = ecs.getEntity<Skill>(Skill);
const actualStartPos = this.resolveRepeatCastStartPos(startPos, castIndex);
// 伪造一个简单的 heroView 供 Skill 初始化使用,只包含方向信息
const mockView = {
node: { scale: new Vec3(1, 1, 1), position: actualStartPos },
@@ -192,7 +193,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
private isOutOfBattleBounds(x: number): boolean {
return x < BoxSet.LETF_END || x > BoxSet.RIGHT_END;
}
/** 系统过滤器:仅处理英雄实体 */
filter(): ecs.IMatcher {
return this.getHeroMatcher();
@@ -205,9 +206,9 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
* 3. 选取本帧可施放技能并执行施法
*/
update(e: ecs.Entity): void {
if(!smc.mission.play ) return;
if(smc.mission.pause) return
if(!smc.mission.in_fight) return
if (!smc.mission.play) return;
if (smc.mission.pause) return
if (!smc.mission.in_fight) return
const heroAttrs = e.get(HeroAttrsComp);
const heroView = e.get(HeroViewComp);
if (!heroAttrs || !heroView || !heroView.node) return;
@@ -231,7 +232,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
heroView.playReady("yellow");
} else if (triggerType === 'dead') {
heroView.playOther("dead");
}else{
} else {
heroView.playOther('yellow')
}
@@ -273,11 +274,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
const sUp = SkillUpList[s_uuid] ? SkillUpList[s_uuid] : SkillUpList[1001];
const cNum = Math.min(2, Math.max(0, Math.floor(sUp.num ?? 0)));
const castTimes = 1 + cNum;
let val=""
if(castTimes >1){
val = "*"+castTimes.toString
let val = ""
if (castTimes > 1) {
val = "*" + castTimes.toString
}
heroView.skill_name(val,s_uuid,triggerType)
heroView.skill_name(val, s_uuid, triggerType)
for (let i = 0; i < castTimes; i++) {
if (!heroView.node || !heroView.node.isValid) return;
if (isFriendly) {
@@ -340,12 +341,12 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
const skillLv = castPlan.skillLv;
const overrides = castPlan.overrides;
let config = SkillSet[s_uuid];
const sUp = SkillUpList[s_uuid] ? SkillUpList[s_uuid]:SkillUpList[1001];
const sUp = SkillUpList[s_uuid] ? SkillUpList[s_uuid] : SkillUpList[1001];
const cNum = Math.min(2, Math.max(0, Math.floor(sUp.num ?? 0)));
if (!config) return;
config = mergeSkillParams(config, overrides);
//播放前摇技能动画
heroView.playReady(config.readyAnm);
//播放角色攻击动画
@@ -363,7 +364,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
// 注意:这里仍然是基于时间的延迟,受帧率波动影响。
// 若需精确同步,建议在动画中添加帧事件并在 HeroViewComp 中监听。
const delay = config.ready > 0 ? config.ready : FightSet.SKILL_CAST_DELAY;
heroView.scheduleOnce(() => {
if (!smc.mission.play || smc.mission.pause || !smc.mission.in_fight) return;
if (!heroView.node || !heroView.node.isValid || heroAttrs.is_dead) return;
@@ -407,7 +408,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
* 创建技能实体(投射物/范围体等)。
* 仅用于对敌伤害技能的实体化表现与碰撞伤害分发。
*/
private createSkillEntity(s_uuid: number, skillLv: number, caster: HeroViewComp,cAttrsComp: HeroAttrsComp, targetPos: Vec3, castIndex: number = 0, overrides?: SkillOverrides) {
private createSkillEntity(s_uuid: number, skillLv: number, caster: HeroViewComp, cAttrsComp: HeroAttrsComp, targetPos: Vec3, castIndex: number = 0, overrides?: SkillOverrides) {
if (!caster.node || !caster.node.isValid) return;
const parent = caster.node.parent;
if (!parent) return;
@@ -443,12 +444,12 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
private applyFriendlySkillEffects(_s_uuid: number, _skillLv: number, config: SkillConfig, _heroView: HeroViewComp, _cAttrsComp: HeroAttrsComp, targets: HeroViewComp[], _targetPos: Vec3 | null, isCardSkill: boolean = false) {
const kind = config.kind ?? SkillKind.Support;
const sUp = SkillUpList[_s_uuid] ?? SkillUpList[1001];
const sAp =config.ap+sUp.ap*_skillLv;
const sHit=config.hit_count+sUp.hit_count*_skillLv;
const sAp = config.ap + sUp.ap * _skillLv;
const sHit = config.hit_count + sUp.hit_count * _skillLv;
const applyTargets = kind === SkillKind.Heal
? this.pickHealTargetsByMostMissingHp(targets, sHit)
: this.pickRandomFriendlyTargets(targets, sHit);
for (const target of applyTargets) {
this.applyActualFriendlyEffect(target, kind, sAp, _cAttrsComp, config, sUp, _skillLv);
}
@@ -460,13 +461,13 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
if (!target.ent) return;
const model = target.ent.get(HeroAttrsComp);
if (!model || model.is_dead) return;
if (config.endAnm && config.endAnm !== "") {
target.playEnd(config.endAnm);
}
if (kind === SkillKind.Heal && sAp !== 0) {
const addHp = Math.floor(sAp*_cAttrsComp.ap/100);
const addHp = Math.floor(sAp * _cAttrsComp.ap / 100);
model.add_hp(addHp);
target.health(addHp);
if (_cAttrsComp.fac === FacSet.HERO) {
@@ -483,20 +484,32 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
MissionEconomy.addCoin(addGold);
}
}
if (config.buff_type !== undefined) {
// 计时性 buff配置了 timed_buff_id 时走 buffManager 限时路径,
// 同一技能不应既永久加成又限时加成,故提前 return 跳过下方永久 add_* 逻辑
if (config.timed_buff_id) {
buffManager.applyBuff(
target.ent,
config.timed_buff_id,
_cAttrsComp.hero_uuid,
_cAttrsComp.ap
);
return;
}
const baseValue = config.ap;
let upgradeValue = 0;
// 根据 buff 类型选择对应的升级加成
if (config.buff_type === Attrs.ap) upgradeValue = sUp.buff_ap || 0;
else if (config.buff_type === Attrs.hp_max) upgradeValue = sUp.buff_hp || 0;
else if (config.buff_type === Attrs.critical) upgradeValue = sUp.crt || 0;
// 如果后续有冰冻、击晕等,在这里加上对应的 sUp 字段即可,如 sUp.frz / sUp.stun
const totalBuffValue = baseValue + upgradeValue;
switch (config.buff_type){
switch (config.buff_type) {
case Attrs.ap:
model.add_ap(totalBuffValue);
break;
@@ -507,7 +520,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
// 除了 hp_max 和 ap其他固定属性走统一的 add_special_attr 方法
model.add_special_attr(config.buff_type, totalBuffValue);
break;
}
}
}
}
@@ -569,11 +582,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
private collectFriendlyTargetEids(fac: number, selfEid: number | undefined, includeSelf: boolean): number[] {
const eids: number[] = [];
const grid = fac === FacSet.HERO ? smc.mission.heroGrid : smc.mission.monGrid;
for (const eid of grid) {
if (eid >= 0) {
if (!includeSelf && typeof selfEid === "number" && eid === selfEid) continue;
const entity = ecs.getEntityByEid(eid);
if (entity) {
const model = entity.get(HeroAttrsComp);
@@ -645,7 +658,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
// 怪物找英雄
for (let col = 0; col < 2; col++) {
// 列内顺序:怪物配置优先中路
const rowOrder = [1, myRow, myRow === 0 ? 2 : 0];
const rowOrder = [1, myRow, myRow === 0 ? 2 : 0];
for (const row of rowOrder) {
const idx = col * 3 + row;
const eid = smc.mission.heroGrid[idx];
@@ -741,10 +754,10 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
halfHeight = 40; // 如果没有 UITransform给一个默认高度偏移
}
}
const pos = target.node.position.clone();
pos.y += halfHeight;
// 至于最终投射物是否要飞出屏幕(例如线性弹道延长至 +-500由 SMoveSystem 统一处理
return pos;
}