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.
760 lines
35 KiB
760 lines
35 KiB
|
3 weeks ago
|
import {
|
||
|
|
EventTouch, instantiate, Node, UIOpacity, UITransform, Vec2, Vec3,
|
||
|
|
} from 'cc';
|
||
|
|
import type { CocosBagBoardView, BattleBuffSkillDropTarget } from '../../battle/view/CocosBagBoardView';
|
||
|
|
import {
|
||
|
|
BattleGameSkillDragKind,
|
||
|
|
readBattleBuffAttackSpeedConfig,
|
||
|
|
readBattleBuffBindNetDuration,
|
||
|
|
readBattleBuffConfuseHeroDuration,
|
||
|
|
readBattleBuffFireTrapConfig,
|
||
|
|
readBattleBuffRandomLevelConfig,
|
||
|
|
readBattleBuffRecyclePeachCount,
|
||
|
|
readBattleBuffUnitLevelDelta,
|
||
|
|
resolveBattleGameSkillDragKind,
|
||
|
|
} from '../../manager/battleBuff/BattleBuffActiveSkillUtil';
|
||
|
|
import type { BattleBuffAttackSpeedConfig, BattleBuffFireTrapConfig, BattleBuffRandomLevelConfig } from '../../manager/battleBuff/BattleBuffParamParse';
|
||
|
|
import { DragPlaceLinePresenter } from '../../battle/view/DragPlaceLinePresenter';
|
||
|
|
import type { BattleGameBindNetTrapController } from './BattleGameBindNetTrapController';
|
||
|
|
import type { BattleGameFireTrapController } from './BattleGameFireTrapController';
|
||
|
|
import type { BattleGameRecycleToPeachPresenter } from './BattleGameRecycleToPeachPresenter';
|
||
|
|
import type { BattleGameRewriteGlyphPresenter } from './BattleGameRewriteGlyphPresenter';
|
||
|
|
import type { BattleGameAttackSpeedFxPresenter } from './BattleGameAttackSpeedFxPresenter';
|
||
|
|
import type { BattleGameRandomLevelFxPresenter } from './BattleGameRandomLevelFxPresenter';
|
||
|
|
import type { BattleGameUnitLevelUpFxPresenter } from './BattleGameUnitLevelUpFxPresenter';
|
||
|
|
import type { BattleGameAttackRangeFxPresenter } from './BattleGameAttackRangeFxPresenter';
|
||
|
|
import type { BattleGameExtraBossFxPresenter } from './BattleGameExtraBossFxPresenter';
|
||
|
|
import type { BattleGameConfuseHeroFxPresenter } from './BattleGameConfuseHeroFxPresenter';
|
||
|
|
import type { BattleGameStealGlyphFxPresenter } from './BattleGameStealGlyphFxPresenter';
|
||
|
|
import type { BattleGameDailyShopSkillView } from './BattleGameDailyShopSkillView';
|
||
|
|
import type { ProjectBattleHandle } from '../../battle/runtime/BattleRuntime';
|
||
|
|
import { BattlePhase } from 'db://assets/mx/module/battle';
|
||
|
|
import { BattleAudio } from '../../battle/audio/BattleAudio';
|
||
|
|
import { DailyShopKind } from '../../manager/DailyShopTypes';
|
||
|
|
import type { DailyShopOwnedDesc } from '../PopDailyShopLottery/DailyShopLotteryIcon';
|
||
|
|
|
||
|
|
/** 拖拽触发阈值(像素) */
|
||
|
|
const DRAG_SENSITIVITY_PX = 10;
|
||
|
|
|
||
|
|
/** 拖拽 icon 影子透明度 */
|
||
|
|
const DRAG_ICON_GHOST_OPACITY = 200;
|
||
|
|
|
||
|
|
/** skill1 / skill2 拖到地图:升级 / 缚仙网 / 乾坤袋等主动 buff */
|
||
|
|
export class BattleGameDailyShopSkillDrag {
|
||
|
|
private _skillView: BattleGameDailyShopSkillView | null = null;
|
||
|
|
private _bagView: CocosBagBoardView | null = null;
|
||
|
|
private _trapController: BattleGameBindNetTrapController | null = null;
|
||
|
|
private _fireTrapController: BattleGameFireTrapController | null = null;
|
||
|
|
private _recyclePresenter: BattleGameRecycleToPeachPresenter | null = null;
|
||
|
|
private _rewriteGlyphPresenter: BattleGameRewriteGlyphPresenter | null = null;
|
||
|
|
private _attackSpeedFxPresenter: BattleGameAttackSpeedFxPresenter | null = null;
|
||
|
|
private _randomLevelFxPresenter: BattleGameRandomLevelFxPresenter | null = null;
|
||
|
|
private _unitLevelUpFxPresenter: BattleGameUnitLevelUpFxPresenter | null = null;
|
||
|
|
private _attackRangeFxPresenter: BattleGameAttackRangeFxPresenter | null = null;
|
||
|
|
private _extraBossFxPresenter: BattleGameExtraBossFxPresenter | null = null;
|
||
|
|
private _confuseHeroFxPresenter: BattleGameConfuseHeroFxPresenter | null = null;
|
||
|
|
private _stealGlyphFxPresenter: BattleGameStealGlyphFxPresenter | null = null;
|
||
|
|
private _battle: ProjectBattleHandle | null = null;
|
||
|
|
private _ownedDesc: DailyShopOwnedDesc | null = null;
|
||
|
|
private _dragLayer: Node | null = null;
|
||
|
|
private readonly _linePresenter = new DragPlaceLinePresenter();
|
||
|
|
|
||
|
|
private _slotNodes: Node[] = [];
|
||
|
|
private _activeSlotIndex = -1;
|
||
|
|
private _activeDragKind: BattleGameSkillDragKind | null = null;
|
||
|
|
private _dragging = false;
|
||
|
|
private _dragTesting = false;
|
||
|
|
private _dragStart = new Vec2();
|
||
|
|
/** 拉线起点:skill 槽 icon 世界坐标(拖动期间固定,与字块拉线一致) */
|
||
|
|
private _lineAnchorWorld = new Vec3();
|
||
|
|
/** 拉线终点 fallback:跟随手指的 icon 影子 */
|
||
|
|
private _cursorWorld = new Vec3();
|
||
|
|
/** 跟随手指的 icon 影子(instantiate 副本,原 icon 留槽位) */
|
||
|
|
private _dragIconGhost: Node | null = null;
|
||
|
|
private _paramValue = 0;
|
||
|
|
private _randomLevelConfig: BattleBuffRandomLevelConfig | null = null;
|
||
|
|
private _attackSpeedConfig: BattleBuffAttackSpeedConfig | null = null;
|
||
|
|
private _fireTrapConfig: BattleBuffFireTrapConfig | null = null;
|
||
|
|
/** 攻速符 buffId(用于区分不同 buff 的叠加) */
|
||
|
|
private _attackSpeedBuffId = 0;
|
||
|
|
/** 千里眼 buffId(用于区分不同 buff 的叠加) */
|
||
|
|
private _rangeBuffId = 0;
|
||
|
|
|
||
|
|
/** 绑定 skill 拖拽(需在 bagView / trapController bind 之后调用) */
|
||
|
|
bind(
|
||
|
|
skillView: BattleGameDailyShopSkillView | null,
|
||
|
|
bagView: CocosBagBoardView | null,
|
||
|
|
trapController: BattleGameBindNetTrapController | null,
|
||
|
|
fireTrapController: BattleGameFireTrapController | null,
|
||
|
|
recyclePresenter: BattleGameRecycleToPeachPresenter | null,
|
||
|
|
rewriteGlyphPresenter: BattleGameRewriteGlyphPresenter | null,
|
||
|
|
attackSpeedFxPresenter: BattleGameAttackSpeedFxPresenter | null,
|
||
|
|
randomLevelFxPresenter: BattleGameRandomLevelFxPresenter | null,
|
||
|
|
unitLevelUpFxPresenter: BattleGameUnitLevelUpFxPresenter | null,
|
||
|
|
attackRangeFxPresenter: BattleGameAttackRangeFxPresenter | null,
|
||
|
|
extraBossFxPresenter: BattleGameExtraBossFxPresenter | null,
|
||
|
|
confuseHeroFxPresenter: BattleGameConfuseHeroFxPresenter | null,
|
||
|
|
stealGlyphFxPresenter: BattleGameStealGlyphFxPresenter | null,
|
||
|
|
battle: ProjectBattleHandle | null,
|
||
|
|
dragLayer: Node | null | undefined,
|
||
|
|
lineNode: Node | null | undefined,
|
||
|
|
ownedDesc?: DailyShopOwnedDesc | null,
|
||
|
|
): void {
|
||
|
|
this.unbind();
|
||
|
|
this._skillView = skillView;
|
||
|
|
this._bagView = bagView;
|
||
|
|
this._ownedDesc = ownedDesc ?? null;
|
||
|
|
this._trapController = trapController;
|
||
|
|
this._fireTrapController = fireTrapController;
|
||
|
|
this._recyclePresenter = recyclePresenter;
|
||
|
|
this._rewriteGlyphPresenter = rewriteGlyphPresenter;
|
||
|
|
this._attackSpeedFxPresenter = attackSpeedFxPresenter;
|
||
|
|
this._randomLevelFxPresenter = randomLevelFxPresenter;
|
||
|
|
this._unitLevelUpFxPresenter = unitLevelUpFxPresenter;
|
||
|
|
this._attackRangeFxPresenter = attackRangeFxPresenter;
|
||
|
|
this._extraBossFxPresenter = extraBossFxPresenter;
|
||
|
|
this._confuseHeroFxPresenter = confuseHeroFxPresenter;
|
||
|
|
this._stealGlyphFxPresenter = stealGlyphFxPresenter;
|
||
|
|
this._battle = battle;
|
||
|
|
this._dragLayer = dragLayer?.isValid ? dragLayer : null;
|
||
|
|
this._linePresenter.bind(lineNode ?? null);
|
||
|
|
this._bindSlotNodes();
|
||
|
|
}
|
||
|
|
|
||
|
|
/** roll 后刷新 skill 触摸绑定(不重建 Drag / Presenter) */
|
||
|
|
refreshSlotBindings(): void {
|
||
|
|
for (const node of this._slotNodes) {
|
||
|
|
if (!node?.isValid) continue;
|
||
|
|
node.off(Node.EventType.TOUCH_START, this._onTouchStart, this);
|
||
|
|
node.off(Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
|
||
|
|
node.off(Node.EventType.TOUCH_END, this._onTouchEnd, this);
|
||
|
|
node.off(Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
|
||
|
|
}
|
||
|
|
this._slotNodes.length = 0;
|
||
|
|
this._bindSlotNodes();
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 注销触摸 */
|
||
|
|
unbind(): void {
|
||
|
|
this._cancelDrag();
|
||
|
|
for (const node of this._slotNodes) {
|
||
|
|
if (!node?.isValid) continue;
|
||
|
|
node.off(Node.EventType.TOUCH_START, this._onTouchStart, this);
|
||
|
|
node.off(Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
|
||
|
|
node.off(Node.EventType.TOUCH_END, this._onTouchEnd, this);
|
||
|
|
node.off(Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
|
||
|
|
}
|
||
|
|
this._slotNodes.length = 0;
|
||
|
|
this._linePresenter.unbind();
|
||
|
|
this._skillView = null;
|
||
|
|
this._bagView = null;
|
||
|
|
this._trapController = null;
|
||
|
|
this._fireTrapController = null;
|
||
|
|
this._recyclePresenter = null;
|
||
|
|
this._rewriteGlyphPresenter = null;
|
||
|
|
this._attackSpeedFxPresenter = null;
|
||
|
|
this._randomLevelFxPresenter = null;
|
||
|
|
this._unitLevelUpFxPresenter = null;
|
||
|
|
this._attackRangeFxPresenter = null;
|
||
|
|
this._extraBossFxPresenter = null;
|
||
|
|
this._confuseHeroFxPresenter = null;
|
||
|
|
this._stealGlyphFxPresenter = null;
|
||
|
|
this._battle = null;
|
||
|
|
this._ownedDesc = null;
|
||
|
|
this._dragLayer = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 为 skill1 / skill2 注册触摸(携带即注册;七十二变 roll 后在拖拽时动态解析 cast 类型) */
|
||
|
|
private _bindSlotNodes(): void {
|
||
|
|
const view = this._skillView;
|
||
|
|
if (!view) return;
|
||
|
|
for (let slotIndex = 0; slotIndex < 2; slotIndex++) {
|
||
|
|
const slot = view.getSlot(slotIndex);
|
||
|
|
const root = slot?.root;
|
||
|
|
if (!root?.isValid || !root.active || !slot.hasCarriedBuff()) continue;
|
||
|
|
root.on(Node.EventType.TOUCH_START, this._onTouchStart, this);
|
||
|
|
root.on(Node.EventType.TOUCH_MOVE, this._onTouchMove, this);
|
||
|
|
root.on(Node.EventType.TOUCH_END, this._onTouchEnd, this);
|
||
|
|
root.on(Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
|
||
|
|
root['battleShopSkillSlotIndex'] = slotIndex;
|
||
|
|
this._slotNodes.push(root);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 触摸开始 */
|
||
|
|
private _onTouchStart(event: EventTouch): void {
|
||
|
|
if (this._dragging) return;
|
||
|
|
const slotIndex = this._resolveSlotIndex(event);
|
||
|
|
if (slotIndex < 0) return;
|
||
|
|
const slot = this._skillView?.getSlot(slotIndex);
|
||
|
|
if (!slot?.root.active || !slot.hasCarriedBuff()) return;
|
||
|
|
const pos = event.getUILocation();
|
||
|
|
this._dragStart.set(pos.x, pos.y);
|
||
|
|
this._activeSlotIndex = slotIndex;
|
||
|
|
this._dragTesting = true;
|
||
|
|
if (!this._canBeginDrag(slotIndex)) return;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 触摸移动 */
|
||
|
|
private _onTouchMove(event: EventTouch): void {
|
||
|
|
if (!this._dragTesting && !this._dragging) return;
|
||
|
|
const pos = event.getUILocation();
|
||
|
|
if (!this._dragging && this._dragTesting) {
|
||
|
|
if (
|
||
|
|
Math.abs(this._dragStart.x - pos.x) < DRAG_SENSITIVITY_PX
|
||
|
|
&& Math.abs(this._dragStart.y - pos.y) < DRAG_SENSITIVITY_PX
|
||
|
|
) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!this._canBeginDrag(this._activeSlotIndex)) {
|
||
|
|
this._resetDragState();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
this._beginDragVisual(this._activeSlotIndex, event);
|
||
|
|
}
|
||
|
|
if (!this._dragging) return;
|
||
|
|
this._updateDragVisual(event);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 触摸结束 */
|
||
|
|
private _onTouchEnd(event: EventTouch): void {
|
||
|
|
this._finishDrag(event);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 触摸取消 */
|
||
|
|
private _onTouchCancel(event: EventTouch): void {
|
||
|
|
this._finishDrag(event);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 是否可开始拖拽 */
|
||
|
|
private _canBeginDrag(slotIndex: number): boolean {
|
||
|
|
const bagView = this._bagView;
|
||
|
|
const skillView = this._skillView;
|
||
|
|
if (!bagView || !skillView) return false;
|
||
|
|
|
||
|
|
const slot = skillView.getSlot(slotIndex);
|
||
|
|
const row = slot?.getBuffRow();
|
||
|
|
const kind = slot?.getCastDragKind() ?? resolveBattleGameSkillDragKind(row);
|
||
|
|
if (!slot?.isReady() || kind == null) return false;
|
||
|
|
if (!slot.getIconNode()?.isValid) return false;
|
||
|
|
|
||
|
|
if (kind === BattleGameSkillDragKind.UnitLevelUp) {
|
||
|
|
if (!bagView.isBattleBuffSkillEnabled()) return false;
|
||
|
|
const levelDelta = readBattleBuffUnitLevelDelta(row);
|
||
|
|
if (levelDelta <= 0) return false;
|
||
|
|
this._paramValue = levelDelta;
|
||
|
|
this._randomLevelConfig = null;
|
||
|
|
} else if (kind === BattleGameSkillDragKind.RandomLevel) {
|
||
|
|
if (!bagView.isBattleBuffSkillEnabled()) return false;
|
||
|
|
const cfg = readBattleBuffRandomLevelConfig(row);
|
||
|
|
if (!cfg) return false;
|
||
|
|
this._randomLevelConfig = cfg;
|
||
|
|
this._paramValue = 0;
|
||
|
|
} else if (kind === BattleGameSkillDragKind.RewriteGlyph) {
|
||
|
|
if (!bagView.isBattleBuffSkillEnabled() || !this._rewriteGlyphPresenter || this._rewriteGlyphPresenter.isPlaying) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
this._randomLevelConfig = null;
|
||
|
|
this._attackSpeedConfig = null;
|
||
|
|
this._paramValue = 0;
|
||
|
|
} else if (kind === BattleGameSkillDragKind.AttackSpeedGlobal) {
|
||
|
|
if (!bagView.isBattleBuffSkillEnabled()) return false;
|
||
|
|
const cfg = readBattleBuffAttackSpeedConfig(row);
|
||
|
|
if (!cfg) return false;
|
||
|
|
this._attackSpeedConfig = cfg;
|
||
|
|
this._attackSpeedBuffId = Number(row?.id) || 0;
|
||
|
|
if (this._attackSpeedBuffId <= 0) return false;
|
||
|
|
this._randomLevelConfig = null;
|
||
|
|
this._paramValue = 0;
|
||
|
|
} else if (kind === BattleGameSkillDragKind.AttackRangeGlobal) {
|
||
|
|
if (!bagView.isBattleBuffSkillEnabled()) return false;
|
||
|
|
this._rangeBuffId = Number(row?.id) || 0;
|
||
|
|
if (this._rangeBuffId <= 0) return false;
|
||
|
|
this._attackSpeedConfig = null;
|
||
|
|
this._randomLevelConfig = null;
|
||
|
|
this._paramValue = 0;
|
||
|
|
} else if (kind === BattleGameSkillDragKind.BindNet) {
|
||
|
|
if (!bagView.isBattleBuffSkillEnabled() || !this._trapController) return false;
|
||
|
|
const bindSec = readBattleBuffBindNetDuration(row);
|
||
|
|
if (bindSec <= 0) return false;
|
||
|
|
this._paramValue = bindSec;
|
||
|
|
this._randomLevelConfig = null;
|
||
|
|
this._fireTrapConfig = null;
|
||
|
|
} else if (kind === BattleGameSkillDragKind.FireTrap) {
|
||
|
|
if (!bagView.isBattleBuffSkillEnabled() || !this._fireTrapController) return false;
|
||
|
|
const cfg = readBattleBuffFireTrapConfig(row);
|
||
|
|
if (!cfg) return false;
|
||
|
|
this._fireTrapConfig = cfg;
|
||
|
|
this._randomLevelConfig = null;
|
||
|
|
this._attackSpeedConfig = null;
|
||
|
|
this._paramValue = 0;
|
||
|
|
} else if (kind === BattleGameSkillDragKind.RecycleToPeach) {
|
||
|
|
if (!bagView.isBattleBuffSkillEnabled() || !this._recyclePresenter) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
const peachCount = readBattleBuffRecyclePeachCount(row);
|
||
|
|
if (peachCount <= 0) return false;
|
||
|
|
this._paramValue = peachCount;
|
||
|
|
this._randomLevelConfig = null;
|
||
|
|
this._attackSpeedConfig = null;
|
||
|
|
this._fireTrapConfig = null;
|
||
|
|
} else if (kind === BattleGameSkillDragKind.ExtraBoss) {
|
||
|
|
if (!bagView.isBattleBuffSkillEnabled() || !this._extraBossFxPresenter || this._extraBossFxPresenter.isPlaying) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
const battle = this._battle;
|
||
|
|
if (!battle || battle.session.phase !== BattlePhase.COMBAT) return false;
|
||
|
|
this._randomLevelConfig = null;
|
||
|
|
this._attackSpeedConfig = null;
|
||
|
|
this._fireTrapConfig = null;
|
||
|
|
this._paramValue = 0;
|
||
|
|
} else if (kind === BattleGameSkillDragKind.ConfuseHero) {
|
||
|
|
if (!bagView.isBattleBuffSkillEnabled() || !this._confuseHeroFxPresenter || this._confuseHeroFxPresenter.isPlaying) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
const battle = this._battle;
|
||
|
|
if (!battle || battle.session.phase !== BattlePhase.COMBAT) return false;
|
||
|
|
const durationSec = readBattleBuffConfuseHeroDuration(row);
|
||
|
|
if (durationSec <= 0) return false;
|
||
|
|
this._paramValue = durationSec;
|
||
|
|
this._randomLevelConfig = null;
|
||
|
|
this._attackSpeedConfig = null;
|
||
|
|
this._fireTrapConfig = null;
|
||
|
|
} else if (kind === BattleGameSkillDragKind.StealGlyph) {
|
||
|
|
if (!bagView.isBattleBuffSkillEnabled() || !this._stealGlyphFxPresenter || this._stealGlyphFxPresenter.isPlaying) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
const battle = this._battle;
|
||
|
|
if (!battle || battle.session.phase !== BattlePhase.COMBAT) return false;
|
||
|
|
this._randomLevelConfig = null;
|
||
|
|
this._attackSpeedConfig = null;
|
||
|
|
this._fireTrapConfig = null;
|
||
|
|
this._paramValue = 0;
|
||
|
|
} else {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
this._activeDragKind = kind;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 克隆 skill icon 到拖拽层作为跟随手指的影子 */
|
||
|
|
private _beginDragVisual(slotIndex: number, event: EventTouch): void {
|
||
|
|
const icon = this._skillView?.getSlot(slotIndex)?.getIconNode();
|
||
|
|
const dragLayer = this._dragLayer;
|
||
|
|
if (!icon?.isValid || !dragLayer?.isValid) {
|
||
|
|
this._resetDragState();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// prefab 中 ui_fly 默认 inactive;挂到未激活层会导致影子全程不可见
|
||
|
|
if (!dragLayer.active) {
|
||
|
|
dragLayer.active = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
this._ownedDesc?.hide();
|
||
|
|
this._clearDragIconGhost();
|
||
|
|
const ghost = instantiate(icon);
|
||
|
|
ghost.name = 'skillDragIconGhost';
|
||
|
|
ghost.parent = dragLayer;
|
||
|
|
ghost.setWorldPosition(icon.worldPosition);
|
||
|
|
ghost.setWorldScale(icon.worldScale);
|
||
|
|
ghost.setSiblingIndex(dragLayer.children.length - 1);
|
||
|
|
const ghostOpacity = ghost.getComponent(UIOpacity) ?? ghost.addComponent(UIOpacity);
|
||
|
|
ghostOpacity.opacity = DRAG_ICON_GHOST_OPACITY;
|
||
|
|
|
||
|
|
this._dragging = true;
|
||
|
|
this._dragTesting = false;
|
||
|
|
this._dragIconGhost = ghost;
|
||
|
|
icon.getWorldPosition(this._lineAnchorWorld);
|
||
|
|
this._setIconAtUiPos(event);
|
||
|
|
BattleAudio.vibratePickup();
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 更新 icon 影子 / 拉线 / 落点预览 */
|
||
|
|
private _updateDragVisual(event: EventTouch): void {
|
||
|
|
const bagView = this._bagView;
|
||
|
|
const ghost = this._dragIconGhost;
|
||
|
|
if (!bagView || !ghost?.isValid || this._activeDragKind == null) return;
|
||
|
|
|
||
|
|
this._setIconAtUiPos(event);
|
||
|
|
ghost.getWorldPosition(this._cursorWorld);
|
||
|
|
|
||
|
|
const uiPos = event.getUILocation();
|
||
|
|
this._updateSkillDragDropPreview(bagView, uiPos);
|
||
|
|
const lineTo = this._resolveSkillDragLineTargetWorld(bagView, uiPos) ?? this._cursorWorld;
|
||
|
|
this._linePresenter.show(this._lineAnchorWorld, lineTo);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 主动道具拖放:刷新绿/红格预览 */
|
||
|
|
private _updateSkillDragDropPreview(bagView: CocosBagBoardView, uiPos: { x: number; y: number }): void {
|
||
|
|
const kind = this._activeDragKind;
|
||
|
|
if (kind == null) return;
|
||
|
|
|
||
|
|
if (kind === BattleGameSkillDragKind.UnitLevelUp) {
|
||
|
|
bagView.setBattleBuffLevelUpDropPreview(
|
||
|
|
bagView.resolveBattleBuffSkillDropTargetAtUiPos(uiPos),
|
||
|
|
this._paramValue,
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.RandomLevel) {
|
||
|
|
bagView.setBattleBuffRandomLevelDropPreview(
|
||
|
|
bagView.resolveBattleBuffSkillDropTargetAtUiPos(uiPos),
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.RewriteGlyph) {
|
||
|
|
bagView.setBattleBuffRewriteGlyphDropPreview(
|
||
|
|
bagView.resolveBattleBuffSkillDropTargetAtUiPos(uiPos),
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.AttackSpeedGlobal) {
|
||
|
|
bagView.setBattleBuffAttackSpeedDropPreview(
|
||
|
|
bagView.resolveBattleBuffSkillDropTargetAtUiPos(uiPos),
|
||
|
|
this._attackSpeedBuffId,
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.AttackRangeGlobal) {
|
||
|
|
bagView.setBattleBuffAttackRangeDropPreview(
|
||
|
|
bagView.resolveBattleBuffSkillDropTargetAtUiPos(uiPos),
|
||
|
|
this._rangeBuffId,
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.BindNet || kind === BattleGameSkillDragKind.FireTrap) {
|
||
|
|
const gridIndex = bagView.resolveWalkableTrapGridAtUiPos(uiPos, this._hasTrapAt.bind(this));
|
||
|
|
bagView.setBindNetTrapDropPreview(gridIndex, this._hasTrapAt.bind(this));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.RecycleToPeach) {
|
||
|
|
bagView.setBattleBuffRecycleDropPreview(
|
||
|
|
bagView.resolveBattleBuffSkillDropTargetAtUiPos(uiPos),
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.ConfuseHero) {
|
||
|
|
bagView.setConfuseHeroDropPreviewAtUiPos(uiPos);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.StealGlyph) {
|
||
|
|
bagView.setStealGlyphDropPreviewAtUiPos(uiPos);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 主动道具拉线终点:有悬停落点则指向落点,否则跟手指(与字块拉线一致) */
|
||
|
|
private _resolveSkillDragLineTargetWorld(
|
||
|
|
bagView: CocosBagBoardView,
|
||
|
|
uiPos: { x: number; y: number },
|
||
|
|
): Vec3 | null {
|
||
|
|
const kind = this._activeDragKind;
|
||
|
|
if (kind == null) return null;
|
||
|
|
|
||
|
|
if (
|
||
|
|
kind === BattleGameSkillDragKind.UnitLevelUp
|
||
|
|
|| kind === BattleGameSkillDragKind.RandomLevel
|
||
|
|
|| kind === BattleGameSkillDragKind.RewriteGlyph
|
||
|
|
|| kind === BattleGameSkillDragKind.AttackSpeedGlobal
|
||
|
|
|| kind === BattleGameSkillDragKind.AttackRangeGlobal
|
||
|
|
|| kind === BattleGameSkillDragKind.RecycleToPeach
|
||
|
|
) {
|
||
|
|
const dropTarget = bagView.resolveBattleBuffSkillDropTargetAtUiPos(uiPos);
|
||
|
|
return dropTarget ? this._dropTargetWorld(bagView, dropTarget) : null;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.BindNet || kind === BattleGameSkillDragKind.FireTrap) {
|
||
|
|
const gridIndex = bagView.resolveWalkableTrapGridAtUiPos(uiPos, this._hasTrapAt.bind(this));
|
||
|
|
return gridIndex != null ? bagView.getMapGridWorldPosition(gridIndex) : null;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.ExtraBoss) {
|
||
|
|
const spawnPort = bagView.resolveOwnSpawnPortAtUiPos(uiPos);
|
||
|
|
return spawnPort != null ? bagView.getSpawnPointWorldPosition(spawnPort) : null;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.ConfuseHero) {
|
||
|
|
const target = bagView.resolveConfuseHeroTargetAtUiPos(uiPos);
|
||
|
|
if (target != null) return bagView.getConfuseHeroTargetWorldCenter(target);
|
||
|
|
const enemyGridIndex = bagView.resolveStealGlyphEnemyGridAtUiPos(uiPos);
|
||
|
|
return enemyGridIndex != null ? bagView.getMapGridWorldPosition(enemyGridIndex) : null;
|
||
|
|
}
|
||
|
|
if (kind === BattleGameSkillDragKind.StealGlyph) {
|
||
|
|
const enemyGridIndex = bagView.resolveStealGlyphEnemyGridAtUiPos(uiPos);
|
||
|
|
return enemyGridIndex != null ? bagView.getMapGridWorldPosition(enemyGridIndex) : null;
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 字块类主动 buff 落点世界坐标 */
|
||
|
|
private _dropTargetWorld(bagView: CocosBagBoardView, target: BattleBuffSkillDropTarget): Vec3 | null {
|
||
|
|
return target.kind === 'bag'
|
||
|
|
? bagView.getBagSlotWorldPosition(target.slotIndex)
|
||
|
|
: bagView.getMapGridWorldPosition(target.gridIndex);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 松手:尝试施放 */
|
||
|
|
private _finishDrag(event: EventTouch): void {
|
||
|
|
if (this._dragging && this._activeDragKind != null) {
|
||
|
|
const bagView = this._bagView;
|
||
|
|
const skillView = this._skillView;
|
||
|
|
const slotIndex = this._activeSlotIndex;
|
||
|
|
const uiPos = event.getUILocation();
|
||
|
|
let success = false;
|
||
|
|
|
||
|
|
if (this._activeDragKind === BattleGameSkillDragKind.UnitLevelUp) {
|
||
|
|
const dropTarget = bagView?.resolveBattleBuffSkillDropTargetAtUiPos(uiPos) ?? null;
|
||
|
|
const presenter = this._unitLevelUpFxPresenter;
|
||
|
|
success = !!(
|
||
|
|
bagView
|
||
|
|
&& dropTarget
|
||
|
|
&& presenter
|
||
|
|
&& bagView.canApplyBattleBuffLevelUpTarget(dropTarget, this._paramValue)
|
||
|
|
&& presenter.tryPlay(
|
||
|
|
dropTarget,
|
||
|
|
this._paramValue,
|
||
|
|
() => skillView?.startCooldown(slotIndex),
|
||
|
|
)
|
||
|
|
);
|
||
|
|
} else if (this._activeDragKind === BattleGameSkillDragKind.RandomLevel) {
|
||
|
|
const cfg = this._randomLevelConfig;
|
||
|
|
const dropTarget = bagView?.resolveBattleBuffSkillDropTargetAtUiPos(uiPos) ?? null;
|
||
|
|
const presenter = this._randomLevelFxPresenter;
|
||
|
|
success = !!(
|
||
|
|
bagView
|
||
|
|
&& cfg
|
||
|
|
&& dropTarget
|
||
|
|
&& presenter
|
||
|
|
&& bagView.canApplyBattleBuffRandomLevelTarget(dropTarget)
|
||
|
|
&& presenter.tryPlay(
|
||
|
|
dropTarget,
|
||
|
|
cfg.levelUpProbsWan,
|
||
|
|
() => skillView?.startCooldown(slotIndex),
|
||
|
|
)
|
||
|
|
);
|
||
|
|
} else if (this._activeDragKind === BattleGameSkillDragKind.RewriteGlyph) {
|
||
|
|
const dropTarget = bagView?.resolveBattleBuffSkillDropTargetAtUiPos(uiPos) ?? null;
|
||
|
|
const presenter = this._rewriteGlyphPresenter;
|
||
|
|
success = !!(
|
||
|
|
bagView
|
||
|
|
&& dropTarget
|
||
|
|
&& presenter
|
||
|
|
&& bagView.canApplyBattleBuffRewriteGlyphTarget(dropTarget)
|
||
|
|
&& presenter.tryPlay(
|
||
|
|
dropTarget,
|
||
|
|
() => skillView?.startCooldown(slotIndex),
|
||
|
|
)
|
||
|
|
);
|
||
|
|
} else if (this._activeDragKind === BattleGameSkillDragKind.AttackSpeedGlobal) {
|
||
|
|
const cfg = this._attackSpeedConfig;
|
||
|
|
const dropTarget = bagView?.resolveBattleBuffSkillDropTargetAtUiPos(uiPos) ?? null;
|
||
|
|
const applied = !!(
|
||
|
|
bagView
|
||
|
|
&& cfg
|
||
|
|
&& dropTarget
|
||
|
|
&& bagView.canApplyBattleBuffAttackSpeedTarget(dropTarget, this._attackSpeedBuffId)
|
||
|
|
&& bagView.applyBattleBuffAttackSpeedTarget(dropTarget, slotIndex, this._attackSpeedBuffId, cfg.bonusWan)
|
||
|
|
);
|
||
|
|
if (applied) {
|
||
|
|
this._attackSpeedFxPresenter?.tryPlay(dropTarget!);
|
||
|
|
} else if (dropTarget && bagView?.isAttackSpeedTargetAlreadyUsed(dropTarget, this._attackSpeedBuffId)) {
|
||
|
|
gg.ui.showToast('此道具无法重复使用');
|
||
|
|
}
|
||
|
|
success = applied;
|
||
|
|
} else if (this._activeDragKind === BattleGameSkillDragKind.AttackRangeGlobal) {
|
||
|
|
const dropTarget = bagView?.resolveBattleBuffSkillDropTargetAtUiPos(uiPos) ?? null;
|
||
|
|
const rangeFxPreview = dropTarget && bagView?.canApplyBattleBuffAttackRangeTarget(dropTarget, this._rangeBuffId)
|
||
|
|
? bagView.resolveAttackRangeBuffFxPreview(dropTarget)
|
||
|
|
: null;
|
||
|
|
const applied = !!(
|
||
|
|
bagView
|
||
|
|
&& dropTarget
|
||
|
|
&& bagView.canApplyBattleBuffAttackRangeTarget(dropTarget, this._rangeBuffId)
|
||
|
|
&& bagView.applyBattleBuffAttackRangeTarget(dropTarget, slotIndex, this._rangeBuffId)
|
||
|
|
);
|
||
|
|
if (applied) {
|
||
|
|
this._attackRangeFxPresenter?.tryPlay(dropTarget!, rangeFxPreview);
|
||
|
|
} else if (dropTarget && bagView?.isAttackRangeTargetMeleeUnit(dropTarget)) {
|
||
|
|
gg.ui.showToast('此道具无法作用于近战单位');
|
||
|
|
} else if (dropTarget && bagView?.isAttackRangeTargetAlreadyUsed(dropTarget, this._rangeBuffId)) {
|
||
|
|
gg.ui.showToast('此道具无法重复使用');
|
||
|
|
}
|
||
|
|
success = applied;
|
||
|
|
} else if (this._activeDragKind === BattleGameSkillDragKind.BindNet) {
|
||
|
|
const gridIndex = bagView?.resolveWalkableTrapGridAtUiPos(uiPos, this._hasTrapAt.bind(this)) ?? null;
|
||
|
|
success = !!(
|
||
|
|
this._trapController
|
||
|
|
&& bagView
|
||
|
|
&& gridIndex != null
|
||
|
|
&& bagView.canPlaceBindNetTrapAt(gridIndex, this._hasTrapAt.bind(this))
|
||
|
|
&& this._trapController.tryPlace(gridIndex, this._paramValue)
|
||
|
|
);
|
||
|
|
} else if (this._activeDragKind === BattleGameSkillDragKind.FireTrap) {
|
||
|
|
const cfg = this._fireTrapConfig;
|
||
|
|
const gridIndex = bagView?.resolveWalkableTrapGridAtUiPos(uiPos, this._hasTrapAt.bind(this)) ?? null;
|
||
|
|
success = !!(
|
||
|
|
cfg
|
||
|
|
&& this._fireTrapController
|
||
|
|
&& bagView
|
||
|
|
&& gridIndex != null
|
||
|
|
&& bagView.canPlaceBindNetTrapAt(gridIndex, this._hasTrapAt.bind(this))
|
||
|
|
&& this._fireTrapController.tryPlace(gridIndex, cfg.maxDamage, cfg.durationSec)
|
||
|
|
);
|
||
|
|
} else if (this._activeDragKind === BattleGameSkillDragKind.RecycleToPeach) {
|
||
|
|
const dropTarget = bagView?.resolveBattleBuffSkillDropTargetAtUiPos(uiPos) ?? null;
|
||
|
|
const presenter = this._recyclePresenter;
|
||
|
|
success = !!(
|
||
|
|
bagView
|
||
|
|
&& dropTarget
|
||
|
|
&& presenter
|
||
|
|
&& bagView.canApplyBattleBuffRecycleTarget(dropTarget)
|
||
|
|
&& presenter.tryPlay(
|
||
|
|
dropTarget,
|
||
|
|
this._paramValue,
|
||
|
|
() => skillView?.startCooldown(slotIndex),
|
||
|
|
)
|
||
|
|
);
|
||
|
|
} else if (this._activeDragKind === BattleGameSkillDragKind.ExtraBoss) {
|
||
|
|
const spawnPort = bagView?.resolveOwnSpawnPortAtUiPos(uiPos) ?? null;
|
||
|
|
const presenter = this._extraBossFxPresenter;
|
||
|
|
success = !!(
|
||
|
|
bagView
|
||
|
|
&& spawnPort != null
|
||
|
|
&& presenter
|
||
|
|
&& presenter.tryPlay(
|
||
|
|
spawnPort,
|
||
|
|
() => skillView?.startCooldown(slotIndex),
|
||
|
|
)
|
||
|
|
);
|
||
|
|
} else if (this._activeDragKind === BattleGameSkillDragKind.ConfuseHero) {
|
||
|
|
const enemyGridIndex = bagView?.resolveStealGlyphEnemyGridAtUiPos(uiPos) ?? null;
|
||
|
|
const target = bagView?.resolveConfuseHeroTargetAtUiPos(uiPos) ?? null;
|
||
|
|
const presenter = this._confuseHeroFxPresenter;
|
||
|
|
if (enemyGridIndex != null && bagView) {
|
||
|
|
const reason = bagView.getConfuseHeroRejectReason(enemyGridIndex);
|
||
|
|
if (reason === 'single_glyph') {
|
||
|
|
gg.ui.showToast('只能对合成神将施放');
|
||
|
|
} else if (reason === 'already_charmed') {
|
||
|
|
gg.ui.showToast('目标已被迷惑');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
success = !!(
|
||
|
|
bagView
|
||
|
|
&& target
|
||
|
|
&& presenter
|
||
|
|
&& presenter.tryPlay(
|
||
|
|
target,
|
||
|
|
this._paramValue,
|
||
|
|
() => skillView?.startCooldown(slotIndex),
|
||
|
|
)
|
||
|
|
);
|
||
|
|
} else if (this._activeDragKind === BattleGameSkillDragKind.StealGlyph) {
|
||
|
|
const rawGridIndex = bagView?.resolveStealGlyphEnemyGridAtUiPos(uiPos) ?? null;
|
||
|
|
const presenter = this._stealGlyphFxPresenter;
|
||
|
|
if (rawGridIndex != null && bagView) {
|
||
|
|
const reason = bagView.getStealGlyphRejectReason(rawGridIndex);
|
||
|
|
if (reason === 'composed') {
|
||
|
|
gg.ui.showToast('合成武将不能偷取');
|
||
|
|
} else if (reason === 'no_space') {
|
||
|
|
gg.ui.showToast('没空位放置不能偷取');
|
||
|
|
} else if (reason === 'invalid') {
|
||
|
|
gg.ui.showToast('只能偷武将散字或普通字');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const enemyGridIndex = bagView?.resolveStealGlyphTargetAtUiPos(uiPos) ?? null;
|
||
|
|
success = !!(
|
||
|
|
bagView
|
||
|
|
&& enemyGridIndex != null
|
||
|
|
&& presenter
|
||
|
|
&& presenter.tryPlay(
|
||
|
|
enemyGridIndex,
|
||
|
|
() => skillView?.startCooldown(slotIndex),
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (success && skillView && this._activeDragKind !== BattleGameSkillDragKind.RecycleToPeach
|
||
|
|
&& this._activeDragKind !== BattleGameSkillDragKind.RewriteGlyph
|
||
|
|
&& this._activeDragKind !== BattleGameSkillDragKind.UnitLevelUp
|
||
|
|
&& this._activeDragKind !== BattleGameSkillDragKind.RandomLevel
|
||
|
|
&& this._activeDragKind !== BattleGameSkillDragKind.ExtraBoss
|
||
|
|
&& this._activeDragKind !== BattleGameSkillDragKind.ConfuseHero
|
||
|
|
&& this._activeDragKind !== BattleGameSkillDragKind.StealGlyph) {
|
||
|
|
skillView.startCooldown(slotIndex);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
this._tryShowSkillDescOnTap();
|
||
|
|
}
|
||
|
|
this._cancelDrag();
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 拖拽 skill 轻触:显示描述(与 PopDailyShopBuy 一致) */
|
||
|
|
private _tryShowSkillDescOnTap(): void {
|
||
|
|
if (!this._dragTesting || this._dragging || this._activeSlotIndex < 0) return;
|
||
|
|
const slot = this._skillView?.getSlot(this._activeSlotIndex);
|
||
|
|
const parsed = slot?.getParsed();
|
||
|
|
const root = slot?.root;
|
||
|
|
const desc = this._ownedDesc;
|
||
|
|
if (!parsed || !root?.isValid || !desc) return;
|
||
|
|
desc.show(root, parsed, DailyShopKind.Active);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 行走格陷阱是否存在(缚仙网 / 三昧真火) */
|
||
|
|
private _hasTrapAt(idx: number): boolean {
|
||
|
|
return (this._trapController?.hasTrapAt(idx) ?? false)
|
||
|
|
|| (this._fireTrapController?.hasTrapAt(idx) ?? false);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** icon 影子跟随触摸点 */
|
||
|
|
private _setIconAtUiPos(event: EventTouch): void {
|
||
|
|
const ghost = this._dragIconGhost;
|
||
|
|
const dragLayer = this._dragLayer;
|
||
|
|
if (!ghost?.isValid || !dragLayer?.isValid) return;
|
||
|
|
const uiPos = event.getUILocation();
|
||
|
|
const layerUt = dragLayer.getComponent(UITransform);
|
||
|
|
if (!layerUt) return;
|
||
|
|
const local = layerUt.convertToNodeSpaceAR(new Vec3(uiPos.x, uiPos.y, 0));
|
||
|
|
ghost.setPosition(local);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 清理拖拽态并销毁 icon 影子 */
|
||
|
|
private _cancelDrag(): void {
|
||
|
|
this._bagView?.clearBattleBuffDropPreview();
|
||
|
|
this._linePresenter.hide();
|
||
|
|
this._clearDragIconGhost();
|
||
|
|
this._resetDragState();
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 销毁 icon 拖拽影子 */
|
||
|
|
private _clearDragIconGhost(): void {
|
||
|
|
const ghost = this._dragIconGhost;
|
||
|
|
if (ghost?.isValid) {
|
||
|
|
ghost.destroy();
|
||
|
|
}
|
||
|
|
this._dragIconGhost = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 重置内部状态 */
|
||
|
|
private _resetDragState(): void {
|
||
|
|
this._dragging = false;
|
||
|
|
this._dragTesting = false;
|
||
|
|
this._activeSlotIndex = -1;
|
||
|
|
this._activeDragKind = null;
|
||
|
|
this._paramValue = 0;
|
||
|
|
this._randomLevelConfig = null;
|
||
|
|
this._attackSpeedConfig = null;
|
||
|
|
this._fireTrapConfig = null;
|
||
|
|
this._attackSpeedBuffId = 0;
|
||
|
|
this._rangeBuffId = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 从节点反查槽位下标 */
|
||
|
|
private _resolveSlotIndex(event: EventTouch): number {
|
||
|
|
let cur = event.target as Node | null;
|
||
|
|
while (cur?.isValid) {
|
||
|
|
const idx = cur['battleShopSkillSlotIndex'];
|
||
|
|
if (typeof idx === 'number') return idx;
|
||
|
|
cur = cur.parent;
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
}
|