Files
2026-10-30 23:54:07 +07:00

149 lines
5.6 KiB
JavaScript

import * as e from './common.mjs';
export function build(raw, season){
const equips=new Map(raw.GridFightEquipConfigRow.map(r=>[r.ID, r])),
seasonItems=new Set(raw.GridFightSeasonItemConfigRow.filter(r=>r.SeasonID===season).map(r=>r.ItemID)),
seasonCrafts=new Set(raw.GridFightSeasonCraftConfigRow.filter(r=>r.SeasonID===season).map(r=>r.CraftID)),
pool=[];
for(const id of [...seasonItems].sort((a, b)=>a-b)){
const row=equips.get(id);
if(!row)continue;
const entry={
id,
category:row.EquipCategory??0
};
if(row.EquipFunc){
entry.func=row.EquipFunc;
entry.func_params=row.EquipFuncParamList??[];
}
if(row.DressRule){
entry.dress_rule=row.DressRule;
entry.dress_params=row.DressRuleParamList??[];
}
pool.push(entry);
}
const basic=pool.filter(e=>e.category===1).map(e=>e.id),
recipes=[];
for(const row of raw.GridFightCraftConfigRow){
if(!seasonCrafts.has(row.CraftID))continue;
const cost=row.CostEquipList??[];
if(cost.length!==2||!equips.has(row.CraftEquipID)||cost.some(c=>!equips.has(c)))e.fail('craft '+row.CraftID+' has an unexpected shape');
recipes.push({
craft_id:row.CraftID,
result:row.CraftEquipID,
cost:cost.toSorted((a, b)=>a-b)
});
}
recipes.sort((a, b)=>a.craft_id-b.craft_id);
if(!basic.length||!recipes.length)e.fail('season has no basic components or recipes');
const poolIds=new Set(pool.map(e=>e.id)),
consumables=raw.GridFightConsumablesConfigRow.filter(r=>seasonItems.has(r.ID)).map(r=>({
id:r.ID,
rule:r.ConsumableRule??0,
params:r.ConsumableParamList??[],
stack:!!r.IfStack,
consume:!!r.IfConsume
})),
upgrades=raw.GridFightEquipUpgradeConfigRow.filter(r=>poolIds.has(r.PreID)&&poolIds.has(r.UpgradeID)).map(r=>({
from:r.PreID,
to:r.UpgradeID
}));
return {
season_id:season,
basic,
equipment:pool,
recipes,
consumables,
upgrades
};
}
export function add_catalog(data, catalog, seasonItems){
const tables=catalog.tables,
poolIds=new Set(data.equipment.map(e=>e.id)),
recommend=e.dict();
for(const r of tables.GridFightRoleRecommendEquipConfigRow){
const ids=(r.FirstRecommendEquipList??[]).filter(i=>poolIds.has(i)),
merged=recommend[r.RoleID]??=[];
for(const id of ids)if(!merged.includes(id))merged.push(id);
}
data.recommend=e.dict(Object.keys(recommend).sort((a, b)=>Number(a)-Number(b)).filter(k=>recommend[k].length).map(k=>[k, recommend[k]]));
const byId=new Map(data.equipment.map(e=>[e.id, e])),
items=new Map(catalog.seasons[data.season_id].items.map(i=>[i.id, i]));
for(const [id, target]of [[353301, 'wearer'], [353401, 'wearer'], [353501, 'teammate']]){
if(!byId.has(id))continue;
const row=e.need(items, id).equipment,
text=row.EquipDesc.text,
expected=target==='teammate'?'random teammate':'copy of this unit';
if(!text.includes(expected)||e.json(row.DressRuleParamList)!==e.json([150602]))e.fail('chip '+id+': reward contract changed; review matching client');
const period=row.ParamList[0].value;
if(period<=0||!Number.isInteger(period))e.fail('chip '+id+': invalid battle cadence');
byId.get(id).battle_reward={
period,
target
};
if(target==='wearer'){
const chance=row.ParamList[2].value;
if(!text.includes('whenever the wearer is obtained through any means')||!(chance>0&&chance<1))e.fail('chip '+id+': acquisition contract changed');
byId.get(id).acquisition_chance=chance;
}
}
if(byId.has(353507)){
const row=e.need(items, 353507).equipment;
if(!row.EquipDesc.text.includes('For every 2 battles')||e.json(row.EquipFuncParamList)!==e.json([100302]))e.fail('Phantasmoon cartridge reward contract changed');
byId.get(353507).battle_reward={
period:2,
target:'bond',
bond:100302
};
}
data.sw_console=tables.GridFightSPSilverWolfChooseConfigRow.filter(r=>poolIds.has(r.EquipID)).map(r=>({
star:r.Star??0,
equip:r.EquipID
}));
data.clara=tables.GridFightClaraEnhanceConfigRow.map(r=>({
id:r.ID,
category:r.Category??0,
cost:r.CostCoin??0,
limit:r.Limit??0,
module:'EnhanceSelectName'in r,
buff:r.BuffID??0
}));
data.clara_links=tables.GridFightClaraLinkRelationConfigRow.map(r=>({
id:r.ID,
convert:r.EnhanceConvert??[],
valid:r.ValidEnhanceList??[]
}));
data.phainon=tables.GridFightPhanionUniqueTraitConfigRow.map(r=>{
const normal=r.SavedValueAddList??[],
special=r.SpecialSavedValueAddList??[];
if(normal.length!==1||special.length!==1||normal[0][0]!=='GP_Avatar_S2_Phainon_00'||special[0][0]!==normal[0][0])e.fail(`Phainon row ${r.Rarity}/${r.Star}: saved value contract changed`);
return {
cost:r.Rarity,
star:r.Star,
add:normal[0][1],
special_add:special[0][1]
};
});
data.forges=tables.GridFightForgeConfigRow.filter(r=>seasonItems.has(r.ID)).map(r=>({
id:r.ID,
category:r.EquipCategory??0,
num:r.EquipNum??0,
func:r.FuncType??0,
params:r.ParamList??[]
}));
}
export function main(){
const a=e.cli([], {
items:e.resource('items.raw.json'),
season:2,
output:e.resource('equipment.json'),
catalog:e.catalogDefault
}),
raw=e.loadJSON(a.items),
data=build(raw, a.season),
items=new Set(raw.GridFightSeasonItemConfigRow.filter(r=>r.SeasonID===a.season).map(r=>r.ItemID));
add_catalog(data, e.loadJSON(a.catalog), items);
e.atomic_json(a.output, data, 1, true);
console.log(`${a.output}: ${data.equipment.length} equipment, ${data.basic.length} basic, ${data.recipes.length} recipes for season ${a.season}`);
}
e.runMain(import.meta.url, main);