added decobench assets added backend logic to best of my ability
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
// features/break_particle.js — custom block break particle
|
||||
// features/break_particle.js custom block break particle
|
||||
|
||||
import { system } from "../core/mc.js";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// features/brush.js — the Color Brush recolour
|
||||
// features/brush.js the Color Brush recolour
|
||||
import { world, system, BlockPermutation, getDir, safeGetBlock } from "../core/mc.js";
|
||||
import { VARIANT_SEQ } from "../core/variants.js";
|
||||
import { particleFor, brushFx, debounced } from "../core/fx.js";
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
|
||||
// features/decor.js — standalone brush recolorable decoration blocks.
|
||||
// features/decor.js standalone brush recolorable decoration blocks.
|
||||
|
||||
import { registerVariants } from "../core/variants.js";
|
||||
|
||||
registerVariants(["rzb_dcs:sp22_white", "rzb_dcs:sp22_yellow", "rzb_dcs:sp22_pink", "rzb_dcs:sp22_purple", "rzb_dcs:sp22_red", "rzb_dcs:sp22_salmon"]); // rose vase
|
||||
registerVariants(["rzb_dcs:sp25_pink", "rzb_dcs:sp25_red"]); // valentine card
|
||||
registerVariants(["rzb_dcs:sp21_white", "rzb_dcs:sp21_yellow", "rzb_dcs:sp21_pink", "rzb_dcs:sp21_red"]); // potted cherry branch
|
||||
registerVariants(["rzb_dcs:sp12_silver", "rzb_dcs:sp12_gold", "rzb_dcs:sp12_rosegold"]); // engagement ring (metals — natural order)
|
||||
registerVariants(["rzb_dcs:sp12_silver", "rzb_dcs:sp12_gold", "rzb_dcs:sp12_rosegold"]); // engagement ring (metals natural order)
|
||||
registerVariants(["rzb_dcs:sp11_yellow", "rzb_dcs:sp11_pink", "rzb_dcs:sp11_white"]); // easter basket
|
||||
registerVariants(["rzb_dcs:sp26_tulips", "rzb_dcs:sp26_flowers1", "rzb_dcs:sp26_flowers2"]); // watering can (flower types — natural order)
|
||||
registerVariants(["rzb_dcs:sp27_plain", "rzb_dcs:sp27_spring"]); // welcome mat (styles — natural order)
|
||||
registerVariants(["rzb_dcs:sp26_tulips", "rzb_dcs:sp26_flowers1", "rzb_dcs:sp26_flowers2"]); // watering can (flower types natural order)
|
||||
registerVariants(["rzb_dcs:sp27_plain", "rzb_dcs:sp27_spring"]); // welcome mat (styles natural order)
|
||||
registerVariants(["rzb_dcs:sp37_white", "rzb_dcs:sp37_red"]); // spider lily bouquet
|
||||
registerVariants(["rzb_dcs:sp39_yellow", "rzb_dcs:sp39_pink", "rzb_dcs:sp39_purple", "rzb_dcs:sp39_blue", "rzb_dcs:sp39_green"]); // spring armchair (canonical cycle order)
|
||||
registerVariants(["rzb_dcs:sp40_yellow", "rzb_dcs:sp40_pink", "rzb_dcs:sp40_purple", "rzb_dcs:sp40_blue", "rzb_dcs:sp40_green"]); // spring couch left
|
||||
@@ -28,7 +28,7 @@ registerVariants(["rzb_dcs:su37_yellow", "rzb_dcs:su37_green"]); // potted bambo
|
||||
registerVariants(["rzb_dcs:sp44_blue_and_purple", "rzb_dcs:sp44_pink_and_green"]); // candle lanterns (spring, 3-frame flipbook)
|
||||
registerVariants(["rzb_dcs:wi49_blue", "rzb_dcs:wi49_green", "rzb_dcs:wi49_red", "rzb_dcs:wi49_white"]); // candle holder / advent candlestick (winter, 3-frame flipbook)
|
||||
registerVariants(["rzb_dcs:sp45", "rzb_dcs:sp45_rainbow"]); // wind chime 2 (spring) OFF-block form; brushing the animated entity recolours via toggles.js
|
||||
// rose vine wall (spring) — 3 shape pieces for building vine chains, each brush-cyclable through its 11 colours
|
||||
// rose vine wall (spring) 3 shape pieces for building vine chains, each brush-cyclable through its 11 colours
|
||||
registerVariants(["rzb_dcs:sp47_red", "rzb_dcs:sp47_pink", "rzb_dcs:sp47_pink_alt", "rzb_dcs:sp47_orange", "rzb_dcs:sp47_yellow", "rzb_dcs:sp47_yellow_alt", "rzb_dcs:sp47_green", "rzb_dcs:sp47_blue", "rzb_dcs:sp47_purple", "rzb_dcs:sp47_white", "rzb_dcs:sp47_black"]); // middle
|
||||
registerVariants(["rzb_dcs:sp48_red", "rzb_dcs:sp48_pink", "rzb_dcs:sp48_pink_alt", "rzb_dcs:sp48_orange", "rzb_dcs:sp48_yellow", "rzb_dcs:sp48_yellow_alt", "rzb_dcs:sp48_green", "rzb_dcs:sp48_blue", "rzb_dcs:sp48_purple", "rzb_dcs:sp48_white", "rzb_dcs:sp48_black"]); // diagonal left
|
||||
registerVariants(["rzb_dcs:sp49_red", "rzb_dcs:sp49_pink", "rzb_dcs:sp49_pink_alt", "rzb_dcs:sp49_orange", "rzb_dcs:sp49_yellow", "rzb_dcs:sp49_yellow_alt", "rzb_dcs:sp49_green", "rzb_dcs:sp49_blue", "rzb_dcs:sp49_purple", "rzb_dcs:sp49_white", "rzb_dcs:sp49_black"]); // diagonal right
|
||||
@@ -74,6 +74,6 @@ registerVariants(["rzb_dcs:bl_p_orange", "rzb_dcs:bl_p_yellow", "rzb_dcs:bl_p_li
|
||||
registerVariants(["rzb_dcs:bl_heart_white", "rzb_dcs:bl_heart_orange", "rzb_dcs:bl_heart_yellow", "rzb_dcs:bl_heart_pink", "rzb_dcs:bl_heart_cyan", "rzb_dcs:bl_heart_purple", "rzb_dcs:bl_heart_blue", "rzb_dcs:bl_heart_green", "rzb_dcs:bl_heart_red", "rzb_dcs:bl_heart_berry", "rzb_dcs:bl_heart_rainbow"]); // heart balloons (regular)
|
||||
registerVariants(["rzb_dcs:bl_heart_p_orange", "rzb_dcs:bl_heart_p_yellow", "rzb_dcs:bl_heart_p_lime", "rzb_dcs:bl_heart_p_pink", "rzb_dcs:bl_heart_p_cyan", "rzb_dcs:bl_heart_p_purple", "rzb_dcs:bl_heart_p_blue", "rzb_dcs:bl_heart_p_green", "rzb_dcs:bl_heart_p_red"]); // heart balloons (pastel)
|
||||
// (balloons_small is a 2-cell multiblock -> brush-cycled via a both-cells branch in brush.js, NOT registerVariants.)
|
||||
// jack 'o lantern carvings — two groups so brushing keeps the on/off state (off cycles off, lit cycles lit); facing preserved
|
||||
// jack 'o lantern carvings two groups so brushing keeps the on/off state (off cycles off, lit cycles lit); facing preserved
|
||||
registerVariants(["rzb_dcs:fa33_1", "rzb_dcs:fa33_2", "rzb_dcs:fa33_3", "rzb_dcs:fa33_4"]); // carvings (off)
|
||||
registerVariants(["rzb_dcs:fa33_1_on", "rzb_dcs:fa33_2_on", "rzb_dcs:fa33_3_on", "rzb_dcs:fa33_4_on"]); // carvings (lit)
|
||||
|
||||
@@ -25,6 +25,9 @@ const STATIONS = new Set(["rzb_dcs:decobench", "rzb_dcs:decomposer"]);
|
||||
const OBJ = { m: "rzb_dcs_deco_mat", r: "rzb_dcs_deco_red", g: "rzb_dcs_deco_green", b: "rzb_dcs_deco_blue" };
|
||||
const FEED_ITEM = { m: "rzb_dcs:raw_material", r: "minecraft:red_dye", g: "minecraft:green_dye", b: "minecraft:blue_dye" };
|
||||
const CHANNELS = ["m", "r", "g", "b"];
|
||||
// decomposer fuel (DecoCraft): coal only, +4 per coal up to 32, -4 consumed per block-decompose
|
||||
const FUEL = { obj: "rzb_dcs_decomp_fuel", max: 32, perCoal: 4, perDecomp: 4 };
|
||||
const FUEL_ITEMS = ["minecraft:coal", "minecraft:charcoal"];
|
||||
|
||||
// block -> how many make 1 raw_material (DecoCraft rates). dirt 8, cobblestone 4, every log/wood 2.
|
||||
const DECOMP = { "minecraft:dirt": 8, "minecraft:cobblestone": 4 };
|
||||
@@ -44,6 +47,10 @@ const get = (station, key) => { try { return objective(key).getScore(station) ??
|
||||
const setRaw = (station, key, v) => { try { objective(key).setScore(station, Math.max(0, Math.min(CAP[key], v))); } catch {} };
|
||||
const addUnits = (station, key, delta) => setRaw(station, key, get(station, key) + delta);
|
||||
const balance = (station) => ({ m: get(station, "m"), r: get(station, "r"), g: get(station, "g"), b: get(station, "b") });
|
||||
// decomposer fuel scoreboard (participant = the decomposer entity)
|
||||
const fuelObj = () => { let o = world.scoreboard.getObjective(FUEL.obj); if (!o) { try { o = world.scoreboard.addObjective(FUEL.obj, FUEL.obj); } catch { o = world.scoreboard.getObjective(FUEL.obj); } } return o; };
|
||||
const fuelGet = (st) => { try { return fuelObj().getScore(st) ?? 0; } catch { return 0; } };
|
||||
const fuelSet = (st, v) => { try { fuelObj().setScore(st, Math.max(0, Math.min(FUEL.max, v))); } catch {} };
|
||||
|
||||
// mirror the balance onto the station's meter_* props so the model can show fill
|
||||
function syncMeters(station) {
|
||||
@@ -72,6 +79,15 @@ function takeFromPlayer(player, itemId, count) {
|
||||
}
|
||||
return count - need;
|
||||
}
|
||||
// pull up to `count` of itemId from the player's MAINHAND stack only (DecoCraft consumes the held stack, not the inventory)
|
||||
function takeFromHand(player, itemId, count) {
|
||||
const inv = player?.getComponent("minecraft:inventory")?.container; if (!inv) return 0;
|
||||
const i = player.selectedSlotIndex ?? 0, it = inv.getItem(i);
|
||||
if (it?.typeId !== itemId) return 0;
|
||||
const take = Math.min(it.amount, count);
|
||||
if (it.amount - take <= 0) inv.setItem(i, undefined); else { it.amount -= take; inv.setItem(i, it); }
|
||||
return take;
|
||||
}
|
||||
function givePlayer(player, itemId, count) {
|
||||
const inv = player?.getComponent("minecraft:inventory")?.container;
|
||||
const stack = new ItemStack(itemId, Math.max(1, Math.min(64, count)));
|
||||
@@ -106,11 +122,21 @@ function decompose(station, slot) {
|
||||
const d = slotDeco(slot); if (!d || !d.cost) return;
|
||||
const player = nearestPlayer(station); if (!player) return;
|
||||
if (takeFromPlayer(player, d.id, 1) < 1) return; // must hold the decoration to break it
|
||||
for (const k of CHANNELS) if (d.cost[k]) givePlayer(player, FEED_ITEM[k], d.cost[k]); // hand its materials back as items
|
||||
if (d.cost.m) givePlayer(player, FEED_ITEM.m, d.cost.m); // raw_material is 1 unit/item -> give m items
|
||||
for (const k of ["r", "g", "b"]) { const items = Math.floor((d.cost[k] || 0) / UNITS[k]); if (items) givePlayer(player, FEED_ITEM[k], items); } // dye cost is in UNITS -> convert to items (/8), no 8x inflation
|
||||
try { world.scoreboard.getObjective("rzb_dcs_logs_decomposed")?.addScore(station, 1); } catch {}
|
||||
}
|
||||
// blocks -> raw_material, from the player's held stack (floor by rate, leftover stays), like DecoCraft
|
||||
// coal -> fuel (DecoCraft: +4 per coal up to 32, from the held stack)
|
||||
function feedFuel(station) {
|
||||
const player = nearestPlayer(station); if (!player) return;
|
||||
const space = FUEL.max - fuelGet(station); if (space < FUEL.perCoal) return;
|
||||
const maxCoal = Math.floor(space / FUEL.perCoal);
|
||||
let took = 0; for (const it of FUEL_ITEMS) { took = takeFromHand(player, it, maxCoal); if (took) break; }
|
||||
if (took) fuelSet(station, fuelGet(station) + took * FUEL.perCoal);
|
||||
}
|
||||
// blocks -> raw_material, from the player's held stack (floor by rate, leftover stays). Requires + consumes coal fuel (DecoCraft)
|
||||
function decompBlock(station) {
|
||||
if (fuelGet(station) < FUEL.perDecomp) { try { nearestPlayer(station)?.playSound("note.bass", { volume: 0.6 }); } catch {} return; } // out of fuel
|
||||
const player = nearestPlayer(station); if (!player) return;
|
||||
const inv = player.getComponent("minecraft:inventory")?.container; if (!inv) return;
|
||||
const idx = player.selectedSlotIndex ?? 0, held = inv.getItem(idx);
|
||||
@@ -120,13 +146,14 @@ function decompBlock(station) {
|
||||
if (held.amount - used <= 0) inv.setItem(idx, undefined);
|
||||
else { held.amount -= used; inv.setItem(idx, held); }
|
||||
givePlayer(player, FEED_ITEM.m, toraw);
|
||||
fuelSet(station, fuelGet(station) - FUEL.perDecomp); // remove_fuel per process
|
||||
}
|
||||
function feed(station, key) {
|
||||
const player = nearestPlayer(station); if (!player) return;
|
||||
const space = CAP[key] - get(station, key); if (space <= 0) return;
|
||||
const per = UNITS[key];
|
||||
const maxItems = Math.floor(space / per) + (space % per ? 1 : 0); // last item may partly fill
|
||||
const took = takeFromPlayer(player, FEED_ITEM[key], maxItems); if (!took) return;
|
||||
const took = takeFromHand(player, FEED_ITEM[key], maxItems); if (!took) return;
|
||||
addUnits(station, key, Math.min(space, took * per));
|
||||
syncMeters(station);
|
||||
}
|
||||
@@ -134,10 +161,9 @@ function feed(station, key) {
|
||||
function feedCompound(station, itemId, kA, kB) {
|
||||
const player = nearestPlayer(station); if (!player) return;
|
||||
const per = 4, spaceA = CAP[kA] - get(station, kA), spaceB = CAP[kB] - get(station, kB);
|
||||
const room = Math.max(spaceA, spaceB); if (room <= 0) return;
|
||||
const took = takeFromPlayer(player, itemId, Math.ceil(room / per)); if (!took) return;
|
||||
addUnits(station, kA, Math.min(spaceA, took * per));
|
||||
addUnits(station, kB, Math.min(spaceB, took * per));
|
||||
const maxItems = Math.min(Math.floor(spaceA / per), Math.floor(spaceB / per)); if (maxItems <= 0) return; // both channels must accept the full 4 (no dye wasted)
|
||||
const took = takeFromHand(player, itemId, maxItems); if (!took) return;
|
||||
addUnits(station, kA, took * per); addUnits(station, kB, took * per);
|
||||
syncMeters(station);
|
||||
}
|
||||
function drop(station, key) {
|
||||
@@ -147,6 +173,11 @@ function drop(station, key) {
|
||||
dropAt(station.dimension, station.location, FEED_ITEM[key], items);
|
||||
syncMeters(station);
|
||||
}
|
||||
// return ALL stored materials as items (DecoCraft returns these when a bench is broken)
|
||||
export function dropAll(station) { for (const k of CHANNELS) drop(station, k); }
|
||||
function dropFuel(station) { const coal = Math.floor(fuelGet(station) / FUEL.perCoal); if (coal > 0) dropAt(station.dimension, station.location, "minecraft:coal", coal); fuelSet(station, 0); }
|
||||
// on break: the bench returns its stored materials, the decomposer returns its stored fuel as coal
|
||||
export function dropStored(station) { if (station?.typeId === "rzb_dcs:decomposer") dropFuel(station); else dropAll(station); }
|
||||
function refresh(station, slot) { const d = slotDeco(slot); if (d?.cost) writeSlotState(slot, station, d.cost); }
|
||||
|
||||
// the /scriptevent router; the UI drives everything through here
|
||||
@@ -154,6 +185,7 @@ const HANDLERS = {
|
||||
"rzb_dcs:deco_craft": (st, src) => craft(st, src),
|
||||
"rzb_dcs:deco_decompose": (st, src) => decompose(st, src),
|
||||
"rzb_dcs:decomp_block": (st) => decompBlock(st),
|
||||
"rzb_dcs:feed_fuel": (st) => feedFuel(st),
|
||||
"rzb_dcs:deco_refresh": (st, src) => refresh(st, src),
|
||||
"rzb_dcs:feed_mat": (st) => feed(st, "m"),
|
||||
"rzb_dcs:feed_red": (st) => feed(st, "r"),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
// features/display.js — animated display pieces (place a block -> spawn an entity).
|
||||
// features/display.js animated display pieces (place a block -> spawn an entity).
|
||||
import { DISPLAY_DIR_YAW } from "../core/grid.js";
|
||||
import { summonDisplay, breakFx, particleFor } from "../core/fx.js";
|
||||
import { onPlace, onBreak } from "../core/events.js";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
// features/interact.js — right-click lids/toggles + the interact fall-through dispatcher.
|
||||
// features/interact.js right-click lids/toggles + the interact fall-through dispatcher.
|
||||
|
||||
import { world, system, BlockPermutation, getDir, safeGetBlock } from "../core/mc.js";
|
||||
import { tubeOverWater } from "../core/grid.js";
|
||||
@@ -110,7 +110,7 @@ world.afterEvents.playerInteractWithBlock?.subscribe((ev) => {
|
||||
if (emberToggle(ev.block)) return;
|
||||
if (lanternToggle(ev.block)) return;
|
||||
if (jolToggle(ev.block)) return;
|
||||
// su02 tube / su03 lounger over water are boats (ride_* component boards them) — don't also sit.
|
||||
// su02 tube / su03 lounger over water are boats (ride_* component boards them) don't also sit.
|
||||
const id = ev.block?.typeId;
|
||||
if ((id?.startsWith("rzb_dcs:su02_") || id?.startsWith("rzb_dcs:su03_")) && tubeOverWater(ev.block)) return;
|
||||
sit(ev.player, ev.block);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// features/maintenance.js — pick-block anchor redirect + firepit embers.
|
||||
// features/maintenance.js pick-block anchor redirect + firepit embers.
|
||||
import { world, system, ItemStack, safeGetBlock, isCreative } from "../core/mc.js";
|
||||
import { onPlace, onBreak } from "../core/events.js";
|
||||
import { UMBRELLA_BAMBOO, UMBRELLA_BEACH, BAMBOO_TABLE } from "./mb_grid.js";
|
||||
@@ -87,7 +87,7 @@ export const EMBER_BLOCKS = {
|
||||
"rzb_dcs:su13_green": { y: 0.85, ent: TORCH_ENT },
|
||||
"rzb_dcs:su13_yellow": { y: 0.85, ent: TORCH_ENT },
|
||||
"rzb_dcs:fa37": { y: 0.7, ent: CAULDRON_ENT },
|
||||
"rzb_dcs:fa08": { y: 0.4, ent: SPARKLE_ENT }, // crystal ball — always-on twinkling sparkle ring around the orb (no lit state)
|
||||
"rzb_dcs:fa08": { y: 0.4, ent: SPARKLE_ENT }, // crystal ball always-on twinkling sparkle ring around the orb (no lit state)
|
||||
// open grill = smoke; the CLOSED grill ids aren't here, so the sweep removes the emitter when it's shut (no lit state)
|
||||
"rzb_dcs:su19_black": { y: 0.9, ent: GRILL_ENT },
|
||||
"rzb_dcs:su19_blue": { y: 0.9, ent: GRILL_ENT },
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// features/mb_grid.js — grid & canopy multiblocks, defined as data.
|
||||
// features/mb_grid.js grid & canopy multiblocks, defined as data.
|
||||
|
||||
import { defineGridMultiblock } from "../core/multiblock.js";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// features/mb_pairs.js — partner / vertical / 2x2 multiblocks.
|
||||
// features/mb_pairs.js partner / vertical / 2x2 multiblocks.
|
||||
import { world, system, BlockPermutation, ItemStack, isCreative, getDir, safeGetBlock } from "../core/mc.js";
|
||||
import { fcCanReplace } from "../core/grid.js";
|
||||
import { undoPlace, breakAir } from "../core/fx.js";
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// features/mb_trees.js — christmas / light / potted trees as multiblocks.
|
||||
// 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
|
||||
// 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 ---
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// features/rideables.js — block <-> rideable-entity vehicles (tube, flamingo, lounger, bike, surfboard, basket bike, sled).
|
||||
// features/rideables.js block <-> rideable-entity vehicles (tube, flamingo, lounger, bike, surfboard, basket bike, sled).
|
||||
|
||||
// Parks as a block; a ride_* interact spawns a steerable entity; riderless it reverts (color + heading kept).
|
||||
// Water craft steer via applyImpulse + no-Y setRotation (engine keeps float); land bikes velocity-drive with gravity.
|
||||
@@ -135,7 +135,7 @@ function steerForward(p, ent, yawOff = 0) {
|
||||
|
||||
// Every buoyant water craft (tube, flamingo, lounger, surfboard) steers identically: impulse horizontal velocity toward
|
||||
// look+WASD * speed (coast when idle), face the travel heading. Only the pose anim, speed, yaw offset, and turn style
|
||||
// vary — smoothTurn>0 lerps the body toward heading (the round tube), 0 snaps instantly (directional lounger/surfboard).
|
||||
// vary smoothTurn>0 lerps the body toward heading (the round tube), 0 snaps instantly (directional lounger/surfboard).
|
||||
function driveWaterCraft(p, boat, anim, speed, yawOff = 0, smoothTurn = 0) {
|
||||
try { p.playAnimation(anim, { blendOutTime: 0, stopExpression: "!q.is_riding" }); } catch {}
|
||||
let cur; try { cur = boat.getVelocity(); } catch { cur = { x: 0, y: 0, z: 0 }; }
|
||||
@@ -263,7 +263,7 @@ function landImpulse(veh, dim, loc, fx, fz, rx, rz, ix, spd, strafe, breakFn) {
|
||||
return { nearGround, gspd: Math.hypot(vel.x, vel.z) * sg, broke };
|
||||
}
|
||||
// VELOCITY DRIVE: engine owns Y (gravity), collision, and carries the rider. We steer only horizontal velocity via
|
||||
// applyImpulse + face with setRotation — NO teleport (teleport pins position and kills gravity).
|
||||
// applyImpulse + face with setRotation NO teleport (teleport pins position and kills gravity).
|
||||
function driveBike(p, bike) {
|
||||
const dim = bike.dimension, loc = bike.location;
|
||||
let spd0 = 0; try { spd0 = bike.getDynamicProperty("spd") ?? 0; } catch {}
|
||||
@@ -398,14 +398,14 @@ const revertBasketBike = (t, dim) => revert2Cell(t, dim, BB_RIG);
|
||||
|
||||
const SLED_ENTITY = "rzb_dcs:sled";
|
||||
export const SLED_B = "rzb_dcs:wi41_b_", SLED_FR = "rzb_dcs:wi41_fr_";
|
||||
const SLED_COLORS = ["red", "green", "blue", "yellow"]; // entity color index — do NOT reorder
|
||||
const SLED_COLORS = ["red", "green", "blue", "yellow"]; // entity color index do NOT reorder
|
||||
export const SLED_ORDER = ["yellow", "blue", "green", "red"]; // brush cycle order (canonical); decoupled from the index map
|
||||
export const SLED_CELLS = { "0_0_0": [0, 0, 0], "0_0_1": [0, 0, 1] }; // anchor(back) + front, one block forward (+FWD)
|
||||
export const SLED_PREFIX = { "0_0_0": SLED_B, "0_0_1": SLED_FR };
|
||||
const SLED_MAX_SNOW = 0.5, SLED_MAX_SLOW = 0.16, SLED_ACCEL = 0.03, SLED_DECEL = 0.03, SLED_STRAFE = 0.1, SLED_STEP = 1.05;
|
||||
const SLED_YAW_OFF = 180; // sled drives "backwards" -> flip the model 180 so its front points the way of travel
|
||||
const isSnow = (id) => !!id && /snow|ice|powder_snow/.test(id);
|
||||
const isSand = (id) => !!id && /sand|gravel/.test(id); // loose ground — bikes bog down like on snow
|
||||
const isSand = (id) => !!id && /sand|gravel/.test(id); // loose ground bikes bog down like on snow
|
||||
registerVariants([...SLED_COLORS.map((c) => SLED_B + c), ...SLED_COLORS.map((c) => SLED_FR + c)]); // brush gate; cycling uses SLED_ORDER
|
||||
const _slH = makeCellHelpers(SLED_CELLS, SLED_PREFIX);
|
||||
export const sledCellWorld = _slH.cellWorld, sledAnchorFrom = _slH.anchorFrom, sledParse = _slH.parse;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// features/seating.js — the sit system (seat entity + per-furniture config).
|
||||
// features/seating.js the sit system (seat entity + per-furniture config).
|
||||
import { world, system, isCreative, getDir, safeGetBlock } from "../core/mc.js";
|
||||
import { DIR_YAW, DIR_TROT, FWD } from "../core/grid.js";
|
||||
import { FLOWER_CART, LOUNGER } from "./mb_grid.js";
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// features/stations.js crafting stations placement + removal (1-to-1 with DecoCraft).
|
||||
// Placer item -> minecraft:entity_placer spawns an INVISIBLE _init; on its spawn we grid-align, check clearance + the
|
||||
// 8-block duplicate guard, then summon the real station PRE-ROTATED to the player's look cardinal (one step, no visible
|
||||
// rotation) and remove the init. A blocked placement refunds the item + plays the reject sound. The station is non-solid;
|
||||
// its footprint is invisible full-collision blocks (bench = 2 cells, decomposer = 1). Hitting a station returns the stored
|
||||
// materials + the item (no item in creative, like DecoCraft) and clears the footprint blocks.
|
||||
import { world, ItemStack, isCreative, safeGetBlock } from "../core/mc.js";
|
||||
import { fcCanReplace } from "../core/grid.js";
|
||||
import { dropStored } from "./decostation.js";
|
||||
|
||||
const INIT_TO_STATION = { "rzb_dcs:decobench_init": "rzb_dcs:decobench", "rzb_dcs:decomposer_init": "rzb_dcs:decomposer" };
|
||||
const STATIONS = new Set(["rzb_dcs:decobench", "rzb_dcs:decomposer"]);
|
||||
const ITEM_FOR = { "rzb_dcs:decobench": "rzb_dcs:seasons_bench", "rzb_dcs:decomposer": "rzb_dcs:deco_seasons_decomp" };
|
||||
const FILL = { "rzb_dcs:decobench": "^ ^ ^ ^1 ^ ^", "rzb_dcs:decomposer": "^ ^ ^ ^ ^ ^" }; // 2-cell bench, 1-cell decomposer
|
||||
const TWO_CELL = new Set(["rzb_dcs:decobench"]);
|
||||
|
||||
const leftCell = (yaw) => { const r = yaw * Math.PI / 180; return [Math.round(Math.cos(r)), Math.round(Math.sin(r))]; }; // world offset of the local-left cell (^1)
|
||||
const replaceable = (dim, cx, cy, cz) => { const b = safeGetBlock(dim, { x: cx, y: cy, z: cz }); return !!b && (b.isAir || fcCanReplace(b)); };
|
||||
|
||||
world.afterEvents.entitySpawn?.subscribe((ev) => {
|
||||
const e = ev.entity;
|
||||
const station = INIT_TO_STATION[e?.typeId];
|
||||
if (station) {
|
||||
const dim = e.dimension, l = e.location;
|
||||
const bx = Math.floor(l.x), by = Math.floor(l.y), bz = Math.floor(l.z);
|
||||
let yaw = 180, player; try { player = dim.getPlayers({ location: l, closest: 1, maxDistance: 6 })[0]; if (player) yaw = Math.round(player.getRotation().y / 90) * 90 + 180; } catch {}
|
||||
const cells = [[bx, by, bz]];
|
||||
if (TWO_CELL.has(station)) { const [lx, lz] = leftCell(yaw); cells.push([bx + lx, by, bz + lz]); }
|
||||
const clear = cells.every(([cx, cy, cz]) => replaceable(dim, cx, cy, cz));
|
||||
let dup = false; try { dup = dim.getEntities({ location: l, type: station, maxDistance: 8 }).length > 0; } catch {}
|
||||
if (!clear || dup) { // DecoCraft: refuse -> refund the item + reject sound
|
||||
try { dim.spawnItem(new ItemStack(ITEM_FOR[station], 1), { x: bx + 0.5, y: by + 0.5, z: bz + 0.5 }); } catch {}
|
||||
try { player?.playSound("note.didgeridoo", { volume: 0.8 }); } catch {}
|
||||
try { e.remove(); } catch {}
|
||||
return;
|
||||
}
|
||||
const ht = TWO_CELL.has(station) ? ` rzb_dcs:ht_${((yaw % 360) + 360) % 360}` : ""; // facing-oriented 2-cell hit box (bench only)
|
||||
try { dim.runCommand(`summon ${station} ${bx + 0.5} ${by} ${bz + 0.5} ${yaw} 0${ht}`); } catch {} // pre-rotated
|
||||
try { e.remove(); } catch {}
|
||||
return;
|
||||
}
|
||||
if (STATIONS.has(e?.typeId) && ev.cause !== "Loaded") { // real station placed -> fill its invisible collision footprint
|
||||
try { e.runCommand(`fill ${FILL[e.typeId]} rzb_dcs:invisible_block`); } catch {}
|
||||
}
|
||||
});
|
||||
|
||||
// hit to break -> clear footprint, return stored materials + the item (no item in creative), remove
|
||||
world.afterEvents.entityHitEntity?.subscribe((ev) => {
|
||||
const t = ev.hitEntity; if (!STATIONS.has(t?.typeId)) return;
|
||||
const dmg = ev.damagingEntity; if (dmg?.typeId !== "minecraft:player") return;
|
||||
try { t.runCommand(`fill ${FILL[t.typeId]} air replace rzb_dcs:invisible_block`); } catch {}
|
||||
try { dropStored(t); } catch {}
|
||||
if (!isCreative(dmg)) { try { t.dimension.spawnItem(new ItemStack(ITEM_FOR[t.typeId], 1), t.location); } catch {} }
|
||||
try { t.remove(); } catch {}
|
||||
});
|
||||
@@ -25,6 +25,7 @@
|
||||
// maintenance pick block anchor redirect + firepit embers
|
||||
// bounce springy blocks that launch a player who lands on them
|
||||
// decostation decobench + decomposer economy backend (scoreboards, scriptevent router, block/deco to materials)
|
||||
// stations place the decobench/decomposer station entities from a placer item (UI/panel = coworker's to build)
|
||||
// break_particle custom block break particle
|
||||
//
|
||||
// To add a prop: plain recolorable block -> one line in decor.js; rotating/canopy multiblock -> one defineGridMultiblock() in mb_grid.js; anything bespoke -> its own features/ module.
|
||||
@@ -43,4 +44,5 @@ import "./features/brush.js";
|
||||
import "./features/maintenance.js";
|
||||
import "./features/bounce.js";
|
||||
import "./features/decostation.js";
|
||||
import "./features/stations.js";
|
||||
import "./features/break_particle.js";
|
||||
|
||||
Reference in New Issue
Block a user