// deno task export-bworld // writes the base game's blocks, items and recipes into mods/bworld as json, from what the game registers now. // the game still loads the typescript definitions until the mod loader exists (phase 1 in MODS.md), // so run this again after changing them. tests/bworld_mod_test.ts fails when the two drift apart import "$/common/blocks/mod.ts"; import "$/common/items/mod.ts"; import { BlockRegistry, EverythingRegistry, ItemRegistry } from "$/common/everything_registry.ts"; import { block_to_json, grid_recipe_to_json, item_to_json, RecipeJson } from "$/common/mod_data.ts"; import { CRAFTING_RECIPES } from "$/server/game/crafting.ts"; import { FUEL_VALUES, FURNACE_RECIPES } from "$/server/game/blocks.ts"; const MOD_DIR = "mods/bworld"; const DATA_FOLDERS = ["blocks", "items", "recipes"]; function name_of(id: string) { return id.split(":")[1]; } // everything the base game registers, as the json files mods/bworld should contain export function bworld_data_files(): { path: string; content: unknown }[] { const files: { path: string; content: unknown }[] = []; const add = (folder: string, name: string, key: string, content: unknown) => files.push({ path: `${folder}/${name}.json`, content: { format_version: 1, [key]: content } }); const items = new Map(EverythingRegistry.entries("items")); for (const [id, block] of EverythingRegistry.entries("blocks")) { const has_item = items.get(id)?.block_id === id; add("blocks", name_of(id), "block", block_to_json(block, has_item)); } for (const [id, item] of items) { // block items come from their block's "item" field if (item.block_id === undefined) { add("items", name_of(id), "item", item_to_json(id, item)); } } for (const recipe of CRAFTING_RECIPES) { add("recipes", `crafting_${name_of(recipe.result.id)}`, "recipe", grid_recipe_to_json(recipe)); } for (const recipe of FURNACE_RECIPES) { add( "recipes", `smelting_${name_of(recipe.input)}`, "recipe", { type: "furnace", input: recipe.input, output: { id: recipe.output.type_id, count: recipe.output.amount }, cook_time: recipe.cook_time, } satisfies RecipeJson, ); } for (const [item, burn_time] of Object.entries(FUEL_VALUES)) { add("recipes", `fuel_${name_of(item)}`, "recipe", { type: "fuel", item, burn_time } satisfies RecipeJson); } return files; } if (import.meta.main) { for (const folder of DATA_FOLDERS) { try { Deno.removeSync(`${MOD_DIR}/${folder}`, { recursive: true }); } catch (e) { if (!(e instanceof Deno.errors.NotFound)) throw e; } Deno.mkdirSync(`${MOD_DIR}/${folder}`, { recursive: true }); } const files = bworld_data_files(); for (const { path, content } of files) { Deno.writeTextFileSync(`${MOD_DIR}/${path}`, JSON.stringify(content, null, "\t") + "\n"); } // match the repo's formatting so reruns don't show up as changes await new Deno.Command(Deno.execPath(), { args: ["fmt", "--quiet", ...DATA_FOLDERS.map((f) => `${MOD_DIR}/${f}`)] }) .output(); const count = (folder: string) => files.filter((f) => f.path.startsWith(`${folder}/`)).length; console.log( `Wrote ${count("blocks")} blocks, ${count("items")} items and ${count("recipes")} recipes to ${MOD_DIR}`, ); }