|
|
|
|
import { Node, UITransform, Vec3, sp } from 'cc';
|
|
|
|
|
import { MergeEventType, type MergeEvent } from 'db://assets/mx/module/merge';
|
|
|
|
|
import type { BattleBagSession } from '../../battle/bag/BattleBagSession';
|
|
|
|
|
import { BattleSpinePlayback } from '../../battle/presentation/BattleSpinePlayback';
|
|
|
|
|
import { RankDataManager } from '../../manager/RankDataManager';
|
|
|
|
|
|
|
|
|
|
/** 引导手指 Spine 动画 */
|
|
|
|
|
const FINGER_ANIM = '点击';
|
|
|
|
|
/** 步骤节点父名 */
|
|
|
|
|
const STEPS_NODE = 'steps';
|
|
|
|
|
/** 第一步:点召唤 */
|
|
|
|
|
const STEP_SUMMON = '2';
|
|
|
|
|
/** 第二步:拖字上阵 */
|
|
|
|
|
const STEP_DRAG = '3';
|
|
|
|
|
/** 手指节点名 */
|
|
|
|
|
const FINGER_NODE = '引导手指';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 新玩家首次进战弱引导:点召唤 → 拖字上阵。
|
|
|
|
|
* 手指对齐召唤按钮世界坐标,避免预制体旧坐标偏移。
|
|
|
|
|
*/
|
|
|
|
|
export class BattleFirstGuidePresenter {
|
|
|
|
|
/** 引导根 */
|
|
|
|
|
private _root: Node | null = null;
|
|
|
|
|
/** 点召唤文案 */
|
|
|
|
|
private _stepSummon: Node | null = null;
|
|
|
|
|
/** 拖字文案 */
|
|
|
|
|
private _stepDrag: Node | null = null;
|
|
|
|
|
/** 手指 */
|
|
|
|
|
private _finger: Node | null = null;
|
|
|
|
|
/** 手指 Spine */
|
|
|
|
|
private _fingerSk: sp.Skeleton | null = null;
|
|
|
|
|
/** 召唤按钮(对齐目标) */
|
|
|
|
|
private _summonBtn: Node | null = null;
|
|
|
|
|
/** 本机背包 */
|
|
|
|
|
private _bag: BattleBagSession | null = null;
|
|
|
|
|
/** 当前步:0 未开 / 2 点召唤 / 3 拖字 */
|
|
|
|
|
private _step = 0;
|
|
|
|
|
/** 复用世界坐标,避免每帧 new */
|
|
|
|
|
private readonly _alignWorld = new Vec3();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 绑定并按存档决定是否展示第一步。
|
|
|
|
|
* @param root ui_guide
|
|
|
|
|
* @param bag 本机背包会话
|
|
|
|
|
* @param summonBtn 召唤按钮(btnRefresh)
|
|
|
|
|
*/
|
|
|
|
|
bind(root: Node | null, bag: BattleBagSession | null, summonBtn?: Node | null): void {
|
|
|
|
|
this.unbind();
|
|
|
|
|
if (!root?.isValid) {
|
|
|
|
|
console.error('[BattleFirstGuide] ui_guide 缺失');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
root.active = false;
|
|
|
|
|
if (!RankDataManager.shouldShowFirstBattleGuide()) return;
|
|
|
|
|
const steps = root.getChildByName(STEPS_NODE);
|
|
|
|
|
this._stepSummon = steps?.getChildByName(STEP_SUMMON) ?? null;
|
|
|
|
|
this._stepDrag = steps?.getChildByName(STEP_DRAG) ?? null;
|
|
|
|
|
this._finger = root.getChildByName(FINGER_NODE) ?? null;
|
|
|
|
|
this._fingerSk = this._finger?.getComponent(sp.Skeleton) ?? null;
|
|
|
|
|
if (!this._stepSummon?.isValid || !this._stepDrag?.isValid || !this._finger?.isValid) {
|
|
|
|
|
console.error('[BattleFirstGuide] ui_guide 子节点缺失');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!bag) {
|
|
|
|
|
console.error('[BattleFirstGuide] bagSession 未就绪');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!summonBtn?.isValid) {
|
|
|
|
|
console.warn('[BattleFirstGuide] 召唤按钮缺失,手指可能错位');
|
|
|
|
|
}
|
|
|
|
|
this._root = root;
|
|
|
|
|
this._bag = bag;
|
|
|
|
|
this._summonBtn = summonBtn?.isValid ? summonBtn : null;
|
|
|
|
|
this._bag.on(MergeEventType.TilePlaced, this._onPlayerDrag);
|
|
|
|
|
this._bag.on(MergeEventType.TileMoved, this._onPlayerDrag);
|
|
|
|
|
this._showSummon();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Widget 布局后再对齐一次(由界面 scheduleOnce 调用) */
|
|
|
|
|
realignFinger(): void {
|
|
|
|
|
if (this._step !== 2) return;
|
|
|
|
|
this._alignFingerToSummon();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 召唤刷新成功:藏手指,切到拖字提示 */
|
|
|
|
|
onSummonOk(): void {
|
|
|
|
|
if (this._step !== 2) return;
|
|
|
|
|
this._showDrag();
|
|
|
|
|
|
|
|
|
|
const curWaveNum = gg.game.CurentBattle.CurentWaveNum;
|
|
|
|
|
gg.sdk.reportDY('inLevel', `${gg.game.getReportDYBattleCountLabel(curWaveNum)}-新手引导1点召唤`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 停动画、解监听、藏根 */
|
|
|
|
|
unbind(): void {
|
|
|
|
|
if (this._bag) {
|
|
|
|
|
this._bag.off(MergeEventType.TilePlaced, this._onPlayerDrag);
|
|
|
|
|
this._bag.off(MergeEventType.TileMoved, this._onPlayerDrag);
|
|
|
|
|
}
|
|
|
|
|
this._stopFinger();
|
|
|
|
|
if (this._root?.isValid) this._root.active = false;
|
|
|
|
|
this._root = null;
|
|
|
|
|
this._stepSummon = null;
|
|
|
|
|
this._stepDrag = null;
|
|
|
|
|
this._finger = null;
|
|
|
|
|
this._fingerSk = null;
|
|
|
|
|
this._summonBtn = null;
|
|
|
|
|
this._bag = null;
|
|
|
|
|
this._step = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 第一步:手指点召唤 + 文案 2 */
|
|
|
|
|
private _showSummon(): void {
|
|
|
|
|
const root = this._root;
|
|
|
|
|
if (!root?.isValid) return;
|
|
|
|
|
this._step = 2;
|
|
|
|
|
root.active = true;
|
|
|
|
|
if (this._stepSummon) this._stepSummon.active = true;
|
|
|
|
|
if (this._stepDrag) this._stepDrag.active = false;
|
|
|
|
|
if (this._finger) this._finger.active = true;
|
|
|
|
|
this._alignFingerToSummon();
|
|
|
|
|
this._playFinger();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 将手指对齐到召唤按钮中心(不拦截点击,否则会挡住召唤) */
|
|
|
|
|
private _alignFingerToSummon(): void {
|
|
|
|
|
const finger = this._finger;
|
|
|
|
|
const btn = this._summonBtn;
|
|
|
|
|
if (!finger?.isValid || !btn?.isValid) return;
|
|
|
|
|
btn.getWorldPosition(this._alignWorld);
|
|
|
|
|
const put = finger.parent?.getComponent(UITransform);
|
|
|
|
|
if (!put) {
|
|
|
|
|
finger.setWorldPosition(this._alignWorld);
|
|
|
|
|
} else {
|
|
|
|
|
const local = put.convertToNodeSpaceAR(this._alignWorld);
|
|
|
|
|
finger.setPosition(local.x, local.y, 0);
|
|
|
|
|
}
|
|
|
|
|
// Spine 仍绘制;命中盒清零,触摸落到下方 btnRefresh
|
|
|
|
|
const fut = finger.getComponent(UITransform);
|
|
|
|
|
if (fut) fut.setContentSize(0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 第二步:只留拖字文案 */
|
|
|
|
|
private _showDrag(): void {
|
|
|
|
|
this._step = 3;
|
|
|
|
|
if (this._stepSummon) this._stepSummon.active = false;
|
|
|
|
|
if (this._stepDrag) this._stepDrag.active = true;
|
|
|
|
|
this._stopFinger();
|
|
|
|
|
if (this._finger) this._finger.active = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 玩家拖到地图:结束引导 */
|
|
|
|
|
private readonly _onPlayerDrag = (_event: MergeEvent): void => {
|
|
|
|
|
if (this._step !== 3) return;
|
|
|
|
|
RankDataManager.markFirstBattleGuideDone();
|
|
|
|
|
this.unbind();
|
|
|
|
|
|
|
|
|
|
const curWaveNum = gg.game.CurentBattle.CurentWaveNum;
|
|
|
|
|
gg.sdk.reportDY('inLevel', `${gg.game.getReportDYBattleCountLabel(curWaveNum)}-新手引导2拖字上阵`);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 循环播手指点击 */
|
|
|
|
|
private _playFinger(): void {
|
|
|
|
|
const sk = this._fingerSk;
|
|
|
|
|
if (!sk?.isValid) {
|
|
|
|
|
console.error('[BattleFirstGuide] 引导手指缺少 Spine');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
BattleSpinePlayback.play(sk, FINGER_ANIM, { loop: true });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 停手指 Spine */
|
|
|
|
|
private _stopFinger(): void {
|
|
|
|
|
const sk = this._fingerSk;
|
|
|
|
|
if (!sk?.isValid) return;
|
|
|
|
|
BattleSpinePlayback.clearPendingComplete(sk);
|
|
|
|
|
BattleSpinePlayback.resetTracks(sk);
|
|
|
|
|
}
|
|
|
|
|
}
|