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.
 
 
 
 
AdLike2048/assets/script/battle/bag/WeaponComposeConfigProvider.ts

203 lines
7.2 KiB

import type { IComposeConfigProvider } from 'db://assets/mx/module/merge';
import {
createMembershipSet,
membershipAdd,
membershipHas,
mapValuesToArray,
type MembershipSet,
} from '../../../mx/tools/MembershipSet';
import { Weapon2TileType } from 'db://assets/mx/module/merge';
import { TableNames } from '../../game/ConfigTableData';
import { PEACH_TREE_MAX_LEVEL, PEACH_TREE_WEAPON2_ID } from './BagPrepConfig';
/** 归一化 Weapon2 数值字段(抖音/热更 JSON 常见字符串数字) */
function normalizeWeapon2Row(w: ITableText): ITableText | null {
const id = Number(w.id);
if (!Number.isFinite(id)) return null;
return {
...w,
id,
type: Number(w.type),
weight: Number(w.weight),
};
}
/** Weapon1 / Weapon2 配表 → MX 合成配置 */
export class WeaponComposeConfigProvider implements IComposeConfigProvider {
private static _instance: WeaponComposeConfigProvider | null = null;
private _weapon1List: ITableHero[] = [];
private _weapon2Map = new Map<number, ITableText>();
private _groupIndex = new Map<string, number>();
private _maxLevel = new Map<number, number>();
private _loaded = false;
static get instance(): WeaponComposeConfigProvider {
if (!this._instance) {
this._instance = new WeaponComposeConfigProvider();
}
return this._instance;
}
/** 配表热更后调用,强制下次 load 重读 */
invalidate(): void {
this._loaded = false;
this._weapon2Map.clear();
this._weapon1List = [];
this._groupIndex.clear();
this._maxLevel.clear();
}
load(): void {
if (this._loaded) return;
const w1List = gg.data.table.getTableList<ITableHero>(TableNames.Hero) ?? [];
const w2List = gg.data.table.getTableList<ITableText>(TableNames.Text) ?? [];
// Weapon2 是字块 type 的权威来源;H5 可能先到 Weapon1,此时绝不能 _loaded=true 锁死空表
if (w2List.length === 0) {
return;
}
this._weapon2Map.clear();
for (const w of w2List) {
const row = normalizeWeapon2Row(w);
if (!row) continue;
this._weapon2Map.set(row.id, row);
}
if (this._weapon2Map.size === 0) {
return;
}
this._weapon1List = w1List;
this._groupIndex.clear();
for (const w1 of this._weapon1List) {
const group = w1.text;
if (!Array.isArray(group)) continue;
const key = group.join(',');
this._groupIndex.set(key, Number(w1.id));
}
this._maxLevel.clear();
for (const w1 of this._weapon1List) {
if (Number(w1.level_limit) > 0) {
this._maxLevel.set(Number(w1.id), Number(w1.level_limit));
}
}
this._loaded = true;
}
/** Weapon2 解锁章节(新产品无 unlock 字段,默认第 1 章可用) */
getWeapon2UnlockChapter(_weapon2: ITableText): number {
return 1;
}
/** 背包刷新候选:weight > 0、当前章节已解锁;单字/组合字须属于局外上阵武器的 text,铲不受此限 */
getWeapon2RefreshCandidates(chapterId: number): ITableText[] {
this.load();
const equippedWeapon2Ids = this._getEquippedWeapon2IdSet();
const result: ITableText[] = [];
for (const w2 of this._weapon2Map.values()) {
const w2Type = Number(w2.type);
const isShovel = w2Type === Weapon2TileType.Shovel;
if (Number(w2.weight) <= 0) continue;
if (this.getWeapon2UnlockChapter(w2) > chapterId) continue;
if (w2Type === Weapon2TileType.Grid) continue;
if (!isShovel && !membershipHas(equippedWeapon2Ids, Number(w2.id))) continue;
result.push(w2);
}
return result;
}
/** 局外上阵武器 text:基础上阵单字(herotype===1)+ 英雄组合字(herotype===2) */
private _getEquippedWeapon2IdSet(): MembershipSet<number> {
const ids = createMembershipSet<number>();
for (const weapon of gg.data.project.getEquippedWeaponDataList()) {
for (const weapon2Id of weapon.text ?? []) {
const id = Number(weapon2Id);
if (Number.isFinite(id) && id > 0) membershipAdd(ids, id);
}
}
return ids;
}
getAllWeapon2Configs(): readonly ITableText[] {
this.load();
return mapValuesToArray(this._weapon2Map);
}
getWeapon2Type(weapon2Id: number): number {
this.load();
const id = Number(weapon2Id);
const row = this._weapon2Map.get(id);
const t = Number(row?.type);
return Number.isFinite(t) ? t : 0;
}
getWeapon1Group(weapon1Id: number): readonly number[] {
this.load();
const w1 = this._weapon1List.find((w) => Number(w.id) === Number(weapon1Id));
return w1?.text ?? [];
}
getWeapon1ByGroupSequence(sequence: readonly number[]): number | null {
this.load();
const key = sequence.join(',');
return this._groupIndex.get(key) ?? null;
}
getWeapon2IdForWeapon1(weapon1Id: number): number | null {
const group = this.getWeapon1Group(weapon1Id);
return group.length > 0 ? group[0] : null;
}
getMaxWeaponLevel(weapon1Id: number): number {
this.load();
const id = Number(weapon1Id);
const cached = this._maxLevel.get(id);
if (cached != null) return cached;
const row = this._weapon1List.find((w) => Number(w.id) === id);
if (!row) {
console.log('[WeaponCompose] Hero 缺失,等级上限按 1', id);
} else {
console.log('[WeaponCompose] Hero.level_limit 未配置,等级上限按 1', id);
}
return 1;
}
isSingleCellWeapon1(weapon1Id: number): boolean {
return this.getWeapon1Group(weapon1Id).length === 1;
}
/** 被动 buff 可在 Dig 格产出的 Weapon2(单字/组合字/蟠桃树) */
allowsPassiveDigSpawn(weapon2Id: number): boolean {
this.load();
const id = Number(weapon2Id);
const w2Type = this.getWeapon2Type(id) as Weapon2TileType;
if (w2Type === Weapon2TileType.Single || w2Type === Weapon2TileType.Combo) return true;
return id === PEACH_TREE_WEAPON2_ID;
}
/** 蟠桃树(102):空 Dig/Put 可落,落到 Put 后转 Dig,可拖回背包 */
passiveSpawnKeepsDigCell(weapon2Id: number): boolean {
this.load();
return Number(weapon2Id) === PEACH_TREE_WEAPON2_ID;
}
/** 蟠桃树合成等级上限(读 Hero.level_limit);其它 weapon2 返回 1 */
getKeepDigTileMaxLevel(weapon2Id: number): number {
this.load();
if (!this.passiveSpawnKeepsDigCell(weapon2Id)) return 1;
const w1 = this.getWeapon1ByGroupSequence([Number(weapon2Id)]);
if (w1 == null) {
console.log('[WeaponCompose] 蟠桃树无对应 Hero,等级上限用常量', weapon2Id, PEACH_TREE_MAX_LEVEL);
return PEACH_TREE_MAX_LEVEL;
}
return this.getMaxWeaponLevel(w1);
}
getWeapon2Config(weapon2Id: number): ITableText | null {
this.load();
return this._weapon2Map.get(Number(weapon2Id)) ?? null;
}
}