feat: 实现技能卡牌系统并添加相关配置
- 在 GameSet 中新增技能卡牌释放起始坐标常量 - 卡牌使用组件增加技能卡释放事件分发 - 任务英雄组件监听技能卡事件并转发给技能施放系统 - 卡牌组件支持技能卡牌的显示和等级星级 - 卡牌配置中添加技能卡牌池和对应的配置信息 - 技能施放系统扩展以支持卡牌技能的直接触发
This commit is contained in:
@@ -7,7 +7,7 @@ import { Skill } from "../skill/Skill";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { HType } from "../common/config/heroSet";
|
||||
import { Attrs } from "../common/config/HeroAttrs";
|
||||
import { BoxSet, FightSet } from "../common/config/GameSet";
|
||||
import { BoxSet, FacSet, FightSet } from "../common/config/GameSet";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
|
||||
@@ -40,10 +40,91 @@ 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, triggerType?: string }) {
|
||||
if (!args || !args.s_uuid || !args.heroAttrs || !args.heroView) return;
|
||||
private onTriggerSkill(event: string, args: {
|
||||
s_uuid: number,
|
||||
heroAttrs?: HeroAttrsComp,
|
||||
heroView?: HeroViewComp,
|
||||
triggerType?: string,
|
||||
isCardSkill?: boolean,
|
||||
card_lv?: number,
|
||||
targetPos?: Vec3
|
||||
}) {
|
||||
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));
|
||||
return;
|
||||
}
|
||||
|
||||
// 常规英雄技能触发
|
||||
if (!args.heroAttrs || !args.heroView) return;
|
||||
this.forceCastTriggerSkill(args.s_uuid, args.heroAttrs, args.heroView, args.triggerType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制执行卡牌技能
|
||||
* 卡牌技能没有施法者主体,直接从指定坐标释放,或者对全体/随机友方生效
|
||||
*/
|
||||
public forceCastCardSkill(s_uuid: number, cardLv: number, spawnPos: Vec3) {
|
||||
const config = SkillSet[s_uuid];
|
||||
if (!config) return;
|
||||
|
||||
// 如果是敌方目标,没有战斗时不释放
|
||||
const isEnemyTarget = !this.isSelfSkill(config.TGroup) && !this.isFriendlySkill(config.TGroup);
|
||||
if (isEnemyTarget && !smc.mission.in_fight) return;
|
||||
|
||||
let isFriendly = false;
|
||||
let targetEids: number[] = [];
|
||||
|
||||
if (this.isFriendlySkill(config.TGroup) || this.isSelfSkill(config.TGroup)) {
|
||||
isFriendly = true;
|
||||
targetEids = this.collectFriendlyTargetEids(FacSet.HERO, undefined, true); // 获取所有英雄阵营的目标
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 构造一个模拟的 HeroAttrsComp 用于数值计算,只包含基础卡牌伤害计算所需的属性
|
||||
const mockAttrs = new HeroAttrsComp();
|
||||
mockAttrs.ap = 100; // 基准攻击力,可以根据设计需求调整
|
||||
mockAttrs.critical = 0;
|
||||
mockAttrs.freeze_chance = 0;
|
||||
mockAttrs.back_chance = 0;
|
||||
mockAttrs.puncture = 0;
|
||||
mockAttrs.fac = FacSet.HERO;
|
||||
|
||||
for (let i = 0; i < castTimes; i++) {
|
||||
if (isFriendly) {
|
||||
const friendlyTargets = this.resolveFriendlyTargets(targetEids, FacSet.HERO);
|
||||
if (friendlyTargets.length === 0) continue;
|
||||
this.applyFriendlySkillEffects(s_uuid, cardLv, config, null as any, mockAttrs, friendlyTargets, null);
|
||||
} else {
|
||||
const enemyTargetPos = this.resolveRepeatCastTargetPos(new Vec3(spawnPos.x + 300, spawnPos.y, spawnPos.z), i);
|
||||
this.createSkillEntityForCard(s_uuid, cardLv, mockAttrs, spawnPos, enemyTargetPos, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 专用于卡牌施放的技能实体生成 */
|
||||
private createSkillEntityForCard(s_uuid: number, skillLv: number, mockAttrs: HeroAttrsComp, startPos: Vec3, targetPos: Vec3 | null, castIndex: number = 0) {
|
||||
const scene = smc.map.MapView.scene;
|
||||
const parent = scene.entityLayer?.node?.getChildByName("SKILL");
|
||||
if (!parent || !targetPos) return;
|
||||
|
||||
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 },
|
||||
ent: { eid: -1 },
|
||||
box_group: BoxSet.HERO
|
||||
} as any;
|
||||
|
||||
skill.load(actualStartPos, parent, s_uuid, targetPos.clone(), mockView, mockAttrs, skillLv, 0);
|
||||
}
|
||||
/** 空施法计划:用于“当前无可施法技能”时的统一返回 */
|
||||
private readonly emptyCastPlan = { skillId: 0, skillLv: 1, isFriendly: false, targetPos: null as Vec3 | null, targetEids: [] as number[] };
|
||||
/** 近战英雄默认施法射程 */
|
||||
|
||||
Reference in New Issue
Block a user