Files
Currency-War-SR/tools/gridfight-extract-node/trait_effects.mjs
T
2026-10-30 23:54:07 +07:00

138 lines
4.0 KiB
JavaScript

import * as s from './stages.mjs';
const e=s.primitive;
export class Reader {
constructor(data){
this.b=data;
this.p=1;
}
var(){
let v=0n,
shift=0n;
for(; ; ){
if(this.p>=this.b.length)e.fail('Truncated table varint');
const x=this.b[this.p++];
v|=BigInt(x&127)<<shift;
shift+=7n;
if(x<128)return e.small(v);
}
}
text(){
const n=Number(this.var());
if(this.p+n>this.b.length)e.fail('Truncated table string');
const text=new TextDecoder('utf-8', {
fatal:true
}).decode(this.b.subarray(this.p, this.p+n));
this.p+=n;
return text;
}
done(name){
if(this.p!==this.b.length)e.fail(`${name}: decode stopped at ${this.p} of ${this.b.length}`);
}
}
export const zigzag=e.zigzag;
export function fixpoint(wire){
const raw=BigInt(zigzag(wire));
return Number(raw&~1n)/2**((raw&1n)?26:33);
}
// Python round uses ties-to-even; retain a float marker for identical JSON 0.0/1.0.
export function rounded(v, n=6){
const scale=10**n,
x=v*scale,
lo=Math.floor(x),
f=x-lo;
return new e.Float((f===0.5?(lo%2===0?lo:lo+1):Math.round(x))/scale);
}
export function effects(raw){
const r=new Reader(raw),
rows=e.dict(),
count=Number(r.var())/2;
for(let i=0; i<Math.floor(count); i++){
const mask=BigInt(r.var()),
row={
};
if(mask&1n)row.id=r.var();
if(mask&2n)row.type=zigzag(r.var());
if(mask&4n)row.f2=r.var();
if(mask&8n)row.json=r.text();
if(mask&16n)row.icon=r.text();
if(mask>>5n)e.fail('GridFightTraitEffect: unknown fields');
if(row.id===undefined||row.type===undefined)e.fail('Missing effect id/type');
rows[row.id]=row.type;
}
r.done('GridFightTraitEffect');
return rows;
}
export function layer_params(raw){
const r=new Reader(raw),
rows=e.dict(),
count=Number(r.var())/2;
for(let i=0; i<Math.floor(count); i++){
const mask=BigInt(r.var()),
row={
id:0,
layer:0,
params:[]
};
if(mask&1n)row.id=r.var();
if(mask&2n)row.layer=r.var();
if(mask&4n)row.params=Array.from({
length:Math.floor(Number(r.var())/2)
}, ()=>rounded(fixpoint(r.var())));
if(mask&8n)row.f3=[r.var(), r.var()];
if(mask&16n){
const count=Math.floor(Number(r.var())/2);
row.doubles=Array.from({
length:count
}, (_, k)=>r.b.readDoubleLE(r.p+k*8));
r.p+=8*count;
}
if(mask>>5n)e.fail('GridFightTraitEffectLayerPa: unknown fields');
rows[row.id]??=e.dict();
rows[row.id][row.layer]=row.params;
}
r.done('GridFightTraitEffectLayerPa');
return rows;
}
export function constants(raw){
const r=new Reader(raw);
function value(tag){
if(tag===0)return zigzag(r.var());
if(tag===6)return Array.from({
length:Math.floor(Number(r.var())/2)
}, ()=>value(r.b[r.p++]));
if(tag===10)return r.text();
e.fail('GridFightTraitConstValueS2: unknown value tag '+tag);
}
const rows=e.dict(),
count=Math.floor(Number(r.var())/2);
for(let i=0; i<count; i++){
if(r.var()!==3)e.fail('GridFightTraitConstValueS2: unknown fields');
const key=r.text(),
tag=r.b[r.p++];
rows[key]=value(tag);
}
r.done('GridFightTraitConstValueS2');
return rows;
}
export function main(){
const a=e.cli(['client'], {
output:e.resource('trait_effects.json')
}),
assets=new s.Assets(a.client),
proofs=[],
out={
};
for(const [key, name, decode]of [['effects', 'GridFightTraitEffect', effects], ['layer_params', 'GridFightTraitEffectLayerPa', layer_params], ['constants', 'GridFightTraitConstValueS2', constants]]){
const [raw, proof]=assets.get('BakedConfig/ExcelOutputGameCore/'+name+'.bytes');
out[key]=decode(raw);
proofs.push({
table:key,
...proof
});
}
out.provenance=proofs;
e.atomic_json(a.output, out, 1, true);
console.log(`${a.output}: ${Object.keys(out.effects).length} effects, ${Object.keys(out.layer_params).length} effect param sets, ${Object.keys(out.constants).length} constants`);
}
e.runMain(import.meta.url, main);