Files
pixelheros/assets/script/game/map/MissionHeroComp.ts
walkpan 47b8aeb789 docs(game): 为英雄合成系统添加详细注释
为 MissionHeroComp 组件中的关键属性和方法添加 JSDoc 注释,以提升代码可读性和维护性。注释涵盖了英雄召唤队列、合成规则、链式合成流程以及事件监听管理等核心逻辑,便于后续开发者理解系统行为。
2026-03-28 09:05:44 +08:00

290 lines
11 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 { _decorator, instantiate, Prefab, v3, Vec3 } 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 { Hero } from "../hero/Hero";
import { smc } from "../common/SingletonModuleComp";
import { Timer } from "db://oops-framework/core/common/timer/Timer";
import { GameEvent } from "../common/config/GameEvent";
import { HeroPos } from "../common/config/heroSet";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { FacSet } from "../common/config/GameSet";
import { oneCom } from "../skill/oncend";
const { ccclass } = _decorator;
/** 视图层对象 */
@ccclass('MissionHeroCompComp')
@ecs.register('MissionHeroComp', false)
export class MissionHeroCompComp extends CCComp {
/** 英雄出生时的掉落高度,用于表现从空中落地 */
private static readonly HERO_DROP_HEIGHT = 260
/** 预留计时器 */
timer:Timer=new Timer(2)
/** 预留状态:友方是否全部死亡 */
Friend_is_dead:boolean=false
/** 当前处理的英雄 uuid */
current_hero_uuid:number=0
/** 当前英雄数量缓存 */
current_hero_num:number=-1
/** 合成规则2 合 1 或 3 合 1 */
merge_need_count:number=2
/** 允许合成的最高等级 */
merge_max_lv:number=3
/** 是否正在执行一次合成流程 */
is_merging:boolean=false
/** 是否正在消费召唤队列,防止并发处理 */
is_processing_queue:boolean=false
/** 召唤请求队列,保证召唤与合成串行 */
summon_queue:{ uuid: number; hero_lv: number }[]=[]
/** 预留英雄列表 */
heros:any=[]
onLoad(){
/** 节点事件监听 */
this.on(GameEvent.FightReady,this.fight_ready,this)
this.on(GameEvent.Zhaohuan,this.zhao_huan,this)
this.on(GameEvent.MissionEnd,this.clear_heros,this)
/** 全局消息监听 */
oops.message.on(GameEvent.CallHero,this.call_hero,this)
}
onDestroy(){
/** 清理监听,避免节点销毁后仍响应消息 */
oops.message.off(GameEvent.CallHero,this.call_hero,this)
oops.message.off(GameEvent.FightReady,this.fight_ready,this)
oops.message.off(GameEvent.Zhaohuan,this.zhao_huan,this)
oops.message.off(GameEvent.MissionEnd,this.clear_heros,this)
}
start() {
// this.test_call()
}
/** 关卡结束时,清理全部存活英雄 */
clear_heros(){
const heroes = this.getAliveHeroes();
for (let i = 0; i < heroes.length; i++) {
heroes[i].destroy();
}
}
/** 战斗准备阶段重置出战英雄计数 */
fight_ready(){
smc.vmdata.mission_data.hero_num=0
}
// protected update(dt: number): void {
// }
/** 预留:召唤事件扩展入口 */
private zhao_huan(event: string, args: any){
}
/** 召唤请求入口:归一化参数并进入串行队列 */
private async call_hero(event: string, args: any){
const uuid = Number(args?.uuid ?? 1001);
const hero_lv = Math.max(1, Number(args?.hero_lv ?? 1));
this.summon_queue.push({ uuid, hero_lv });
this.processSummonQueue();
}
/** 添加英雄:固定出生点上方生成,再落至落点 */
private addHero(uuid:number=1001,hero_lv:number=1) {
console.log("addHero uuid:",uuid)
let hero_pos=0
let hero = ecs.getEntity<Hero>(Hero);
let scale = 1
let landingPos:Vec3 = HeroPos[hero_pos].pos;
let spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroCompComp.HERO_DROP_HEIGHT, 0);
hero.load(spawnPos,scale,uuid,landingPos.y,hero_lv);
return hero;
}
/** 添加合成后的新英雄,并覆盖为聚合后的属性 */
private addMergedHero(uuid:number, hero_lv:number, ap:number, hp_max:number): number {
const hero = this.addHero(uuid, hero_lv);
const model = hero.get(HeroAttrsComp);
if (!model) return hero_lv;
model.ap = Math.max(0, ap);
model.hp_max = Math.max(1, hp_max);
model.hp = model.hp_max;
model.dirty_hp = true;
return model.lv;
}
/** 获取当前全部存活友方英雄 */
private getAliveHeroes(): Hero[] {
const heroes: Hero[] = [];
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
const model = entity.get(HeroAttrsComp);
if (!model) return;
if (model.fac !== FacSet.HERO) return;
if (model.is_dead) return;
heroes.push(entity as Hero);
});
return heroes;
}
/** 挑选可参与本次合成的英雄组 */
private pickMergeHeroes(aliveHeroes: Hero[], uuid: number, hero_lv: number, needCount: number = 3): Hero[] {
const mergeHeroes: Hero[] = [];
for (let i = 0; i < aliveHeroes.length; i++) {
const model = aliveHeroes[i].get(HeroAttrsComp);
if (!model) continue;
if (model.hero_uuid !== uuid) continue;
if (model.lv !== hero_lv) continue;
mergeHeroes.push(aliveHeroes[i]);
if (mergeHeroes.length === needCount) break;
}
return mergeHeroes;
}
/** 统计满足同 uuid、同等级的可合成英雄数量 */
private countMergeHeroes(aliveHeroes: Hero[], uuid: number, hero_lv: number): number {
let count = 0;
for (let i = 0; i < aliveHeroes.length; i++) {
const model = aliveHeroes[i].get(HeroAttrsComp);
if (!model) continue;
if (model.hero_uuid !== uuid) continue;
if (model.lv !== hero_lv) continue;
count += 1;
}
return count;
}
/** 读取当前合成需要数量,仅支持 2 或 3 */
private getMergeNeedCount(): number {
return this.merge_need_count === 2 ? 2 : 3;
}
/** 判断该等级是否还能继续向上合成 */
private canMergeLevel(hero_lv: number): boolean {
return hero_lv < Math.max(1, this.merge_max_lv);
}
/** 串行消费召唤队列,避免同帧并发触发多次合成导致状态错乱 */
private async processSummonQueue() {
if (this.is_processing_queue) return;
this.is_processing_queue = true;
try {
while (this.summon_queue.length > 0) {
const payload = this.summon_queue.shift();
if (!payload) continue;
await this.handleSingleSummon(payload.uuid, payload.hero_lv);
}
} finally {
this.is_processing_queue = false;
}
}
/** 处理单次召唤:先生成,再检测是否触发合成,再尝试链式合成 */
private async handleSingleSummon(uuid: number, hero_lv: number) {
this.addHero(uuid, hero_lv);
if (!this.canMergeLevel(hero_lv)) return;
const needCount = this.getMergeNeedCount();
const aliveHeroes = this.getAliveHeroes();
const mergeHeroes = this.pickMergeHeroes(aliveHeroes, uuid, hero_lv, needCount);
if (mergeHeroes.length !== needCount) return;
this.is_merging = true;
try {
const mergedLv = await this.mergeGroupHeroes(mergeHeroes, uuid, hero_lv);
await this.tryChainMerge(uuid, mergedLv);
} finally {
this.is_merging = false;
}
}
/** 将一组合成素材向出生点汇聚并销毁,全部完成后返回 */
private mergeDestroyAtBirth(mergeHeroes: Hero[], spawnPos: Vec3): Promise<void> {
return new Promise((resolve) => {
let doneCount = 0;
const total = mergeHeroes.length;
if (total <= 0) {
resolve();
return;
}
const onDone = () => {
doneCount += 1;
if (doneCount >= total) {
resolve();
}
};
for (let i = 0; i < mergeHeroes.length; i++) {
mergeHeroes[i].mergeToBirthAndDestroy(spawnPos, onDone);
}
});
}
/** 播放合成爆点特效,使用 oneCom 控制特效生命周期 */
private playMergeBoomFx(worldPos: Vec3): Promise<void> {
return new Promise((resolve) => {
const scene = smc.map?.MapView?.scene;
const layer = scene?.entityLayer?.node;
if (!layer || !layer.isValid) {
resolve();
return;
}
const prefab: Prefab = oops.res.get("game/skill/end/dead", Prefab)!;
if (!prefab) {
resolve();
return;
}
const fx = instantiate(prefab);
if (!fx || !fx.isValid) {
resolve();
return;
}
fx.parent = layer;
fx.setPosition(worldPos);
fx.getComponent(oneCom) || fx.addComponent(oneCom);
this.scheduleOnce(() => resolve(), 0.4);
});
}
/** 执行一次完整合成:聚合属性、销毁素材、播放特效、生成高一级英雄 */
private async mergeGroupHeroes(mergeHeroes: Hero[], uuid: number, hero_lv: number): Promise<number> {
let sumAp = 0;
let sumHpMax = 0;
for (let i = 0; i < mergeHeroes.length; i++) {
const model = mergeHeroes[i].get(HeroAttrsComp);
if (!model) continue;
sumAp += model.ap;
sumHpMax += model.hp_max;
}
let hero_pos = 0;
const landingPos:Vec3 = HeroPos[hero_pos].pos;
const spawnPos:Vec3 = v3(landingPos.x, landingPos.y + MissionHeroCompComp.HERO_DROP_HEIGHT, 0);
await this.mergeDestroyAtBirth(mergeHeroes, spawnPos);
await this.playMergeBoomFx(spawnPos);
return this.addMergedHero(uuid, Math.min(this.merge_max_lv, hero_lv + 1), sumAp, sumHpMax);
}
/** 链式合成:当前等级合成完成后,继续尝试更高等级,直到条件不满足 */
private async tryChainMerge(uuid: number, startLv: number) {
let checkLv = Math.max(1, startLv);
const needCount = this.getMergeNeedCount();
let guard = 0;
while (guard < 20) {
guard += 1;
if (!this.canMergeLevel(checkLv)) {
break;
}
const aliveHeroes = this.getAliveHeroes();
const sameCount = this.countMergeHeroes(aliveHeroes, uuid, checkLv);
if (sameCount < needCount) {
break;
}
const mergeHeroes = this.pickMergeHeroes(aliveHeroes, uuid, checkLv, needCount);
if (mergeHeroes.length < needCount) {
break;
}
checkLv = await this.mergeGroupHeroes(mergeHeroes, uuid, checkLv);
}
}
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件时触发,用于自定义释放逻辑 */
reset() {
// this.node.destroy();
}
}