You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
AdLike2048/assets/script/ui/BattleGame/BattleGameExtraBossFxPresen...

152 lines
5.2 KiB

import { Node, sp, Tween } from 'cc';
import { BattlePhase } from 'db://assets/mx/module/battle';
import { GBundle } from '../../game/ConfigRes';
import { BATTLE_HERO_BUFF_PATH } from '../../battle/res/BattleHeroResCollector';
import { BattleSpinePlayback } from '../../battle/presentation/BattleSpinePlayback';
import type { CocosBagBoardView } from '../../battle/view/CocosBagBoardView';
import type { ProjectBattleHandle } from '../../battle/runtime/BattleRuntime';
import { BattleGameExtraBossSkill } from './BattleGameExtraBossSkill';
/** 万神幡预制体(battleHero/prefab/buff/万神幡) */
const EXTRA_BOSS_FX_PREFAB_NAME = '万神幡';
/** 万神幡 Spine 动画名 */
const EXTRA_BOSS_FX_ANIM_NAME = 'animaction';
/**
* 万神幡(functiontype=13):拖到我方出怪口播万神幡,播完再额外召唤 Boss。
*/
export class BattleGameExtraBossFxPresenter {
private _battle: ProjectBattleHandle | null = null;
private _bagView: CocosBagBoardView | null = null;
private _playing = false;
private _activeFxNode: Node | null = null;
/** 是否正在播放万神幡 */
get isPlaying(): boolean {
return this._playing;
}
/**
* 绑定战斗与背包视图。
* @param battle 战斗句柄
* @param bagView 背包视图(读出怪口节点)
*/
bind(battle: ProjectBattleHandle | null, bagView: CocosBagBoardView | null): void {
this.unbind();
this._battle = battle;
this._bagView = bagView;
}
/** 注销并中断进行中的表现 */
unbind(): void {
this._abortFx();
this._battle = null;
this._bagView = null;
}
/**
* 在出怪口播万神幡,播完召唤 Boss。
* @param spawnPort 出怪口编号
* @param onApplied 召唤成功并进入 CD
*/
tryPlay(spawnPort: number, onApplied: () => void): boolean {
if (this._playing) return false;
const battle = this._battle;
const bagView = this._bagView;
if (!battle || !bagView) return false;
if (battle.session.phase !== BattlePhase.COMBAT) {
console.log('[ExtraBossFx] 仅战斗阶段可召唤 Boss');
return false;
}
const ownPort = bagView.resolveOwnSpawnPort();
if (spawnPort !== ownPort) {
console.log('[ExtraBossFx] 仅可施放到己方出怪口', spawnPort, ownPort);
return false;
}
const spawnNode = bagView.getSpawnPointNode(spawnPort);
if (!spawnNode?.isValid) {
console.log('[ExtraBossFx] 出怪口节点缺失', spawnPort);
return false;
}
const fxNode = this._spawnFxOnSpawnPoint(spawnNode);
if (!fxNode) return false;
const skel = fxNode.getComponent(sp.Skeleton) ?? fxNode.getComponentInChildren(sp.Skeleton);
if (!skel?.isValid) {
console.log('[ExtraBossFx] 万神幡缺少 sp.Skeleton');
this._releaseFx(fxNode);
return false;
}
this._playing = true;
const started = BattleSpinePlayback.play(skel, EXTRA_BOSS_FX_ANIM_NAME, {
loop: false,
onComplete: this._onAnimComplete.bind(this, fxNode, skel, onApplied),
});
if (!started) {
this._releaseFx(fxNode);
this._playing = false;
return false;
}
return true;
}
/** 动画播完:回收表现 → 召唤 Boss → CD */
private _onAnimComplete(fxNode: Node, skel: sp.Skeleton, onApplied: () => void): void {
if (skel?.isValid) BattleSpinePlayback.clearPendingComplete(skel);
this._releaseFx(fxNode);
this._playing = false;
const battle = this._battle;
if (!battle) return;
if (!BattleGameExtraBossSkill.trySummon(battle)) return;
onApplied();
}
/** 加载并挂到出怪口 */
private _spawnFxOnSpawnPoint(spawnNode: Node): Node | null {
if (!spawnNode?.isValid) return null;
let node: Node | null = null;
try {
node = gg.res.getNode(BATTLE_HERO_BUFF_PATH, EXTRA_BOSS_FX_PREFAB_NAME, GBundle.BattleHero);
} catch (e) {
console.log('[ExtraBossFx] 万神幡预制体加载异常', String(e));
return null;
}
if (!node?.isValid) {
console.log('[ExtraBossFx] 万神幡预制体缺失', EXTRA_BOSS_FX_PREFAB_NAME);
return null;
}
node.parent = spawnNode;
node.setPosition(0, 0, 0);
node.active = true;
this._activeFxNode = node;
return node;
}
/** 回收特效节点 */
private _releaseFx(node: Node | null): void {
if (!node?.isValid) return;
if (this._activeFxNode === node) this._activeFxNode = null;
Tween.stopAllByTarget(node);
if (node['resKey']) gg.res.putNode(node);
else node.destroy();
}
/** 中断并清理表现 */
private _abortFx(): void {
const node = this._activeFxNode;
if (node?.isValid) {
const skel = node.getComponent(sp.Skeleton) ?? node.getComponentInChildren(sp.Skeleton);
if (skel?.isValid) BattleSpinePlayback.clearPendingComplete(skel);
this._releaseFx(node);
}
this._playing = false;
}
}