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; } }