import { world, system } from "@minecraft/server"; const SAPLING_BLOCK = "ods_orch:wild_cherry_tree_sapling"; const LOG = "ods_orch:wild_cherry_tree_log"; const LEAVES = "ods_orch:wild_cherry_tree_leaves"; const FLOWER = "ods_orch:wild_cherry_tree_fruit_side"; function rand(n) { return Math.floor(Math.random() * n); } function chance(p) { return Math.random() < p; } // Build a wisteria tree procedurally. Variant 0/1/2 = subtle canopy width tweaks. // Roughly 25% larger than the prior 5-tall structures: 7-block trunk, 7x7 base canopy. function buildWisteria(dim, x, y, z, variant) { const cmds = []; const trunkH = 6 + (variant === 1 ? 1 : 0); // 6 or 7 const wide = variant === 2 ? 4 : 3; // half-extent of widest canopy ring const mid = wide - 1; const narrow = mid - 1; // Clear sapling tile, then place trunk. cmds.push(`setblock ${x} ${y} ${z} air`); for (let dy = 0; dy < trunkH; dy++) { cmds.push(`setblock ${x} ${y + dy} ${z} ${LOG}`); } const canopyBase = y + trunkH - 2; // canopy starts 2 below the very top so the trunk pokes up // Wide ring (full square minus corners) fillSquareRing(cmds, x, canopyBase, z, wide, true); fillSquareRing(cmds, x, canopyBase + 1, z, wide, true); // Mid ring fillSquareRing(cmds, x, canopyBase + 2, z, mid, false); // Narrow cap fillSquareRing(cmds, x, canopyBase + 3, z, narrow, false); cmds.push(`setblock ${x} ${canopyBase + 4} ${z} ${LEAVES} [] replace air`); // Hanging wisteria flower tassels: drop FLOWER blocks 1-3 below the wide ring edges. const edgeOffsets = [ [-wide, 0], [wide, 0], [0, -wide], [0, wide], [-mid, -mid], [-mid, mid], [mid, -mid], [mid, mid], [-wide, -1], [-wide, 1], [wide, -1], [wide, 1], [-1, -wide], [1, -wide], [-1, wide], [1, wide], ]; for (const [dx, dz] of edgeOffsets) { if (!chance(0.7)) continue; const drop = 1 + rand(3); // 1-3 blocks for (let i = 1; i <= drop; i++) { cmds.push(`setblock ${x + dx} ${canopyBase - i} ${z + dz} ${FLOWER} [] replace air`); } } return cmds; } // Fill a hollow square (or solid) ring of LEAVES at level y with given half-extent r. // `clipCorners` true = drop the four extreme corners for a softer outline. function fillSquareRing(cmds, cx, cy, cz, r, clipCorners) { const x1 = cx - r, x2 = cx + r; const z1 = cz - r, z2 = cz + r; cmds.push(`fill ${x1} ${cy} ${z1} ${x2} ${cy} ${z2} ${LEAVES} replace air`); if (clipCorners) { cmds.push(`setblock ${x1} ${cy} ${z1} air`); cmds.push(`setblock ${x2} ${cy} ${z1} air`); cmds.push(`setblock ${x1} ${cy} ${z2} air`); cmds.push(`setblock ${x2} ${cy} ${z2} air`); } } function growSapling(block, player) { const dim = block.dimension; const { x, y, z } = block.location; const variant = rand(3); const cmds = buildWisteria(dim, x, y, z, variant); let i = 0; // Throttle to ~30 commands per tick to avoid command-rate spikes. const step = () => { const end = Math.min(i + 30, cmds.length); for (; i < end; i++) { try { dim.runCommand(cmds[i]); } catch (_) {} } if (i < cmds.length) system.runTimeout(step, 1); else if (player) player.sendMessage("§5[Trees] §7A wisteria blooms."); }; step(); } // Bonemeal usage on sapling → grow tree world.beforeEvents.playerInteractWithBlock.subscribe((event) => { const block = event.block; if (!block || block.typeId !== SAPLING_BLOCK) return; const stack = event.itemStack; if (!stack || stack.typeId !== "minecraft:bone_meal") return; event.cancel = true; const player = event.player; system.run(() => { growSapling(block, player); // Consume one bonemeal try { const inv = player.getComponent("minecraft:inventory")?.container; if (inv) { const sel = player.selectedSlotIndex; const cur = inv.getItem(sel); if (cur && cur.typeId === "minecraft:bone_meal") { if (cur.amount > 1) { cur.amount -= 1; inv.setItem(sel, cur); } else { inv.setItem(sel, undefined); } } } } catch (_) {} }); }); system.run(() => { world.sendMessage("§5[Trees] §7Wisteria pack loaded."); });