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.
241 lines
18 KiB
JavaScript
241 lines
18 KiB
JavaScript
// features/toggles.js; block <-> animated-entity toggles (buoy, sprinkler, spinner birds).
|
|
|
|
import { world, system, BlockPermutation, ItemStack, isCreative, getDir, safeGetBlock } from "../core/mc.js";
|
|
import { DISPLAY_DIR_YAW, displayYawToDir, TUBE_DIR_YAW, tubeYawToDir, fcCanReplace } from "../core/grid.js";
|
|
import { summonDisplay, breakFx, toggleDebounced, brushFx, particleFor } from "../core/fx.js";
|
|
import { BO_BOTTOM, BO_TOP, boCanReplace } from "./mb_pairs.js";
|
|
import { registerVariants } from "../core/variants.js";
|
|
import { onPlace, onBreak } from "../core/events.js";
|
|
import { KOINO_CHILDREN, KOINO_CHILDREN_2, KOINO_PARENTS, KOINO_TOP } from "./mb_grid.js";
|
|
|
|
const heldItem = (e) => { try { return e.getComponent("minecraft:inventory")?.container?.getItem(e.selectedSlotIndex)?.typeId; } catch { return undefined; } };
|
|
const BRUSH_ENT_SEQ = {}; // (legacy) brushable toggle ENTITY id -> its ordered variant list
|
|
function brushEntRecolor(t, player) { // swap animated entity to next variant, same spot/yaw
|
|
const seq = BRUSH_ENT_SEQ[t.typeId]; if (!seq) return false;
|
|
const next = seq[(seq.indexOf(t.typeId) + 1) % seq.length];
|
|
const dim = t.dimension, l = t.location; let yaw = 0; try { yaw = t.getRotation().y; } catch {}
|
|
try { t.remove(); } catch {}
|
|
summonDisplay(dim, next, l.x, l.y, l.z, yaw);
|
|
try { brushFx(dim, { x: Math.floor(l.x), y: Math.floor(l.y), z: Math.floor(l.z) }, particleFor(next), player); } catch {}
|
|
return true;
|
|
}
|
|
|
|
const BUOY_ENTITY = "rzb_dcs:buoy";
|
|
function buoyBlockToEntity(block) {
|
|
if (block.typeId !== BO_BOTTOM && block.typeId !== BO_TOP) return false;
|
|
const dim = block.dimension, bx = block.x, bz = block.z, by = (block.typeId === BO_TOP) ? block.y - 1 : block.y;
|
|
if (toggleDebounced(bx, by, bz)) return true;
|
|
const bottom = safeGetBlock(dim, { x: bx, y: by, z: bz }), top = safeGetBlock(dim, { x: bx, y: by + 1, z: bz });
|
|
try { if (bottom && bottom.typeId === BO_BOTTOM) bottom.setType("minecraft:air"); } catch {}
|
|
try { if (top && top.typeId === BO_TOP) top.setType("minecraft:air"); } catch {}
|
|
try { dim.spawnEntity(BUOY_ENTITY, { x: bx + 0.5, y: by, z: bz + 0.5 }); } catch {}
|
|
return true;
|
|
}
|
|
function buoyEntityToBlock(ent) {
|
|
const dim = ent.dimension, l = ent.location, bx = Math.floor(l.x), by = Math.floor(l.y), bz = Math.floor(l.z);
|
|
if (toggleDebounced(bx, by, bz)) return;
|
|
const bb = safeGetBlock(dim, { x: bx, y: by, z: bz }), tt = safeGetBlock(dim, { x: bx, y: by + 1, z: bz });
|
|
if (boCanReplace(bb) && boCanReplace(tt)) { try { bb.setPermutation(BlockPermutation.resolve(BO_BOTTOM)); tt.setPermutation(BlockPermutation.resolve(BO_TOP)); ent.remove(); } catch {} }
|
|
else { try { ent.remove(); } catch {} try { dim.spawnItem(new ItemStack(BO_BOTTOM, 1), { x: bx + 0.5, y: by + 0.5, z: bz + 0.5 }); } catch {} }
|
|
}
|
|
|
|
const BOBBING_BLOCK = "rzb_dcs:fa38", BOBBING_ENT = "rzb_dcs:bobbing_apples";
|
|
function bobbingBlockToEntity(block) {
|
|
if (block?.typeId !== BOBBING_BLOCK) return false;
|
|
const dim = block.dimension, x = block.x, y = block.y, z = block.z;
|
|
if (toggleDebounced(x, y, z)) return true;
|
|
try { block.setType("minecraft:air"); } catch {}
|
|
try { dim.spawnEntity(BOBBING_ENT, { x: x + 0.5, y, z: z + 0.5 }); } catch {}
|
|
return true;
|
|
}
|
|
function bobbingEntityToBlock(ent) {
|
|
const dim = ent.dimension, l = ent.location, bx = Math.floor(l.x), by = Math.floor(l.y), bz = Math.floor(l.z);
|
|
if (toggleDebounced(bx, by, bz)) return;
|
|
const nb = safeGetBlock(dim, { x: bx, y: by, z: bz });
|
|
if (nb && (nb.isAir || nb.isLiquid || fcCanReplace(nb))) { try { nb.setPermutation(BlockPermutation.resolve(BOBBING_BLOCK)); ent.remove(); } catch {} } // setPermutation -> onPlace does NOT fire -> stays static
|
|
else { try { ent.remove(); } catch {} try { dim.spawnItem(new ItemStack(BOBBING_BLOCK, 1), { x: bx + 0.5, y: by + 0.5, z: bz + 0.5 }); } catch {} }
|
|
}
|
|
onPlace((ev) => { if (ev.block?.typeId === BOBBING_BLOCK) bobbingBlockToEntity(ev.block); }); // item place -> animated entity (base)
|
|
|
|
// Wind Chime 2 (sp45 / sp45_rainbow): same animated-base toggle as the bobbing apples, but 2 colours carried by an int
|
|
// `color` property on ONE entity (0 regular / 1 rainbow) so toggle + brush-recolour preserve it. NON-directional.
|
|
const WINDCHIME_ENT = "rzb_dcs:windchime", WINDCHIME_BLOCKS = ["rzb_dcs:sp45", "rzb_dcs:sp45_rainbow"]; // index = colour
|
|
function windchimeBlockToEntity(block) {
|
|
const ci = WINDCHIME_BLOCKS.indexOf(block?.typeId); if (ci < 0) return false;
|
|
const dim = block.dimension, x = block.x, y = block.y, z = block.z, dir = getDir(block.permutation);
|
|
if (toggleDebounced(x, y, z)) return true;
|
|
try { block.setType("minecraft:air"); } catch {}
|
|
let ent; try { ent = dim.spawnEntity(WINDCHIME_ENT, { x: x + 0.5, y, z: z + 0.5 }); } catch {}
|
|
if (ent) { try { ent.setProperty("rzb_dcs:color", ci); } catch {} try { ent.setRotation({ x: 0, y: TUBE_DIR_YAW[dir] ?? 0 }); } catch {} } // entity yaw matches the block facing
|
|
return true;
|
|
}
|
|
function windchimeEntityToBlock(ent) {
|
|
const dim = ent.dimension, l = ent.location, bx = Math.floor(l.x), by = Math.floor(l.y), bz = Math.floor(l.z);
|
|
if (toggleDebounced(bx, by, bz)) return;
|
|
let ci = 0; try { ci = ent.getProperty("rzb_dcs:color") ?? 0; } catch {}
|
|
let yaw = 0; try { yaw = ent.getRotation().y; } catch {}
|
|
const blockId = WINDCHIME_BLOCKS[ci] || WINDCHIME_BLOCKS[0];
|
|
const nb = safeGetBlock(dim, { x: bx, y: by, z: bz });
|
|
if (nb && (nb.isAir || nb.isLiquid || fcCanReplace(nb))) { try { nb.setPermutation(BlockPermutation.resolve(blockId, { "minecraft:cardinal_direction": tubeYawToDir(yaw) })); ent.remove(); } catch {} } // setPermutation -> onPlace does NOT fire -> stays static
|
|
else { try { ent.remove(); } catch {} try { dim.spawnItem(new ItemStack(blockId, 1), { x: bx + 0.5, y: by + 0.5, z: bz + 0.5 }); } catch {} }
|
|
}
|
|
function windchimeRecolor(ent, player) { // brush the animated chime -> flip colour property in place
|
|
let ci = 0; try { ci = ent.getProperty("rzb_dcs:color") ?? 0; } catch {}
|
|
const next = (ci + 1) % WINDCHIME_BLOCKS.length;
|
|
try { ent.setProperty("rzb_dcs:color", next); } catch {}
|
|
try { const l = ent.location; brushFx(ent.dimension, { x: Math.floor(l.x), y: Math.floor(l.y), z: Math.floor(l.z) }, next === 1 ? "rainbow" : "white", player); } catch {}
|
|
}
|
|
onPlace((ev) => { if (WINDCHIME_BLOCKS.includes(ev.block?.typeId)) windchimeBlockToEntity(ev.block); }); // item place -> animated entity (base)
|
|
|
|
const SPRINK_ID = "rzb_dcs:su34", SPRINK_ENT = "rzb_dcs:sprinkler";
|
|
const sprinkPos = (b) => ({ x: b.x + 0.5, y: b.y, z: b.z + 0.5 }); // entity origin = block floor centre
|
|
function sprinklerBlockToEntity(block) {
|
|
if (block.typeId !== SPRINK_ID) return false;
|
|
if (toggleDebounced(block.x, block.y, block.z)) return true;
|
|
const dim = block.dimension, at = sprinkPos(block), dir = getDir(block.permutation);
|
|
summonDisplay(dim, SPRINK_ENT, at.x, at.y, at.z, TUBE_DIR_YAW[dir] ?? 0);
|
|
try { block.setType("minecraft:air"); } catch {}
|
|
return true;
|
|
}
|
|
function sprinklerEntityToBlock(ent) {
|
|
const dim = ent.dimension, l = ent.location, bx = Math.floor(l.x), by = Math.floor(l.y), bz = Math.floor(l.z);
|
|
if (toggleDebounced(bx, by, bz)) return;
|
|
let yaw = 0; try { yaw = ent.getRotation().y; } catch {}
|
|
const nb = safeGetBlock(dim, { x: bx, y: by, z: bz });
|
|
if (nb && (nb.isAir || nb.isLiquid || fcCanReplace(nb))) { try { nb.setPermutation(BlockPermutation.resolve(SPRINK_ID, { "minecraft:cardinal_direction": tubeYawToDir(yaw) })); ent.remove(); } catch {} }
|
|
else { try { ent.remove(); } catch {} try { dim.spawnItem(new ItemStack(SPRINK_ID, 1), { x: bx + 0.5, y: by + 0.5, z: bz + 0.5 }); } catch {} }
|
|
}
|
|
|
|
const SPINNER_PAIRS = {
|
|
"rzb_dcs:sp35_robin": "rzb_dcs:sp35_robin_e", "rzb_dcs:sp35_blue_j": "rzb_dcs:sp35_blue_j_e",
|
|
"rzb_dcs:su01_1": "rzb_dcs:su01_1_e", "rzb_dcs:su01_2": "rzb_dcs:su01_2_e", "rzb_dcs:su01_3": "rzb_dcs:su01_3_e",
|
|
"rzb_dcs:su01_4": "rzb_dcs:su01_4_e", "rzb_dcs:su01_5": "rzb_dcs:su01_5_e", "rzb_dcs:su01_6": "rzb_dcs:su01_6_e",
|
|
"rzb_dcs:su01_7": "rzb_dcs:su01_7_e", "rzb_dcs:su01_rainbow": "rzb_dcs:su01_rainbow_e",
|
|
};
|
|
const SPINNER_ENT_TO_BLOCK = {}; for (const [b, e] of Object.entries(SPINNER_PAIRS)) SPINNER_ENT_TO_BLOCK[e] = b;
|
|
export const spinnerIsSpin = (perm) => { try { return !!perm.getState("rzb_dcs:spin"); } catch { return false; } };
|
|
export function spinnerSpawnAt(dim, blockId, x, y, z, yaw) { const ent = SPINNER_PAIRS[blockId]; if (ent) try { summonDisplay(dim, ent, x + 0.5, y, z + 0.5, yaw); } catch {} }
|
|
export function spinnerDespawnAt(dim, blockId, x, y, z) { const ent = SPINNER_PAIRS[blockId]; if (!ent) return; try { for (const e of dim.getEntities({ type: ent, location: { x: x + 0.5, y, z: z + 0.5 }, maxDistance: 0.6 })) e.remove(); } catch {} }
|
|
function spinnerToggle(block) {
|
|
if (!SPINNER_PAIRS[block.typeId]) return;
|
|
const spin = spinnerIsSpin(block.permutation), dim = block.dimension, x = block.x, y = block.y, z = block.z;
|
|
const yaw = DISPLAY_DIR_YAW[getDir(block.permutation)] ?? 0;
|
|
try { block.setPermutation(block.permutation.withState("rzb_dcs:spin", !spin)); } catch {}
|
|
if (!spin) spinnerSpawnAt(dim, block.typeId, x, y, z, yaw); else spinnerDespawnAt(dim, block.typeId, x, y, z);
|
|
}
|
|
onBreak((ev) => {
|
|
const id = ev.brokenBlockPermutation?.type?.id; if (!SPINNER_PAIRS[id]) return;
|
|
if (spinnerIsSpin(ev.brokenBlockPermutation)) { const b = ev.block; spinnerDespawnAt(b.dimension, id, b.x, b.y, b.z); }
|
|
});
|
|
|
|
function toggleEntityToBlock(ent) {
|
|
const t = ent?.typeId; if (t === BUOY_ENTITY) buoyEntityToBlock(ent); else if (t === SPRINK_ENT) sprinklerEntityToBlock(ent); else if (t === BOBBING_ENT) bobbingEntityToBlock(ent); else if (t === WINDCHIME_ENT) windchimeEntityToBlock(ent);
|
|
}
|
|
|
|
world.afterEvents.dataDrivenEntityTrigger?.subscribe((ev) => {
|
|
if (ev.id === "rzb_dcs:to_block") toggleEntityToBlock(ev.entity);
|
|
else if (ev.id === "rzb_dcs:to_recolor") brushEntRecolor(ev.entity);
|
|
});
|
|
world.afterEvents.playerInteractWithEntity?.subscribe((ev) => {
|
|
const t = ev.target, id = t?.typeId;
|
|
if (BRUSH_ENT_SEQ[id] && heldItem(ev.player) === "rzb_dcs:brush_color") return;
|
|
if (id === WINDCHIME_ENT && heldItem(ev.player) === "rzb_dcs:brush_color") return;
|
|
if (id === BUOY_ENTITY || id === SPRINK_ENT || id === BOBBING_ENT || id === WINDCHIME_ENT || SPINNER_ENT_TO_BLOCK[id]) toggleEntityToBlock(t);
|
|
});
|
|
world.afterEvents.entityHitEntity?.subscribe((ev) => {
|
|
const t = ev.hitEntity, id = t?.typeId; if (!id) return;
|
|
if (BRUSH_ENT_SEQ[id] && heldItem(ev.damagingEntity) === "rzb_dcs:brush_color") { brushEntRecolor(t, ev.damagingEntity); return; }
|
|
if (id === WINDCHIME_ENT && heldItem(ev.damagingEntity) === "rzb_dcs:brush_color") { windchimeRecolor(t, ev.damagingEntity); return; }
|
|
let wcc = 0; if (id === WINDCHIME_ENT) { try { wcc = t.getProperty("rzb_dcs:color") ?? 0; } catch {} }
|
|
const item = (id === BUOY_ENTITY) ? BO_BOTTOM : (id === SPRINK_ENT) ? SPRINK_ID : (id === BOBBING_ENT) ? BOBBING_BLOCK : (id === WINDCHIME_ENT) ? WINDCHIME_BLOCKS[wcc] : SPINNER_ENT_TO_BLOCK[id];
|
|
if (!item) return;
|
|
const dim = t.dimension, l = t.location, dropIt = ev.damagingEntity && !isCreative(ev.damagingEntity);
|
|
try { t.remove(); } catch {}
|
|
breakFx(dim, l.x, l.y + 0.4, l.z);
|
|
if (dropIt) { try { dim.spawnItem(new ItemStack(item, 1), { x: Math.floor(l.x) + 0.5, y: Math.floor(l.y) + 0.5, z: Math.floor(l.z) + 0.5 }); } catch {} }
|
|
});
|
|
|
|
const KOINO = {
|
|
"rzb_dcs:sp15": { mb: KOINO_CHILDREN, ent: "rzb_dcs:sp15_e" },
|
|
"rzb_dcs:sp15_2": { mb: KOINO_CHILDREN_2, ent: "rzb_dcs:sp15_2_e" }, // children colour variant 2
|
|
"rzb_dcs:sp16": { mb: KOINO_PARENTS, ent: "rzb_dcs:sp16_e" },
|
|
"rzb_dcs:sp18": { mb: KOINO_TOP, ent: "rzb_dcs:sp18_e" },
|
|
};
|
|
const KOINO_CELL = {}, KOINO_ANCHOR = {};
|
|
for (const [main, c] of Object.entries(KOINO)) { KOINO_ANCHOR[main] = { main, mb: c.mb, ent: c.ent }; for (const cell in c.mb.cells) KOINO_CELL[c.mb.prefix + cell] = { main, mb: c.mb, ent: c.ent, cell }; }
|
|
const KOINO_COLOR_SEQ = ["rzb_dcs:sp15", "rzb_dcs:sp15_2"];
|
|
export const koinoAnchorBrushable = (id) => KOINO_COLOR_SEQ.includes(id);
|
|
const koinoSpawn = (dim, ent, ax, ay, az, yaw = 0) => { try { summonDisplay(dim, ent, ax + 0.5, ay, az + 0.5, yaw); } catch {} };
|
|
const koinoDespawn = (dim, ent, ax, ay, az) => { try { for (const e of dim.getEntities({ type: ent, location: { x: ax + 0.5, y: ay, z: az + 0.5 }, maxDistance: 0.6 })) e.remove(); } catch {} };
|
|
onPlace((ev) => {
|
|
const b = ev.block, info = KOINO_ANCHOR[b?.typeId]; if (!info) return;
|
|
let yaw = 0; try { yaw = DISPLAY_DIR_YAW[b.permutation.getState("minecraft:cardinal_direction")] ?? 0; } catch {}
|
|
koinoSpawn(b.dimension, info.ent, b.x, b.y, b.z, yaw);
|
|
});
|
|
// mine the invisible anchor -> despawn entity (loot drops the main item)
|
|
onBreak((ev) => {
|
|
const id = ev.brokenBlockPermutation?.type?.id, info = KOINO_ANCHOR[id]; if (!info) return;
|
|
const b = ev.block; koinoDespawn(b.dimension, info.ent, b.x, b.y, b.z);
|
|
});
|
|
function koinoAnchorToBlock(block) {
|
|
const info = KOINO_ANCHOR[block?.typeId]; if (!info) return;
|
|
const mb = info.mb, dim = block.dimension, ax = block.x, ay = block.y, az = block.z;
|
|
let dir = "south"; try { dir = block.permutation.getState("minecraft:cardinal_direction") ?? "south"; } catch {}
|
|
koinoDespawn(dim, info.ent, ax, ay, az);
|
|
try { for (const cell in mb.cells) { const p = mb.cellWorld(cell, ax, ay, az, dir); const nb = safeGetBlock(dim, p); if (nb) nb.setPermutation(BlockPermutation.resolve(mb.prefix + cell, { "minecraft:cardinal_direction": dir })); } } catch {}
|
|
}
|
|
function koinoBlockToEntity(block) {
|
|
const info = KOINO_CELL[block?.typeId]; if (!info) return;
|
|
const mb = info.mb, dim = block.dimension;
|
|
let dir = "south"; try { dir = block.permutation.getState("minecraft:cardinal_direction") ?? "south"; } catch {}
|
|
const a = mb.anchorFrom(info.cell, block.x, block.y, block.z, dir);
|
|
try {
|
|
for (const cell in mb.cells) { const p = mb.cellWorld(cell, a.x, a.y, a.z, dir); const nb = safeGetBlock(dim, p); if (nb && mb.cellOf(nb.typeId) === cell) nb.setType("minecraft:air"); }
|
|
const anchorBlk = safeGetBlock(dim, { x: a.x, y: a.y, z: a.z }); // pole cell becomes the invisible anchor again
|
|
if (anchorBlk) anchorBlk.setPermutation(BlockPermutation.resolve(info.main, { "minecraft:cardinal_direction": dir }));
|
|
koinoSpawn(dim, info.ent, a.x, a.y, a.z, DISPLAY_DIR_YAW[dir] ?? 0);
|
|
} catch {}
|
|
}
|
|
|
|
export function koinoAnchorRecolor(block) {
|
|
const cur = block?.typeId, i = KOINO_COLOR_SEQ.indexOf(cur); if (i < 0) return false;
|
|
const nextMain = KOINO_COLOR_SEQ[(i + 1) % KOINO_COLOR_SEQ.length], info = KOINO_ANCHOR[cur], nextEnt = KOINO[nextMain].ent;
|
|
const dim = block.dimension, ax = block.x, ay = block.y, az = block.z;
|
|
let dir = "south"; try { dir = block.permutation.getState("minecraft:cardinal_direction") ?? "south"; } catch {}
|
|
try { block.setPermutation(BlockPermutation.resolve(nextMain, { "minecraft:cardinal_direction": dir })); } catch {}
|
|
koinoDespawn(dim, info.ent, ax, ay, az);
|
|
koinoSpawn(dim, nextEnt, ax, ay, az, DISPLAY_DIR_YAW[dir] ?? 0);
|
|
return true;
|
|
}
|
|
|
|
const KOINO_FACE_OFF = { Up: [0, 1, 0], Down: [0, -1, 0], North: [0, 0, -1], South: [0, 0, 1], East: [1, 0, 0], West: [-1, 0, 0] };
|
|
const koinoPlayerDir = (p) => { try { const v = p.getViewDirection(); return Math.abs(v.x) > Math.abs(v.z) ? (v.x > 0 ? "east" : "west") : (v.z > 0 ? "south" : "north"); } catch { return "south"; } };
|
|
function koinoPlaceHeld(e, player, inv, slot, item) {
|
|
const off = KOINO_FACE_OFF["" + e.face]; if (!off) return;
|
|
const b = e.block, dim = e.dimension ?? b.dimension, pos = { x: b.x + off[0], y: b.y + off[1], z: b.z + off[2] };
|
|
const target = safeGetBlock(dim, pos); if (!target) return;
|
|
try { if (!target.isAir) return; } catch { return; } // only place into empty space, never overwrite
|
|
const id = item.typeId;
|
|
let perm; try { perm = BlockPermutation.resolve(id); } catch { return; } // held item isn't a placeable block -> ignore
|
|
try { perm = BlockPermutation.resolve(id, { "minecraft:cardinal_direction": koinoPlayerDir(player) }); } catch {} // face like a normal placement, if supported
|
|
try { target.setPermutation(perm); } catch { return; }
|
|
const k = KOINO_ANCHOR[id]; // a koinobori placed by script -> spawn its entity (script placement doesn't fire onPlace)
|
|
if (k) { let yaw = 0; try { yaw = DISPLAY_DIR_YAW[target.permutation.getState("minecraft:cardinal_direction")] ?? 0; } catch {} koinoSpawn(dim, k.ent, pos.x, pos.y, pos.z, yaw); }
|
|
try { if (!isCreative(player)) { if (item.amount > 1) { item.amount -= 1; inv.setItem(slot, item); } else inv.setItem(slot, undefined); } } catch {} // consume one in survival
|
|
}
|
|
|
|
system.beforeEvents.startup.subscribe((init) => {
|
|
const reg = init.blockComponentRegistry;
|
|
reg.registerCustomComponent("rzb_dcs:buoy_toggle", { onPlayerInteract(e) { buoyBlockToEntity(e.block); } });
|
|
reg.registerCustomComponent("rzb_dcs:bobbing_toggle", { onPlayerInteract(e) { bobbingBlockToEntity(e.block); } });
|
|
reg.registerCustomComponent("rzb_dcs:windchime_toggle", { onPlayerInteract(e) { if (heldItem(e.player) === "rzb_dcs:brush_color") return; windchimeBlockToEntity(e.block); } }); // brush in hand -> let brush.js recolour
|
|
reg.registerCustomComponent("rzb_dcs:sprinkler_toggle", { onPlayerInteract(e) { sprinklerBlockToEntity(e.block); } });
|
|
reg.registerCustomComponent("rzb_dcs:spinner_toggle", { onPlayerInteract(e) { if (heldItem(e.player) === "rzb_dcs:brush_color") return; spinnerToggle(e.block); } }); // brush in hand -> let brush.js recolour, don't toggle
|
|
// Koinobori: EMPTY hand -> toggle (anchor->multiblock, cell->anchor+entity); ITEM in hand -> place it at the clicked face.
|
|
reg.registerCustomComponent("rzb_dcs:koino_toggle", { onPlayerInteract(e) {
|
|
const player = e.player, inv = player?.getComponent("minecraft:inventory")?.container, slot = player?.selectedSlotIndex ?? 0, item = inv?.getItem(slot);
|
|
if (!item) { const id = e.block?.typeId; if (KOINO_ANCHOR[id]) koinoAnchorToBlock(e.block); else if (KOINO_CELL[id]) koinoBlockToEntity(e.block); return; } // empty hand -> toggle
|
|
koinoPlaceHeld(e, player, inv, slot, item); // item in hand -> place against the koinobori
|
|
} });
|
|
});
|