import fs from "fs"; import upath from "upath"; import { overridesFolder, configFolder, configOverridesFolder, sharedDestDirectory, } from "#globals"; import { Quest, QuestBook, QuestLine as QuestLine, } from "#types/bqQuestBook.ts"; const sharedQBDefaults = upath.join( sharedDestDirectory, configFolder, "betterquesting", ); const sharedConfigOverrides = upath.join( sharedDestDirectory, configOverridesFolder, ); const langFileLocation = "resources/questbook/lang"; function escapeString(string: string) { return string.replace(/%/g, "%%").replace(/\n/g, "%n"); } function transformKeyPairs( database: { [key: string]: T }, mode: string, namespace: string, lines: string[], getId: (item: T) => number, ) { Object.keys(database).forEach((key) => { const item = database[key]; const id = getId(item); const properties = item["properties:10"]["betterquesting:10"]; if (properties["name:8"] !== "Gap") { const titleKey = `nomifactory.quest.${mode}.${namespace}.${id}.title`; const descKey = `nomifactory.quest.${mode}.${namespace}.${id}.desc`; // Push lang file lines. lines.push( `# ${namespace} ${id} of mode ${mode}`.trimEnd(), `${titleKey}=${escapeString(properties["name:8"])}`.trimEnd(), `${descKey}=${escapeString(properties["desc:8"])}`.trimEnd(), "", ); properties["name:8"] = titleKey; properties["desc:8"] = descKey; } else { properties["name:8"] = ""; properties["desc:8"] = ""; } }); } /** * Trimming all that results in almost half the size of the original JSON file. * * Interesting, huh? */ const uselessProps: Record = { "simultaneous:1": 0, "ismain:1": 0, "repeat_relative:1": 1, "globalshare:1": 0, "repeattime:3": -1, "issilent:1": 0, "snd_complete:8": "minecraft:entity.player.levelup", "snd_update:8": "minecraft:entity.player.levelup", "tasklogic:8": "AND", "questlogic:8": "AND", "entryLogic:8": "AND", "visibility:8": "NORMAL", "partysinglereward:1": 0, "lockedprogress:1": 0, "OreDict:8": "", "Damage:2": 0, "Count:3": 0, "autoclaim:1": 0, "autoConsume:1": 0, "consume:1": 0, "groupDetect:1": 0, "ignoreNBT:1": 0, "index:3": 0, "partialMatch:1": 1, "ignoresview:1": 0, }; function stripUselessMetadata(object: Record) { Object.keys(object).forEach((propName) => { const prop = object[propName]; if (prop === uselessProps[propName]) { return delete object[propName]; } if (typeof prop === "object") { if (Array.isArray(prop) && prop.length === 0) { return delete object[propName]; } stripUselessMetadata(prop as Record); if (Object.keys(prop as Record).length === 0) { return delete object[propName]; } } }); } /** * Extract lang entries from the quest book and transform the database. */ export async function transformQuestBook(): Promise { // Source Quest Book File Locations const questPathNormalSource = upath.join( sharedQBDefaults, "DefaultQuests.json", ); const questPathExpertSource = upath.join( sharedQBDefaults, "saved_quests", "ExpertQuests.json", ); // Quest Book Objects const questBookNormal: QuestBook = JSON.parse( await fs.promises.readFile(questPathNormalSource, "utf-8"), ); const questBookExpert: QuestBook = JSON.parse( await fs.promises.readFile(questPathExpertSource, "utf-8"), ); // Quest Book Paths const questPathNormalDefault = upath.join( sharedQBDefaults, "DefaultQuests.json", ); const questPathNormalOverride = upath.join( sharedConfigOverrides, "normal", "betterquesting", "DefaultQuests.json", ); const questPathExpertDefault = upath.join( sharedQBDefaults, "saved_quests", "ExpertQuests.json", ); const questPathExpertOverride = upath.join( sharedConfigOverrides, "expert", "betterquesting", "DefaultQuests.json", ); // Quest Lang Location const questLangLocation = upath.join( sharedDestDirectory, overridesFolder, langFileLocation, ); // Traverse through the quest book and rewrite titles/descriptions. // Extract title/desc pairs into a lang file. const lines: string[] = []; lines.push( "# DO NOT EDIT THIS FILE!", "# THIS IS A SOURCE FILE ONLY!", "# See https://github.com/Nomi-CEu/Nomi-CEu/wiki/Part-1:-Contributing-Information#section-2-quest-book-contributing FOR HOW TO EDIT THE QUEST BOOK!", "", ); const questID = (item: Quest) => item["questID:3"]; const lineID = (item: QuestLine) => item["lineID:3"]; lines.push("# Normal Quest Lang Entries:", ""); // Normal Mode Quest lines. transformKeyPairs( questBookNormal["questLines:9"], "normal", "line", lines, lineID, ); // Normal Mode Quests themselves. transformKeyPairs( questBookNormal["questDatabase:9"], "normal", "db", lines, questID, ); lines.push("# Expert Quest Lang Entries:", ""); // Expert Mode Quest lines. transformKeyPairs( questBookExpert["questLines:9"], "expert", "line", lines, lineID, ); // Expert Mode Quests themselves. transformKeyPairs( questBookExpert["questDatabase:9"], "expert", "db", lines, questID, ); // Write lang file. await fs.promises.mkdir(questLangLocation, { recursive: true }); await fs.promises.writeFile( upath.join(questLangLocation, "en_us.lang"), lines.join("\n"), ); // Strip useless metadata. stripUselessMetadata(questBookNormal as unknown as Record); stripUselessMetadata(questBookExpert as unknown as Record); // Write QB files. await fs.promises.writeFile( questPathNormalDefault, JSON.stringify(questBookNormal, null, 2), ); await fs.promises.writeFile( questPathNormalOverride, JSON.stringify(questBookNormal, null, 2), ); await fs.promises.writeFile( questPathExpertDefault, JSON.stringify(questBookExpert, null, 2), ); return await fs.promises.writeFile( questPathExpertOverride, JSON.stringify(questBookExpert, null, 2), ); }