diff --git a/assets/mx/module/merge/core/MergeSession.ts b/assets/mx/module/merge/core/MergeSession.ts index dcb532f..72f1374 100644 --- a/assets/mx/module/merge/core/MergeSession.ts +++ b/assets/mx/module/merge/core/MergeSession.ts @@ -151,9 +151,13 @@ export class MergeSession { const w2 = Math.floor(Number(weapon2Id) || 0); const lv = Math.max(1, Math.floor(Number(level) || 0)); if (w2 <= 0) return PlaceRejectReason.InvalidMerge; - this.map.placeTile(gridIndex, { ...tile, weapon2Id: w2, level: lv }); - this._recompose(); const heroId = this._resolveElementHeroId(w2, lv) ?? 0; + if (!(heroId > 0)) { + console.warn('[MergeSession] applyBattleCardToMapTile 无 Hero', w2, lv); + return PlaceRejectReason.InvalidMerge; + } + this.map.placeTile(gridIndex, { ...tile, weapon2Id: w2, level: lv, weapon1Id: heroId }); + this._recompose(); this.emit(MergeEventFactory.mapUpgraded(gridIndex, heroId, lv)); return null; } @@ -171,8 +175,12 @@ export class MergeSession { const w2 = Math.floor(Number(weapon2Id) || 0); const lv = Math.max(1, Math.floor(Number(level) || 0)); if (w2 <= 0) return PlaceRejectReason.InvalidMerge; - this.bag.setSlot(slotIndex, { ...dst, weapon2Id: w2, level: lv }); const heroId = this._resolveElementHeroId(w2, lv) ?? 0; + if (!(heroId > 0)) { + console.warn('[MergeSession] applyBattleCardToBagSlot 无 Hero', w2, lv); + return PlaceRejectReason.InvalidMerge; + } + this.bag.setSlot(slotIndex, { ...dst, weapon2Id: w2, level: lv, weapon1Id: heroId }); this.emit(MergeEventFactory.bagMerged(slotIndex, heroId, lv)); return null; } @@ -397,9 +405,13 @@ export class MergeSession { const max = this._config.getElementMaxLevel(srcType); if (srcLevel >= max) return PlaceRejectReason.MaxLevel; const newLevel = srcLevel + 1; - clearSource(); - this.bag.setSlot(toSlot, { ...target, level: newLevel }); const heroId = this._config.getElementHeroId(srcType, newLevel) ?? 0; + if (!(heroId > 0)) { + console.warn('[MergeSession] 背包五行合成无 Hero', srcType, newLevel); + return PlaceRejectReason.InvalidMerge; + } + clearSource(); + this.bag.setSlot(toSlot, { ...target, level: newLevel, weapon1Id: heroId }); this.emit(MergeEventFactory.bagMerged(toSlot, heroId, newLevel)); return null; } @@ -628,14 +640,22 @@ export class MergeSession { return null; } - /** 组合内所有字块设为同一等级,并同步绑定快照等级 */ + /** 组合内所有字块设为同一等级,并同步 Hero.id / 绑定快照 */ private _applyLevelToCompositionTiles(compose: ComposedUnit, level: number): void { + const w1 = Math.floor(Number(compose.weapon1Id) || 0); for (const idx of compose.gridIndices) { const tile = this.map.getTile(idx); if (!tile) continue; - if ((tile.level ?? 1) === level && tile.heroBind?.level === level) continue; + const sameLv = (tile.level ?? 1) === level && tile.heroBind?.level === level; + const sameW1 = (tile.weapon1Id ?? 0) === w1; + if (sameLv && sameW1) continue; const rec: MapTileRecord = { ...tile, level }; - if (tile.heroBind) rec.heroBind = { ...this._cloneHeroBind(tile.heroBind), level }; + if (w1 > 0) rec.weapon1Id = w1; + if (tile.heroBind) { + const bind = { ...this._cloneHeroBind(tile.heroBind), level }; + if (w1 > 0) bind.weapon1Id = w1; + rec.heroBind = bind; + } this.map.placeTile(idx, rec); } } @@ -1008,10 +1028,15 @@ export class MergeSession { const max = this._config.getElementMaxLevel(srcType); if (srcLevel >= max) return PlaceRejectReason.MaxLevel; const newLevel = srcLevel + 1; + const heroId = this._config.getElementHeroId(srcType, newLevel) ?? 0; + if (!(heroId > 0)) { + console.warn('[MergeSession] 五行合成无 Hero', srcType, newLevel); + return PlaceRejectReason.InvalidMerge; + } clearSource(); - this.map.placeTile(toGrid, { ...target, level: newLevel }); + this.map.placeTile(toGrid, { ...target, level: newLevel, weapon1Id: heroId }); this._recompose(); - this.emit(MergeEventFactory.mapUpgraded(toGrid, target.weapon1Id ?? 0, newLevel)); + this.emit(MergeEventFactory.mapUpgraded(toGrid, heroId, newLevel)); return null; } diff --git a/assets/script/battle/ai/runtime/AiHeadlessBagHost.ts b/assets/script/battle/ai/runtime/AiHeadlessBagHost.ts index 270ae94..86447a8 100644 --- a/assets/script/battle/ai/runtime/AiHeadlessBagHost.ts +++ b/assets/script/battle/ai/runtime/AiHeadlessBagHost.ts @@ -7,6 +7,7 @@ import type { MapManager } from '../../map/legacy/MapManager'; import type { AiMatchProfile } from '../match/AiMatchProfile'; import { AI_PLACE_CAMP, AI_PLAYER_ID, AI_VIEW_LOCAL_CAMP } from './AiCampArea'; import { WeaponComposeConfigProvider } from '../../bag/WeaponComposeConfigProvider'; +import { isBagToolTextType, isShovelTextType } from 'db://assets/mx/module/merge'; import { textDisplayName } from '../../config/BattleHeroTable'; import { TextListCellState, TextListLandHost } from '../../view/TextListLandHost'; import { TableNames } from '../../../game/ConfigTableData'; @@ -121,11 +122,24 @@ export class AiHeadlessBagHost { const slots = bag.getBagSlots(); let best = -1; let bestLv = 1e9; + let bestIsTool = false; for (let i = 0; i < slots.length; i++) { const slot = slots[i]; if (!slot || !(slot.weapon2Id > 0)) continue; if (this._land.stateOf(i) !== TextListCellState.Open) continue; - const lv = Math.floor(Number(slot.level) || 0); + const w2Type = Math.floor(WeaponComposeConfigProvider.instance.getWeapon2Type(slot.weapon2Id)); + const isTool = isBagToolTextType(w2Type) || isShovelTextType(w2Type) || Math.floor(Number(slot.battleCardKind) || 0) > 0; + const lv = Math.max(0, Math.floor(Number(slot.level) || 0)); + // 功能符 level=0:优先丢掉,避免卡死开启格 + if (isTool) { + if (!bestIsTool || i < best || best < 0) { + bestIsTool = true; + bestLv = lv; + best = i; + } + continue; + } + if (bestIsTool) continue; if (!(lv > 0)) continue; if (lv < bestLv || (lv === bestLv && i < best)) { bestLv = lv; @@ -134,7 +148,7 @@ export class AiHeadlessBagHost { } if (best < 0) return false; if (!bag.discardBagSlot(best)) return false; - aiOp(`卡死丢弃 bag[${best}] Lv${bestLv} 腾召唤位`); + aiOp(`卡死丢弃 bag[${best}] Lv${bestLv}${bestIsTool ? ' 工具' : ''} 腾召唤位`); return true; } diff --git a/assets/script/battle/ai/runtime/AiMapTextHost.ts b/assets/script/battle/ai/runtime/AiMapTextHost.ts index 71f40f0..4b3ce4c 100644 --- a/assets/script/battle/ai/runtime/AiMapTextHost.ts +++ b/assets/script/battle/ai/runtime/AiMapTextHost.ts @@ -1,5 +1,5 @@ import { Director, director, Tween, tween, v3, Vec3, type Node } from 'cc'; -import { canDragMapTile, Weapon2TileType, isElementGlyphType, type ComposedUnit, type DragSource, type DropTarget, type MapTileRecord } from 'db://assets/mx/module/merge'; +import { canDragMapTile, Weapon2TileType, isBagToolTextType, isElementGlyphType, type ComposedUnit, type DragSource, type DropTarget, type MapTileRecord } from 'db://assets/mx/module/merge'; import { aiLog, aiWarn } from './AiLog'; import type { BattleBagSession } from '../../bag/BattleBagSession'; import type { MapManager } from '../../map/legacy/MapManager'; @@ -157,6 +157,16 @@ export class AiMapTextHost { return false; } if (source.kind === 'bag' && target.kind === 'bag') return false; + // 功能符/产卡:瞬移生效,避免飞向已占格(含对方)时误藏目标格 HeroActor + if (source.kind === 'bag') { + const slot = bag.merge.bag.getSlot(source.slotIndex); + const cardKind = Math.floor(Number(slot?.battleCardKind) || 0); + if (cardKind > 0) return false; + if (slot && slot.weapon2Id > 0) { + const w2Type = Math.floor(WeaponComposeConfigProvider.instance.getWeapon2Type(slot.weapon2Id)); + if (isBagToolTextType(w2Type)) return false; + } + } const bagOrigin = this._resolveBagOriginWorld(); if (!bagOrigin) { diff --git a/assets/script/battle/combat/TowerQueryService.ts b/assets/script/battle/combat/TowerQueryService.ts index 24a87d3..f91a42f 100644 --- a/assets/script/battle/combat/TowerQueryService.ts +++ b/assets/script/battle/combat/TowerQueryService.ts @@ -388,15 +388,19 @@ export class TowerQueryService { composition: ComposedUnit | null, tileType: Weapon2TileType, ): number { - if (record.weapon1Id != null && record.weapon1Id > 0) return record.weapon1Id; - if (composition) return composition.weapon1Id; - if (record.weapon2Id <= 0) return 0; const provider = WeaponComposeConfigProvider.instance; - // 五行数字:按 type + 显示等级解析 Hero + // 五行:以 type+显示等级为准,避免升/降级后 tile.weapon1Id 陈旧盖住 composition if (isElementGlyphType(Number(tileType))) { const level = Math.max(1, Math.floor(Number(record.level) || 1)); - return provider.getElementHeroId(Number(tileType), level) ?? 0; + const fromLevel = provider.getElementHeroId(Number(tileType), level) ?? 0; + if (fromLevel > 0) return fromLevel; + if (composition && composition.weapon1Id > 0) return composition.weapon1Id; + if (record.weapon1Id != null && record.weapon1Id > 0) return record.weapon1Id; + return 0; } + if (record.weapon1Id != null && record.weapon1Id > 0) return record.weapon1Id; + if (composition) return composition.weapon1Id; + if (record.weapon2Id <= 0) return 0; // 蟠桃等非五行工具字:单 id 解析 return provider.getWeapon1ByGroupSequence([record.weapon2Id]) ?? 0; } diff --git a/assets/script/battle/view/CocosBagBoardView.ts b/assets/script/battle/view/CocosBagBoardView.ts index 3b5d41e..8ffd494 100644 --- a/assets/script/battle/view/CocosBagBoardView.ts +++ b/assets/script/battle/view/CocosBagBoardView.ts @@ -940,9 +940,10 @@ export class CocosBagBoardView { if (this._bagSession?.merge.map.hasTile(mapGrid)) { const tile = this._bagSession.merge.map.getTile(mapGrid); const level = tile?.level ?? 1; + const w1 = Number(tile?.weapon1Id) || 0; const grid = this._mapGrids.get(mapGrid); if (grid?.isValid) { - grid.applyMapLevel(level); + grid.applyMapLevel(level, w1); grid.enableDrag(this._canDrag()); this._bindDrag(grid); } @@ -964,7 +965,7 @@ export class CocosBagBoardView { const record = this._bagSession.getBagSlots()[bagSlot]; if (grid?.isValid && record) { this._grids.setBagGrid(bagSlot, grid); - grid.applyMapLevel(record.level ?? 1); + grid.applyMapLevel(record.level ?? 1, Number(record.weapon1Id) || 0); } this._sanitizeBagSlotAfterMerge(bagSlot); this._playBagUpgradeEffect(bagSlot); @@ -4593,7 +4594,7 @@ export class CocosBagBoardView { this._deferredBagUpgradeSlot = event.slotIndex; break; } - if (this._applyBagUpgradeInPlace(event.slotIndex, event.level)) { + if (this._applyBagUpgradeInPlace(event.slotIndex, event.level, event.weapon1Id)) { this._sanitizeBagSlotAfterMerge(event.slotIndex); if (!this._suppressDefaultUpgradeFx) { this._playBagUpgradeEffect(event.slotIndex); @@ -4614,7 +4615,7 @@ export class CocosBagBoardView { } const prevMapLv = Math.floor(Number(this._grids.getMapGrid(event.gridIndex)?.level) || 0); const mapLevelDown = event.level < prevMapLv; - if (this._applyMapUpgradeInPlace(event.gridIndex, event.level)) { + if (this._applyMapUpgradeInPlace(event.gridIndex, event.level, event.weapon1Id)) { this._refreshHeroLevelAtGrid(event.gridIndex, event.level); if (this._deploySync && this._map.gridsInitialized) { this._deploySync.reconcileFromMerge('mapUpgraded'); @@ -5471,9 +5472,12 @@ const w2Type = WeaponComposeConfigProvider.instance.getWeapon2Type(weapon2Id) as /** * 地图字块仅等级变化:原地刷新 TextGrid,避免全量 rebuild 叠字。 + * @param gridIndex 地图格 + * @param newLevel 新等级 + * @param weapon1Id 新 Hero.id(0 表示仅改等级) * @returns 是否已在原节点上更新成功 */ - private _applyMapUpgradeInPlace(gridIndex: number, newLevel: number): boolean { + private _applyMapUpgradeInPlace(gridIndex: number, newLevel: number, weapon1Id = 0): boolean { if (!this._bagSession || !this._map) return false; if (!this._bagSession.merge.map.hasTile(gridIndex)) return false; @@ -5483,13 +5487,14 @@ const w2Type = WeaponComposeConfigProvider.instance.getWeapon2Type(weapon2Id) as const indices = composition?.gridIndices.length ? composition.gridIndices.slice() : [gridIndex]; + const w1 = Math.floor(Number(weapon1Id) || 0) || (composition?.weapon1Id ?? 0); let applied = 0; for (let i = 0; i < indices.length; i++) { const idx = indices[i]; const grid = this._resolveMapTextGridForUpgrade(idx); if (!grid?.isValid) continue; - grid.applyMapLevel(newLevel); + grid.applyMapLevel(newLevel, w1); this._grids.setMapGrid(idx, grid); applied++; } @@ -5497,8 +5502,13 @@ const w2Type = WeaponComposeConfigProvider.instance.getWeapon2Type(weapon2Id) as return applied > 0; } - /** 背包字块仅等级变化:原地刷新 TextGrid */ - private _applyBagUpgradeInPlace(slotIndex: number, newLevel: number): boolean { + /** + * 背包字块仅等级变化:原地刷新 TextGrid。 + * @param slotIndex 槽 + * @param newLevel 新等级 + * @param weapon1Id 新 Hero.id + */ + private _applyBagUpgradeInPlace(slotIndex: number, newLevel: number, weapon1Id = 0): boolean { if (!this._bagSession || !this._bagList) return false; if (!this._bagSession.getBagSlots()[slotIndex]) return false; const slotNode = this._bagSlotNodeAt(slotIndex); @@ -5506,7 +5516,9 @@ const w2Type = WeaponComposeConfigProvider.instance.getWeapon2Type(weapon2Id) as const grid = this._resolveBagTextGridForUpgrade(slotIndex, slotNode); if (!grid?.isValid) return false; - grid.applyMapLevel(newLevel); + const slot = this._bagSession.getBagSlots()[slotIndex]; + const w1 = Math.floor(Number(weapon1Id) || 0) || (Number(slot?.weapon1Id) || 0); + grid.applyMapLevel(newLevel, w1); this._grids.setBagGrid(slotIndex, grid); return true; } @@ -6912,12 +6924,13 @@ const w2Type = WeaponComposeConfigProvider.instance.getWeapon2Type(weapon2Id) as return loc?.kind === 'map' && this._isEnemyPlaceCampCell(loc.gridIndex); } - /** 字块完整快照(敌方格补 ECS runtime) */ - private _resolveTextGridData(grid: TextGrid): TextGridData { + /** 字块完整快照(敌方格补 ECS runtime);无实体时 null */ + private _resolveTextGridData(grid: TextGrid): TextGridData | null { const deploySync = this._isEnemyTextGrid(grid) ? AiBattleBridge.inst.aiDeploySync : this._deploySync; const data = grid.getData(deploySync); + if (!data) return null; if (!this._isEnemyTextGrid(grid)) return data; const loc = grid.location; if (loc?.kind !== 'map') return data; @@ -6981,7 +6994,7 @@ const w2Type = WeaponComposeConfigProvider.instance.getWeapon2Type(weapon2Id) as /** 字块占格合成体(含敌方) */ private _resolveGridComposition(grid: TextGrid, gridIndex: number): ComposedUnit | null { - const fromEntity = this._resolveTextGridData(grid).composition; + const fromEntity = this._resolveTextGridData(grid)?.composition; if (fromEntity) return fromEntity; if (this._isEnemyPlaceCampCell(gridIndex)) { const enemyPlayer = this._battle?.getPlayer(this._resolveEnemyPlayerId()); @@ -7004,6 +7017,11 @@ const w2Type = WeaponComposeConfigProvider.instance.getWeapon2Type(weapon2Id) as this._textInteractDedupKey = dedupKey; const data = this._resolveTextGridData(grid); + if (!data) { + console.warn('[BagBoard] 字块交互无快照', target.kind, target.index, grid.weapon2Id); + this._clearTextSelect(); + return; + } const isPeachTree = grid.isPeachTreeTile(); const isBagItem = this._isBagItemEntity(data); // 五行数字与旧 Single=1 编号重叠,数字格子一律 Hero,勿再开 Desc1 @@ -7136,6 +7154,7 @@ const w2Type = WeaponComposeConfigProvider.instance.getWeapon2Type(weapon2Id) as ): Pick | null { if (!grid?.node?.isValid) return null; let data = this._resolveTextGridData(grid); + if (!data) return null; if (target.kind === 'map') { const comp = this._resolveGridComposition(grid, target.index); if (comp) { @@ -7215,6 +7234,7 @@ const w2Type = WeaponComposeConfigProvider.instance.getWeapon2Type(weapon2Id) as private _gridCanShowWeaponDesc(grid: TextGrid): boolean { const data = this._resolveTextGridData(grid); + if (!data) return false; if (grid.isPeachTreeTile()) return !!data.tile; if (this._isBagItemEntity(data)) return !!data.tile; return !!(data.tower && data.weapon1Id > 0); @@ -7249,6 +7269,7 @@ const w2Type = WeaponComposeConfigProvider.instance.getWeapon2Type(weapon2Id) as /** 字块攻击范围(像素):已部署优先读 runtime */ private _resolveGridRangePx(grid: TextGrid): number { const data = this._resolveTextGridData(grid); + if (!data) return 0; const px = data.runtime?.stats.range ?? data.tower?.stats.range ?? 0; return px > 0 ? px : 0; } @@ -7437,6 +7458,7 @@ const w2Type = WeaponComposeConfigProvider.instance.getWeapon2Type(weapon2Id) as private _canShowType1Range(grid: TextGrid | null): boolean { if (!grid?.node?.isValid) return false; const data = this._resolveTextGridData(grid); + if (!data) return false; if (this._isCampGridText(data)) return false; if (!data.tower && !data.runtime) return false; const rangePx = this._resolveGridRangePx(grid); diff --git a/assets/script/battle/view/TextGrid.ts b/assets/script/battle/view/TextGrid.ts index 3f480fb..044e78c 100644 --- a/assets/script/battle/view/TextGrid.ts +++ b/assets/script/battle/view/TextGrid.ts @@ -793,11 +793,18 @@ export class TextGrid extends Component { this.location = loc; } - /** 完整快照:实体走战斗层查询,视图层只补 UI 字段 */ - getData(deploySync?: DeploySync | null): TextGridData { + /** + * 完整快照:实体走战斗层查询,视图层只补 UI 字段。 + * @returns 无实体/location 时 null(点选勿崩) + */ + getData(deploySync?: DeploySync | null): TextGridData | null { const entity = this._aiMapLite && this._entity ? this._entity : this._queryEntity(); + if (!entity?.location) { + console.warn('[TextGrid] getData 无实体或 location', this.weapon2Id, this.node?.name); + return null; + } const runtime = entity.location.kind === 'map' && !this._aiMapLite ? TowerQueryService.instance.getRuntimeByGridIndex(entity.location.gridIndex, undefined, deploySync ?? null) : null; @@ -815,9 +822,10 @@ export class TextGrid extends Component { } onClickText(e: EventTouch): void { - const data = this.getData(); - const range = data.runtime?.stats.range ?? data.tower?.stats.range; - + if (!this.location && !this._entity?.location) { + console.warn('[TextGrid] onClick 无 location,忽略', this.weapon2Id); + return; + } if (this.location?.kind === 'map' || this.location?.kind === 'bag') { if (this._mapClickHandler) { e.propagationStopped = true; @@ -1078,21 +1086,23 @@ export class TextGrid extends Component { this.node.removeDragEvent?.(); } - private _queryEntity(): TileEntitySnapshot { + private _queryEntity(): TileEntitySnapshot | null { const location = this.location ?? this._entity?.location; - if (!location) return this._entity!; + if (!location) return this._entity; const composition = this._findComposition() ?? this._entity?.composition ?? null; const level = composition?.level ?? this.level; + const fromCompose = composition && composition.weapon1Id > 0 ? composition.weapon1Id : 0; + const weapon1Id = fromCompose > 0 ? fromCompose : (this.weapon1Id > 0 ? this.weapon1Id : undefined); return TowerQueryService.instance.buildTileEntity( location, { weapon2Id: this.weapon2Id, - weapon1Id: this.weapon1Id > 0 ? this.weapon1Id : undefined, + weapon1Id, level, source: this._entity?.source ?? undefined, }, { composition, player: gg.game.localPlayer }, - ) ?? this._entity!; + ) ?? this._entity; } private _findComposition(): ComposedUnit | null { @@ -2405,16 +2415,23 @@ export class TextGrid extends Component { return this.level; } - /** 地图字块等级变更后刷新数字皮(免全量 setData) */ - applyMapLevel(level: number): void { + /** + * 地图字块等级变更后刷新数字皮(免全量 setData)。 + * @param level 新显示等级 + * @param weapon1Id 新 Hero.id;>0 时同步,避免升/降级后陈旧 + */ + applyMapLevel(level: number, weapon1Id = 0): void { if (!this.isValid) return; const lv = Math.max(1, Math.floor(level) || 1); this.level = lv; + const w1 = Math.floor(Number(weapon1Id) || 0); + if (w1 > 0) this.weapon1Id = w1; if (this._entity) { + const nextW1 = w1 > 0 ? w1 : (Number(this._entity.weapon1Id) || this.weapon1Id); const comp = this._entity.composition - ? { ...this._entity.composition, level: lv } + ? { ...this._entity.composition, level: lv, weapon1Id: nextW1 > 0 ? nextW1 : this._entity.composition.weapon1Id } : null; - this._entity = { ...this._entity, level: lv, composition: comp }; + this._entity = { ...this._entity, level: lv, weapon1Id: nextW1, composition: comp }; } this._updateLevelLabel(); this._syncSkillActiveState(); diff --git a/assets/script/ui/BattleGame/UIBattleGame.ts b/assets/script/ui/BattleGame/UIBattleGame.ts index 04ce09b..d24f55c 100644 --- a/assets/script/ui/BattleGame/UIBattleGame.ts +++ b/assets/script/ui/BattleGame/UIBattleGame.ts @@ -991,16 +991,23 @@ export class UIBattleGame extends Auto_BattleGame { * @param gridIndex 地图格 */ private readonly _onPeerMapToolApplied = (peerOwnerPlayerId: number, gridIndex: number): void => { + if (!(gridIndex >= 0)) { + console.warn('[UIBattleGame] 衰弱符对端格无效', peerOwnerPlayerId, gridIndex); + return; + } if (peerOwnerPlayerId === AiHeadlessBagHost.AI_PLAYER_ID) { const bridge = AiBattleBridge.inst; bridge.aiDeploySync?.reconcileFromMerge('bagToolWeaken'); bridge.notifyAiBoardChanged(); return; } - // 本机盘面由 peer.merge 的 MapUpgraded 驱动 CocosBagBoardView - if (!(gridIndex >= 0)) { - console.warn('[UIBattleGame] 衰弱符对端格无效', peerOwnerPlayerId, gridIndex); + // AI 衰弱本机:MapUpgraded 已改视图等级;补 DeploySync,避免 Hero/装载仍绑旧等级 + const battle = this._battle; + if (!battle?.deploySync) { + console.warn('[UIBattleGame] 衰弱本机缺 DeploySync', peerOwnerPlayerId, gridIndex); + return; } + battle.deploySync.reconcileFromMerge('bagToolWeakenLocal'); }; /**