|
|
|
|
@ -1,4 +1,5 @@ |
|
|
|
|
import { |
|
|
|
|
BattleBagCardKind, |
|
|
|
|
MapCellKind, |
|
|
|
|
Weapon2TileType, |
|
|
|
|
isElementGlyphType, |
|
|
|
|
@ -188,6 +189,94 @@ export class AiBoardProbe { |
|
|
|
|
return bestMap ? { source: bestMap.source, target: bestMap.target } : null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 倍率卡/属性卡:拖到己方可拖开启格或地图五行数字(优先场上、高等级)。 |
|
|
|
|
* @param session 会话 |
|
|
|
|
* @param map 地图 |
|
|
|
|
*/ |
|
|
|
|
findBattleCardAction(session: BattleBagSession, map: MapManager): { source: DragSource; target: DropTarget } | null { |
|
|
|
|
WeaponComposeConfigProvider.instance.load(); |
|
|
|
|
const provider = WeaponComposeConfigProvider.instance; |
|
|
|
|
const slots = session.getBagSlots(); |
|
|
|
|
let best: { source: DragSource; target: DropTarget; score: number } | null = null; |
|
|
|
|
|
|
|
|
|
for (let s = 0; s < slots.length; s++) { |
|
|
|
|
const card = slots[s]; |
|
|
|
|
if (!card) continue; |
|
|
|
|
const kind = Math.floor(Number(card.battleCardKind) || 0); |
|
|
|
|
if (kind !== BattleBagCardKind.Multiplier && kind !== BattleBagCardKind.Attr) continue; |
|
|
|
|
if (!this._canDragBagSlot(session, s)) continue; |
|
|
|
|
|
|
|
|
|
for (let t = 0; t < slots.length; t++) { |
|
|
|
|
if (t === s) continue; |
|
|
|
|
const dst = slots[t]; |
|
|
|
|
if (!dst || dst.battleCardKind) continue; |
|
|
|
|
const land = session.landStateOf?.(t) ?? TextListCellState.Open; |
|
|
|
|
if (land !== TextListCellState.Open) continue; |
|
|
|
|
if (!this._isBattleCardDigitTarget(provider, dst.weapon2Id, dst.level ?? 1, kind)) continue; |
|
|
|
|
const score = this._scoreBattleCardTarget(provider, dst.weapon2Id, dst.level ?? 1, false, t); |
|
|
|
|
if (!best || score > best.score) { |
|
|
|
|
best = { |
|
|
|
|
source: { kind: 'bag', slotIndex: s }, |
|
|
|
|
target: { kind: 'bag', slotIndex: t }, |
|
|
|
|
score, |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const grids = this._listActiveGrids(map); |
|
|
|
|
for (let g = 0; g < grids.length; g++) { |
|
|
|
|
const mg = grids[g]; |
|
|
|
|
const tile = session.merge.map.getTile(mg.Index); |
|
|
|
|
if (!tile || tile.battleCardKind) continue; |
|
|
|
|
if (!session.validator.isAllowedCampForPlace(mg.Index)) continue; |
|
|
|
|
if (!this._isBattleCardDigitTarget(provider, tile.weapon2Id, tile.level ?? 1, kind)) continue; |
|
|
|
|
const score = this._scoreBattleCardTarget(provider, tile.weapon2Id, tile.level ?? 1, true, mg.Index); |
|
|
|
|
if (!best || score > best.score) { |
|
|
|
|
best = { |
|
|
|
|
source: { kind: 'bag', slotIndex: s }, |
|
|
|
|
target: { kind: 'map', gridIndex: mg.Index }, |
|
|
|
|
score, |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return best ? { source: best.source, target: best.target } : null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 产卡目标是否为可生效五行数字。 |
|
|
|
|
* @param provider 配表 |
|
|
|
|
* @param weapon2Id 目标字 |
|
|
|
|
* @param level 显示等级 |
|
|
|
|
* @param kind 卡种类 |
|
|
|
|
*/ |
|
|
|
|
private _isBattleCardDigitTarget(provider: WeaponComposeConfigProvider, weapon2Id: number, level: number, kind: number): boolean { |
|
|
|
|
const w2Type = Math.floor(provider.getWeapon2Type(weapon2Id)); |
|
|
|
|
if (!isElementGlyphType(w2Type)) return false; |
|
|
|
|
const lv = Math.max(1, Math.floor(Number(level) || 1)); |
|
|
|
|
if (kind === BattleBagCardKind.Multiplier) { |
|
|
|
|
return lv < provider.getElementMaxLevel(w2Type); |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 产卡落点评分:场上优先、等级高优先、下标稳定。 |
|
|
|
|
* @param provider 配表 |
|
|
|
|
* @param weapon2Id 目标字 |
|
|
|
|
* @param level 显示等级 |
|
|
|
|
* @param onMap 是否地图 |
|
|
|
|
* @param index 槽/格下标 |
|
|
|
|
*/ |
|
|
|
|
private _scoreBattleCardTarget(provider: WeaponComposeConfigProvider, weapon2Id: number, level: number, onMap: boolean, index: number): number { |
|
|
|
|
const lv = Math.max(1, Math.floor(Number(level) || 1)); |
|
|
|
|
const w2Type = Math.floor(provider.getWeapon2Type(weapon2Id)); |
|
|
|
|
const maxLv = isElementGlyphType(w2Type) ? provider.getElementMaxLevel(w2Type) : lv; |
|
|
|
|
return (onMap ? 100000 : 0) + lv * 1000 + (maxLv - lv) + (1000 - (index % 1000)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 空 Put 格是否可落五行字。 |
|
|
|
|
* @param session 会话 |
|
|
|
|
|