You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
5.1 KiB
JavaScript
99 lines
5.1 KiB
JavaScript
|
|
// features/containers.js (presents).
|
|
|
|
import { world, system, isCreative, BlockPermutation } from "../core/mc.js";
|
|
import { onPlace, onBreak } from "../core/events.js";
|
|
import { brushFx, particleFor, breakFx } from "../core/fx.js";
|
|
|
|
const FAMILY = "rzb_dcs_present_inv";
|
|
const OFF_Y = 0.1;
|
|
const BRUSH = "rzb_dcs:brush_color";
|
|
const heldItem = (e) => { try { return e.getComponent("minecraft:inventory")?.container?.getItem(e.selectedSlotIndex)?.typeId; } catch { return undefined; } };
|
|
|
|
const PRESENT_SEQ = {};
|
|
const _reg = (ids) => { for (const id of ids) PRESENT_SEQ[id] = ids; };
|
|
const RAINBOW = ["white", "orange", "magenta", "light_blue", "yellow", "lime", "pink", "cyan", "purple", "blue", "green", "red"]; // DecoCraft canonical order
|
|
for (const t of ["wi30", "wi31", "wi32", "wi33"]) _reg(RAINBOW.map((c) => "rzb_dcs:" + t + "_" + c)); // 12-colour presents
|
|
_reg(["wi34_1", "wi34_2", "wi34_3", "wi34_4", "wi34_5"].map((x) => "rzb_dcs:" + x)); // present trio (designs)
|
|
_reg(["wi35_6", "wi35_7", "wi35_8", "wi35_9", "wi35_10", "wi35_11"].map((x) => "rzb_dcs:" + x)); // present trio (designs)
|
|
|
|
function recolorPresent(dim, bx, by, bz, player) {
|
|
let blk = null; try { blk = dim.getBlock({ x: bx, y: by, z: bz }); } catch {}
|
|
const seq = blk && PRESENT_SEQ[blk.typeId]; if (!seq) return false;
|
|
const next = seq[(seq.indexOf(blk.typeId) + 1) % seq.length];
|
|
let dir; try { dir = blk.permutation.getState("minecraft:cardinal_direction"); } catch {}
|
|
try { blk.setPermutation(BlockPermutation.resolve(next, dir !== undefined ? { "minecraft:cardinal_direction": dir } : {})); }
|
|
catch { try { blk.setType(next); } catch {} }
|
|
brushFx(dim, { x: bx, y: by, z: bz }, particleFor(next), player);
|
|
return true;
|
|
}
|
|
|
|
const PRESENTS = {
|
|
"rzb_dcs:wi30_": "rzb_dcs:present_inv", // Present
|
|
"rzb_dcs:wi31_": "rzb_dcs:present_inv", // Present Long
|
|
"rzb_dcs:wi32_": "rzb_dcs:present_inv", // Present Round
|
|
"rzb_dcs:wi33_": "rzb_dcs:present_inv", // Present Small
|
|
"rzb_dcs:wi34_": "rzb_dcs:present_inv", // Present Trio
|
|
"rzb_dcs:wi35_": "rzb_dcs:present_inv" // Present Trio
|
|
};
|
|
function presentEntity(typeId) { if (!typeId) return null; for (const p in PRESENTS) if (typeId.startsWith(p)) return PRESENTS[p]; return null; }
|
|
|
|
const invEntsAt = (dim, at, max = 0.5) => { try { return dim.getEntities({ families: [FAMILY], location: at, maxDistance: max }); } catch { return []; } };
|
|
function dropContents(dim, ent, at) {
|
|
try {
|
|
const c = ent.getComponent("minecraft:inventory")?.container; if (!c) return;
|
|
for (let i = 0; i < c.size; i++) { const it = c.getItem(i); if (it) { try { dim.spawnItem(it, at); } catch {} } }
|
|
} catch {}
|
|
}
|
|
|
|
// Place a present -> spawn its container entity, once.
|
|
onPlace((ev) => {
|
|
const b = ev.block; if (!b) return;
|
|
const entId = presentEntity(b.typeId); if (!entId) return;
|
|
const at = { x: b.x + 0.5, y: b.y + OFF_Y, z: b.z + 0.5 };
|
|
if (invEntsAt(b.dimension, at, 0.4).length) return; // don't double up
|
|
try { b.dimension.spawnEntity(entId, at); } catch {}
|
|
});
|
|
|
|
// Break a present > drop the container's contents + despawn it.
|
|
onBreak((ev) => {
|
|
const id = ev.brokenBlockPermutation?.type?.id; if (!presentEntity(id)) return;
|
|
const b = ev.block, at = { x: b.x + 0.5, y: b.y + OFF_Y, z: b.z + 0.5 };
|
|
for (const e of invEntsAt(b.dimension, at)) { dropContents(b.dimension, e, at); try { e.remove(); } catch {} }
|
|
});
|
|
|
|
world.afterEvents.entityHitEntity?.subscribe((ev) => {
|
|
const t = ev.hitEntity; if (!t || !t.typeId.startsWith("rzb_dcs:present_inv")) return;
|
|
const dim = t.dimension, l = t.location, bx = Math.floor(l.x), by = Math.floor(l.y), bz = Math.floor(l.z);
|
|
if (heldItem(ev.damagingEntity) === BRUSH) { recolorPresent(dim, bx, by, bz, ev.damagingEntity); return; }
|
|
const at = { x: bx + 0.5, y: by + 0.3, z: bz + 0.5 };
|
|
dropContents(dim, t, at);
|
|
let blk = null; try { blk = dim.getBlock({ x: bx, y: by, z: bz }); } catch {}
|
|
if (blk && presentEntity(blk.typeId)) {
|
|
if (!isCreative(ev.damagingEntity)) { try { const is = blk.getItemStack(1, false); if (is) dim.spawnItem(is, at); } catch {} }
|
|
try { blk.setType("minecraft:air"); } catch {}
|
|
}
|
|
try { t.remove(); } catch {}
|
|
breakFx(dim, bx + 0.5, by + 0.5, bz + 0.5); // entity attack-to-pickup: block is script-removed (no vanilla particle) -> puff breakdeco
|
|
});
|
|
|
|
world.beforeEvents.playerInteractWithEntity?.subscribe((ev) => {
|
|
const t = ev.target; if (!t || !t.typeId.startsWith("rzb_dcs:present_inv")) return;
|
|
if (ev.itemStack?.typeId !== BRUSH) return;
|
|
ev.cancel = true;
|
|
const dim = t.dimension, l = t.location, bx = Math.floor(l.x), by = Math.floor(l.y), bz = Math.floor(l.z);
|
|
system.run(() => recolorPresent(dim, bx, by, bz, ev.player));
|
|
});
|
|
|
|
system.runInterval(() => {
|
|
for (const name of ["overworld", "nether", "the_end"]) {
|
|
let dim; try { dim = world.getDimension(name); } catch { continue; }
|
|
let ents; try { ents = dim.getEntities({ families: [FAMILY] }); } catch { continue; }
|
|
for (const e of ents) {
|
|
const l = e.location; let blk = null;
|
|
try { blk = dim.getBlock({ x: Math.floor(l.x), y: Math.floor(l.y), z: Math.floor(l.z) }); } catch {}
|
|
if (blk && !presentEntity(blk.typeId)) { dropContents(dim, e, l); try { e.remove(); } catch {} }
|
|
}
|
|
}
|
|
}, 40);
|