Files
pixelheros/assets/script/game/map/SkillBoxComp.ts
walkpan 62b7b9783a feat(map): 新增任务技能面板并优化技能触发逻辑
- 新增 mskills.prefab 作为任务技能容器
- 将 MissSkillsComp 挂载到场景实体层,移除 MissionCardComp 中的引用
- 优化 SkillBoxComp 触发坐标计算,改为基于父节点位置
- 调整技能盒尺寸并添加等级标签显示
- 修复战斗开始时技能触发计时器重置逻辑
2026-04-06 22:09:43 +08:00

176 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { mLogger } from "../common/Logger";
import { _decorator, Node, Prefab, Sprite, Label, Vec3, resources, SpriteAtlas } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { CardPoolList } from "../common/config/CardSet";
import { SkillSet } from "../common/config/SkillSet";
import { oops } from "db://oops-framework/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { smc } from "../common/SingletonModuleComp";
const { ccclass, property } = _decorator;
/** 视图层对象 */
@ccclass('SkillBoxComp')
@ecs.register('SkillBoxComp', false)
export class SkillBoxComp extends CCComp {
private debugMode: boolean = true;
@property({type: Node})
private icon_node:Node= null;
@property(Label)
private info_label: Label = null;
private s_uuid: number = 0;
private card_lv: number = 1;
private is_instant: boolean = true;
private trigger_times: number = 1;
private trigger_interval: number = 0;
private duration_rounds: number = 1;
private current_round: number = 0;
private current_trigger_times: number = 0;
private timer: number = 0;
private in_combat: boolean = false;
private initialized: boolean = false;
onLoad() {
oops.message.on(GameEvent.FightStart, this.onFightStart, this);
oops.message.on(GameEvent.MissionEnd, this.onMissionEnd, this);
this.node.on(GameEvent.NewWave, this.onNewWave, this);
oops.message.on(GameEvent.NewWave, this.onNewWaveGlobal, this);
}
onDestroy() {
oops.message.off(GameEvent.FightStart, this.onFightStart, this);
oops.message.off(GameEvent.MissionEnd, this.onMissionEnd, this);
this.node.off(GameEvent.NewWave, this.onNewWave, this);
oops.message.off(GameEvent.NewWave, this.onNewWaveGlobal, this);
}
init(uuid: number, card_lv: number) {
// this.node.parent=smc.map.MapView.scene.entityLayer!.node!
this.s_uuid = uuid;
this.card_lv = card_lv;
const config = CardPoolList.find(c => c.uuid === uuid);
if (config) {
this.is_instant = config.is_inst ?? true;
this.trigger_times = config.t_times ?? 1;
this.trigger_interval = config.t_inv ?? 0;
this.duration_rounds = config.d_rds ?? 1;
}
this.current_round = 0;
this.current_trigger_times = 0;
this.timer = 0;
this.initialized = true;
this.updateUI();
if (this.is_instant) {
// 即时起效:立即触发
this.triggerSkill();
this.current_trigger_times++;
if (this.current_trigger_times >= this.trigger_times) {
this.scheduleOnce(() => {
this.node.destroy();
}, 1.0); // 稍微延迟销毁,保证表现
}
}
}
updateUI() {
if (this.icon_node) {
const iconId = SkillSet[this.s_uuid]?.icon || `${this.s_uuid}`;
resources.load("gui/uicons", SpriteAtlas, (err, atlas) => {
if (err || !atlas) return;
const frame = atlas.getSpriteFrame(iconId);
if (frame && this.icon_node && this.icon_node.isValid) {
const sprite = this.icon_node.getComponent(Sprite) || this.icon_node.addComponent(Sprite);
sprite.spriteFrame = frame;
}
});
}
if (this.info_label) {
if (!this.is_instant) {
this.info_label.string = `${this.duration_rounds - this.current_round}`;
} else {
this.info_label.string = "";
}
}
}
private onFightStart() {
if (!this.initialized) return;
this.in_combat = true;
if (!this.is_instant) {
// 战斗开始时计时归0重新计时
this.timer = 0;
// 如果这个技能每回合都可以触发 t_times 次,则在每回合开始时重置当前回合触发次数
this.current_trigger_times = 0;
}
}
private onNewWave() {
this.handleNewWave();
}
private onNewWaveGlobal() {
this.handleNewWave();
}
private handleNewWave() {
if (!this.initialized) return;
this.in_combat = false;
if (!this.is_instant) {
this.current_round++;
this.updateUI();
if (this.current_round >= this.duration_rounds) {
this.node.destroy();
}
}
}
private onMissionEnd() {
this.node.destroy();
}
update(dt: number) {
if (!this.initialized || !this.in_combat || this.is_instant) return;
if (!smc.mission.play || smc.mission.pause) return;
if (this.current_trigger_times < this.trigger_times) {
this.timer += dt;
if (this.timer >= this.trigger_interval) {
this.timer = 0; // 触发后重新计时
this.triggerSkill();
this.current_trigger_times++;
}
}
}
private triggerSkill() {
// 获取自身在父节点下的局部坐标
// UI 的局部坐标在 2D 相机中和实际的游戏逻辑坐标存在偏移关系,
// 可以结合自身局部坐标做一次偏移,此处直接读取自身的 localPosition 加上父节点的偏移
let targetPos = new Vec3();
const localPos = this.node.position;
const parentPos = this.node.parent ? this.node.parent.position : new Vec3(0, 0, 0);
targetPos.set(parentPos.x + localPos.x, parentPos.y + localPos.y, 0);
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: this.s_uuid,
isCardSkill: true,
card_lv: this.card_lv,
targetPos: targetPos
});
}
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
reset() {
this.node.destroy();
}
}