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