// features/mb_trees.js — christmas / light / potted trees as multiblocks. import { BlockPermutation, ItemStack, isCreative, getDir, safeGetBlock } from "../core/mc.js"; import { grid, fcCanReplace } from "../core/grid.js"; import { undoPlace, breakAir } from "../core/fx.js"; import { onPlace, onBreak } from "../core/events.js"; // Rainbow and white are INDEPENDENT block sets (white id = rainbow id + "_w"), each registered as a plain multiblock — no // rzb_dcs:tc state. Pick-block therefore returns the correct item (and name) natively; brush.js converts between the two. // --- grid trees (christmas / light): a 3D cell grid placed from the anchor cell --- function registerGridTree(cfg) { // cfg: ANCHOR, ANCHOR_KEY, CELLS, cellWorld, anchorFrom, parse, label, actionBar? onPlace((ev) => { const b = ev.block; if (b?.typeId !== cfg.ANCHOR) return; const dim = b.dimension, dir = getDir(b.permutation); for (const c of Object.keys(cfg.CELLS)) { if (c === cfg.ANCHOR_KEY) continue; const nb = safeGetBlock(dim, cfg.cellWorld(c, b.x, b.y, b.z, dir)); if (!fcCanReplace(nb)) { undoPlace(b, ev.player, cfg.ANCHOR, cfg.label); return; } } let placed = 0; for (const c of Object.keys(cfg.CELLS)) { if (c === cfg.ANCHOR_KEY) continue; const nb = dim.getBlock(cfg.cellWorld(c, b.x, b.y, b.z, dir)); if (fcCanReplace(nb)) { try { nb.setPermutation(BlockPermutation.resolve("rzb_dcs:" + c, { "minecraft:cardinal_direction": dir })); placed++; } catch {} } } if (cfg.actionBar) { try { ev.player.onScreenDisplay.setActionBar(cfg.actionBar(placed, Object.keys(cfg.CELLS).length - 1)); } catch {} } }); onBreak((ev) => { const cell = cfg.parse(ev.brokenBlockPermutation?.type?.id || ""); if (!cell) return; const b = ev.block, dir = getDir(ev.brokenBlockPermutation), a = cfg.anchorFrom(cell, b.x, b.y, b.z, dir); for (const c of Object.keys(cfg.CELLS)) { if (c === cell) continue; const nb = safeGetBlock(b.dimension, cfg.cellWorld(c, a.x, a.y, a.z, dir)); if (nb && cfg.parse(nb.typeId) === c) { try { breakAir(nb); } catch {} } } if (ev.player && !isCreative(ev.player)) { try { b.dimension.spawnItem(new ItemStack(cfg.ANCHOR, 1), { x: b.x + 0.5, y: b.y + 0.5, z: b.z + 0.5 }); } catch {} } }); } // register a grid tree family as BOTH a rainbow set and a "_w" white set, returning the parse/grid helpers of each variant. function gridTreeFamily(CELLS, anchorKey, label, actionBar) { const parseIn = (M) => (id) => { if (!id || !id.startsWith("rzb_dcs:")) return null; const c = id.slice(8); return M[c] ? c : null; }; const gR = grid(CELLS, true), parseR = parseIn(CELLS); registerGridTree({ ANCHOR: "rzb_dcs:" + anchorKey, ANCHOR_KEY: anchorKey, CELLS, cellWorld: gR.cellWorld, anchorFrom: gR.anchorFrom, parse: parseR, label, actionBar }); const CELLS_W = {}; for (const k in CELLS) CELLS_W[k + "_w"] = CELLS[k]; const gW = grid(CELLS_W, true), parseW = parseIn(CELLS_W); registerGridTree({ ANCHOR: "rzb_dcs:" + anchorKey + "_w", ANCHOR_KEY: anchorKey + "_w", CELLS: CELLS_W, cellWorld: gW.cellWorld, anchorFrom: gW.anchorFrom, parse: parseW, label: label + " (white)", actionBar }); return { CELLS, parse: parseR, cellWorld: gR.cellWorld, anchorFrom: gR.anchorFrom, CELLS_W, parseW, cellWorldW: gW.cellWorld, anchorFromW: gW.anchorFrom }; } export const CT_CELLS = { "ct_1_1_0_1": [0, 0, 0], "ct_1_1_0_0": [0, 0, -1], "ct_1_0_0_1": [1, 0, 0], "ct_1_1_0_2": [0, 0, 1], "ct_1_2_0_1": [-1, 0, 0], "ct_1_1_1_1": [0, 1, 0], "ct_1_1_2_1": [0, 2, 0], "ct_1_1_3_1": [0, 3, 0], "ct_1_1_1_0": [0, 1, -1], "ct_1_2_1_1": [-1, 1, 0], "ct_1_0_1_1": [1, 1, 0], "ct_1_1_1_2": [0, 1, 1], "ct_1_1_2_0": [0, 2, -1], "ct_1_0_2_1": [1, 2, 0], "ct_1_1_2_2": [0, 2, 1], "ct_1_2_0_0": [-1, 0, -1], "ct_1_0_0_0": [1, 0, -1], "ct_1_0_0_2": [1, 0, 1], "ct_1_2_0_2": [-1, 0, 1], "ct_1_2_2_1": [-1, 2, 0], "ct_1_1_3_2": [0, 3, 1], "ct_1_1_3_0": [0, 3, -1], "ct_1_0_3_1": [1, 3, 0], "ct_1_2_2_2": [-1, 2, 1], "ct_1_2_3_2": [-1, 3, 1], "ct_1_2_2_0": [-1, 2, -1], "ct_1_0_2_2": [1, 2, 1], "ct_1_2_3_0": [-1, 3, -1], "ct_1_0_3_2": [1, 3, 1], "ct_1_2_3_1": [-1, 3, 0], "ct_1_0_2_0": [1, 2, -1], "ct_1_0_3_0": [1, 3, -1], "ct_1_2_1_0": [-1, 1, -1], "ct_1_0_1_2": [1, 1, 1], "ct_1_2_1_2": [-1, 1, 1], "ct_1_0_1_0": [1, 1, -1] }; const CT = gridTreeFamily(CT_CELLS, "ct_1_1_0_1", "christmas tree", (n, tot) => "§aChristmas tree: placed " + n + "/" + tot + " cells"); export const ctParse = CT.parse, ctCellWorld = CT.cellWorld, ctAnchorFrom = CT.anchorFrom; export const CT_CELLS_W = CT.CELLS_W, ctParseW = CT.parseW, ctCellWorldW = CT.cellWorldW, ctAnchorFromW = CT.anchorFromW; export const LT_CELLS = { "lt_1_1_2_1": [0, 2, 0], "lt_1_1_1_1": [0, 1, 0], "lt_1_1_0_1": [0, 0, 0], "lt_1_2_0_0": [-1, 0, -1], "lt_1_0_0_2": [1, 0, 1], "lt_1_0_0_0": [1, 0, -1], "lt_1_2_0_2": [-1, 0, 1], "lt_1_0_0_1": [1, 0, 0], "lt_1_2_0_1": [-1, 0, 0], "lt_1_1_0_2": [0, 0, 1], "lt_1_1_0_0": [0, 0, -1] }; const LT = gridTreeFamily(LT_CELLS, "lt_1_1_0_1", "light tree"); export const ltParse = LT.parse, ltCellWorld = LT.cellWorld, ltAnchorFrom = LT.anchorFrom; export const LT_CELLS_W = LT.CELLS_W, ltParseW = LT.parseW, ltCellWorldW = LT.cellWorldW, ltAnchorFromW = LT.anchorFromW; // --- potted christmas tree: 2-cell vertical multiblock (pct_b pot/anchor at Y, pct_t tree at Y+1) --- function registerVerticalTree(anchorId, topId, label) { onPlace((ev) => { const b = ev.block; if (b?.typeId !== anchorId) return; const dim = b.dimension, dir = getDir(b.permutation); const top = safeGetBlock(dim, { x: b.x, y: b.y + 1, z: b.z }); if (!fcCanReplace(top)) { undoPlace(b, ev.player, anchorId, label); return; } try { top.setPermutation(BlockPermutation.resolve(topId, { "minecraft:cardinal_direction": dir })); } catch {} }); onBreak((ev) => { const id = ev.brokenBlockPermutation?.type?.id || ""; if (id !== anchorId && id !== topId) return; const b = ev.block; const sibY = id === anchorId ? b.y + 1 : b.y - 1, want = id === anchorId ? topId : anchorId; const nb = safeGetBlock(b.dimension, { x: b.x, y: sibY, z: b.z }); if (nb && nb.typeId === want) { try { breakAir(nb); } catch {} } if (ev.player && !isCreative(ev.player)) { try { b.dimension.spawnItem(new ItemStack(anchorId, 1), { x: b.x + 0.5, y: b.y + 0.5, z: b.z + 0.5 }); } catch {} } }); } export const PCT_ANCHOR = "rzb_dcs:pct_b", PCT_TOP = "rzb_dcs:pct_t", PCT_ANCHOR_W = "rzb_dcs:pct_b_w", PCT_TOP_W = "rzb_dcs:pct_t_w"; export const pctParse = (id) => (id === PCT_ANCHOR || id === PCT_TOP) ? id.slice(8) : null; export const pctParseW = (id) => (id === PCT_ANCHOR_W || id === PCT_TOP_W) ? id.slice(8) : null; registerVerticalTree(PCT_ANCHOR, PCT_TOP, "potted christmas tree"); registerVerticalTree(PCT_ANCHOR_W, PCT_TOP_W, "potted christmas tree (white)");