Hello World
This commit is contained in:
commit
7d342af29f
124 files changed
+627642
No files matched your search
@@ -0,0 +1,104 @@
|
||||
import * as e from './extract.mjs';
|
||||
import * as s from './stages.mjs';
|
||||
import {table_bindings} from './rounds.mjs';
|
||||
|
||||
// Client tables behind the Elite branch (binary node rule, enemy difficulty) and
|
||||
// the SpecialGoods-gated projections. Every table is decoded byte-exactly.
|
||||
export const TYPES = [
|
||||
'GridFightBinaryNodeRuleConfigRow',
|
||||
'GridFightBinaryDiffAddRuleConfigRow',
|
||||
'GridFightSpecialGoodsConfigRow',
|
||||
'GridFightProjectionConfigRow',
|
||||
'GridFightEnemyDifficultyLvConfigRow'
|
||||
];
|
||||
|
||||
// GridFightProjectionUnlockType: 0 TraitUnlock, 1 SpecialGoods.
|
||||
export const UNLOCK_SPECIAL_GOODS = 1;
|
||||
// Update-union case 1684 (CDKJHJNFDIH) -> GridFightModule.UpdateUnlockProjectionID (DC17B10).
|
||||
export const UNLOCK_PROJECTION_CASE = 1684;
|
||||
// SpecialGoods JsonPath folder names, keyed by GroupID (1 Cyrene, 2 Aha, 3 Aha upper).
|
||||
export const SERIES = ['Cyrene', 'Aha'];
|
||||
|
||||
// FixPoint wire is signed Q32.32.
|
||||
export function fixed(p) {
|
||||
return Math.round(Number(BigInt.asIntN(64, BigInt(p?.wire ?? 0))) / 4294967296 * 1e6) / 1e6;
|
||||
}
|
||||
|
||||
export function eliteResource(t) {
|
||||
const node_rules = t.GridFightBinaryNodeRuleConfigRow.map(r => ({
|
||||
id: r.ID,
|
||||
// Elite nodes carry ParamList[0] = family; option id = family * 100 + quality.
|
||||
family: r.ID >= 100 ? Math.floor(r.ID / 100) : 0,
|
||||
quality: r.Quality ?? 0,
|
||||
perform_level: r.PerformLevel ?? 0
|
||||
})).sort((a, b) => a.id - b.id);
|
||||
for (const r of node_rules) {
|
||||
if (r.id >= 100 && r.id !== r.family * 100 + r.quality) e.fail('Binary node rule id does not follow family*100+quality: ' + r.id);
|
||||
}
|
||||
const diff_add = t.GridFightBinaryDiffAddRuleConfigRow.map(r => ({
|
||||
rule: r.ID, quality: r.Quality, add: r.EnemyDifficultyAddValue ?? 0
|
||||
})).sort((a, b) => a.rule - b.rule || a.quality - b.quality);
|
||||
const difficulty = t.GridFightEnemyDifficultyLvConfigRow.map(r => ({
|
||||
chapter: r.ChapterID ?? 0,
|
||||
level: r.EnemyDifficultyLevel ?? 0,
|
||||
attack: fixed(r.AttackRatio),
|
||||
defence: fixed(r.DefenceRatio),
|
||||
hp: fixed(r.HPRatio),
|
||||
speed: fixed(r.SpeedRatio),
|
||||
stance: fixed(r.StanceRatio),
|
||||
damage_reduce: fixed(r.AllDamageReduce)
|
||||
})).sort((a, b) => a.chapter - b.chapter || a.level - b.level);
|
||||
return {schema: 1, node_rules, diff_add, difficulty, extra_rewards: []};
|
||||
}
|
||||
|
||||
export function goodsResource(t) {
|
||||
const projections = t.GridFightProjectionConfigRow.filter(r => r.UnlockType === UNLOCK_SPECIAL_GOODS).map(r => ({
|
||||
id: r.ID,
|
||||
role_id: r.RoleID,
|
||||
rarity: r.Rarity,
|
||||
mazebuff_id: r.MazebuffID,
|
||||
params: (r.ParamList ?? []).map(fixed)
|
||||
})).sort((a, b) => a.id - b.id);
|
||||
const special_goods = t.GridFightSpecialGoodsConfigRow.map(g => {
|
||||
const series = SERIES.find(n => g.JsonPath.includes('/' + n + '/'));
|
||||
if (!series) e.fail('Unknown SpecialGoods series: ' + g.JsonPath);
|
||||
return {
|
||||
id: g.ID,
|
||||
group: g.GroupID,
|
||||
series,
|
||||
cost: g.Cost ?? 0,
|
||||
quality: g.Quality ?? 0,
|
||||
lower_good_id: g.LowerGoodID ?? 0,
|
||||
params: (g.EffectParamList ?? []).map(fixed)
|
||||
};
|
||||
}).sort((a, b) => a.id - b.id);
|
||||
// The good -> projection entitlement lives in the SpecialGoods JsonPath assets,
|
||||
// which no installed table or tool decodes. Kept empty until proven.
|
||||
return {schema: 1, unlock_case: UNLOCK_PROJECTION_CASE, projections, special_goods, grants: []};
|
||||
}
|
||||
|
||||
export function main() {
|
||||
const a = e.cli(['client', 'dump', 'identity', 'literals', 'output']);
|
||||
const ctx = s.extractionContext(a);
|
||||
const literals = new Map(e.loadJSON(a.literals).map(r => [Number(BigInt(r.Address)), r.Value]));
|
||||
const tables = {}, proofs = [];
|
||||
for (const name of TYPES) {
|
||||
const assets = table_bindings(ctx.codec, literals, 'RPG.GameCore.' + name, null);
|
||||
tables[name] = [];
|
||||
for (const asset of assets) {
|
||||
const match = asset.match(/^BakedConfig\/ExcelOutput(GameCore)?\/(\w+)\.bytes$/);
|
||||
if (!match) e.fail('Unsupported table path: ' + asset);
|
||||
const [rows, proof] = s.table(ctx.assets, ctx.codec, name, match[1] ?? '', match[2]);
|
||||
tables[name].push(...rows);
|
||||
proofs.push(proof);
|
||||
console.log(`${name}: ${rows.length} byte-exact rows`);
|
||||
}
|
||||
}
|
||||
const source = {dump_sha256: ctx.identity.dump_sha256, game_assembly_sha256: ctx.identity.game_assembly_sha256, tables: TYPES};
|
||||
s.publish(a, ctx, tables, proofs, {
|
||||
'rewards.raw.json': tables,
|
||||
'elite_rewards.json': {...eliteResource(tables), source},
|
||||
'projection_goods.json': {...goodsResource(tables), source}
|
||||
}, 'rewards', {gameplay_verified: false});
|
||||
}
|
||||
e.runMain(import.meta.url, main);
|
||||
Reference in new issue
Block a user