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.
140 lines
5.0 KiB
140 lines
5.0 KiB
import { Node, sp, Tween, Vec3 } from 'cc';
|
|
import { GBundle } from '../../game/ConfigRes';
|
|
import { BATTLE_HERO_BUFF_PATH } from '../../battle/res/BattleHeroResCollector';
|
|
import { BattleSpinePlayback } from '../../battle/presentation/BattleSpinePlayback';
|
|
import type { BattleBuffSkillDropTarget, CocosBagBoardView } from '../../battle/view/CocosBagBoardView';
|
|
|
|
/** 文昌笔预制体目录 */
|
|
const WENCHANG_BI_PREFAB_DIR = BATTLE_HERO_BUFF_PATH;
|
|
/** 文昌笔预制体名 */
|
|
const WENCHANG_BI_PREFAB_NAME = '文昌笔';
|
|
/** 文昌笔 Spine 动画名 */
|
|
const WENCHANG_BI_ANIM_NAME = 'animation';
|
|
|
|
/**
|
|
* 文昌笔主动 buff:skill 拖到字块上,先播文昌笔动画,播完再随机改字。
|
|
*/
|
|
export class BattleGameRewriteGlyphPresenter {
|
|
private _bagView: CocosBagBoardView | null = null;
|
|
private _flyLayer: Node | null = null;
|
|
private _playing = false;
|
|
private _fxNode: Node | null = null;
|
|
|
|
/** 绑定背包视图与飞行层 */
|
|
bind(bagView: CocosBagBoardView | null, flyLayer: Node | null | undefined): void {
|
|
this.unbind();
|
|
this._bagView = bagView;
|
|
this._flyLayer = flyLayer?.isValid ? flyLayer : null;
|
|
}
|
|
|
|
/** 注销并中断进行中的演出 */
|
|
unbind(): void {
|
|
this._abortPresentation();
|
|
this._bagView = null;
|
|
this._flyLayer = null;
|
|
}
|
|
|
|
/** 是否正在播放改字前置动画 */
|
|
get isPlaying(): boolean {
|
|
return this._playing;
|
|
}
|
|
|
|
/**
|
|
* 尝试在目标字块中心播放文昌笔,动画结束后执行随机改字。
|
|
* @param target 地图格 / 背包槽
|
|
* @param onSkillConsumed skill 进入 CD
|
|
*/
|
|
tryPlay(target: BattleBuffSkillDropTarget, onSkillConsumed: () => void): boolean {
|
|
if (this._playing) return false;
|
|
const bagView = this._bagView;
|
|
const flyLayer = this._flyLayer;
|
|
if (!bagView || !flyLayer?.isValid) return false;
|
|
if (!bagView.canApplyBattleBuffRewriteGlyphTarget(target)) return false;
|
|
|
|
const grid = bagView.getTextGridForDropTarget(target);
|
|
if (!grid?.node?.isValid) {
|
|
console.log(`[RewriteGlyph] 目标字块视图缺失 ${target.kind}`);
|
|
return false;
|
|
}
|
|
|
|
const worldPos = grid.node.worldPosition.clone();
|
|
this._playing = true;
|
|
|
|
const fx = this._spawnWenchangBiFx(flyLayer, worldPos);
|
|
if (!fx) {
|
|
console.log('[RewriteGlyph] 文昌笔预制体加载失败');
|
|
this._abortPresentation();
|
|
return false;
|
|
}
|
|
this._fxNode = fx;
|
|
onSkillConsumed();
|
|
|
|
const skel = fx.getComponent(sp.Skeleton) ?? fx.getComponentInChildren(sp.Skeleton);
|
|
if (!skel?.isValid) {
|
|
console.log('[RewriteGlyph] 文昌笔缺少 sp.Skeleton');
|
|
this._abortPresentation();
|
|
return false;
|
|
}
|
|
|
|
BattleSpinePlayback.play(skel, WENCHANG_BI_ANIM_NAME, {
|
|
loop: false,
|
|
onComplete: this._onAnimComplete.bind(this, target),
|
|
});
|
|
return true;
|
|
}
|
|
|
|
/** 动画播完:移除预制体并提交随机改字 */
|
|
private _onAnimComplete(target: BattleBuffSkillDropTarget): void {
|
|
this._releaseFx();
|
|
const bagView = this._bagView;
|
|
if (!bagView) {
|
|
this._playing = false;
|
|
return;
|
|
}
|
|
if (!bagView.canApplyBattleBuffRewriteGlyphTarget(target)) {
|
|
console.log(`[RewriteGlyph] 动画结束时目标已不可改字 ${target.kind}`);
|
|
this._playing = false;
|
|
return;
|
|
}
|
|
if (!bagView.applyBattleBuffRewriteGlyphTarget(target)) {
|
|
console.log(`[RewriteGlyph] 随机改字失败 ${target.kind}`);
|
|
}
|
|
this._playing = false;
|
|
}
|
|
|
|
/** 在字块中心生成文昌笔表现 */
|
|
private _spawnWenchangBiFx(flyLayer: Node, worldPos: Readonly<Vec3>): Node | null {
|
|
if (!flyLayer.active) flyLayer.active = true;
|
|
let node: Node | null = null;
|
|
try {
|
|
node = gg.res.getNode(WENCHANG_BI_PREFAB_DIR, WENCHANG_BI_PREFAB_NAME, GBundle.BattleHero);
|
|
} catch (e) {
|
|
console.log('[RewriteGlyph] 文昌笔加载异常', String(e));
|
|
return null;
|
|
}
|
|
if (!node?.isValid) return null;
|
|
node.parent = flyLayer;
|
|
node.setWorldPosition(worldPos);
|
|
node.setSiblingIndex(flyLayer.children.length - 1);
|
|
node.active = true;
|
|
return node;
|
|
}
|
|
|
|
/** 回收文昌笔节点 */
|
|
private _releaseFx(): void {
|
|
const node = this._fxNode;
|
|
this._fxNode = null;
|
|
if (!node?.isValid) return;
|
|
const skel = node.getComponent(sp.Skeleton) ?? node.getComponentInChildren(sp.Skeleton);
|
|
if (skel?.isValid) BattleSpinePlayback.clearPendingComplete(skel);
|
|
Tween.stopAllByTarget(node);
|
|
if (node['resKey']) gg.res.putNode(node);
|
|
else node.destroy();
|
|
}
|
|
|
|
/** 中断演出并清理 */
|
|
private _abortPresentation(): void {
|
|
this._releaseFx();
|
|
this._playing = false;
|
|
}
|
|
}
|
|
|