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.
255 lines
20 KiB
JavaScript
255 lines
20 KiB
JavaScript
// 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";
|
|
const _brushRecent = new Set(); // per-cell cooldown so holding the brush doesn't rapid-fire swaps
|
|
import { CT_CELLS, ctParse, ctCellWorld, ctAnchorFrom, CT_CELLS_W, ctParseW, ctCellWorldW, ctAnchorFromW, LT_CELLS, ltParse, ltCellWorld, ltAnchorFrom, LT_CELLS_W, ltParseW, ltCellWorldW, ltAnchorFromW, PCT_ANCHOR, PCT_ANCHOR_W, pctParse, pctParseW } from "./mb_trees.js";
|
|
import { UMBRELLA_BAMBOO, UMBRELLA_BEACH, BAMBOO_TABLE, BT_COLORS, KOINO_CHILDREN, LOUNGER, LOUNGER_COLORS } from "./mb_grid.js";
|
|
import { koinoAnchorBrushable, koinoAnchorRecolor, spinnerIsSpin, spinnerSpawnAt, spinnerDespawnAt } from "./toggles.js";
|
|
import { displayAnchorBrushable, displayAnchorRecolor } from "./display.js";
|
|
import { DISPLAY_DIR_YAW } from "../core/grid.js";
|
|
|
|
const SPINNER_SETS = [
|
|
["rzb_dcs:su01_1", "rzb_dcs:su01_2", "rzb_dcs:su01_3", "rzb_dcs:su01_4", "rzb_dcs:su01_5", "rzb_dcs:su01_6", "rzb_dcs:su01_7", "rzb_dcs:su01_rainbow"],
|
|
["rzb_dcs:sp35_robin", "rzb_dcs:sp35_blue_j"],
|
|
];
|
|
const SPINNER_NEXT = {}; for (const s of SPINNER_SETS) for (let i = 0; i < s.length; i++) SPINNER_NEXT[s[i]] = s[(i + 1) % s.length];
|
|
import { WEB4_L, WEB4_R, WEB4_VARIANTS, WEB4_RIGHT_OFF, FAIRY_L, FAIRY_R, FAIRY_THEMES, FAIRY_RIGHT_OFF, SWING_BASE, SWING_PIECES, SWING_VARIANTS, swingParse, swingAnchor, swingCells, ROPE_BASE, ROPE_VARIANTS, ropeParse, ropeBottom, TW_FRONT, TW_BACK, TW_COLORS, TW_BACK_OFF, LO_FRONT, LO_BACK, LO_COLORS, LO_BACK_OFF, LC_BASE, LC_AWNING, LC_COLORS, SC_VARIANTS, scParse, bsParse, BS_CYCLES, bhParse, BH_CYCLES } from "./mb_pairs.js";
|
|
import { SB_BASE, SB_TOP, SB_LB, SB_LF, SB_ORDER, SB_LAY_CELLS, SB_LAY_PREFIX, sbLayParse, sbLayCellWorld, sbLayAnchorFrom, BB_B, BB_FR, BB_ORDER, BB_CELLS, BB_PREFIX, bbParse, bbCellWorld, bbAnchorFrom, SLED_B, SLED_FR, SLED_ORDER, SLED_CELLS, SLED_PREFIX, sledParse, sledCellWorld, sledAnchorFrom } from "./rideables.js";
|
|
|
|
const umParse = UMBRELLA_BAMBOO.parse, buParse = UMBRELLA_BEACH.parse, btParse = BAMBOO_TABLE.parse;
|
|
const UM_CELLS = UMBRELLA_BAMBOO.cells, BU_CELLS = UMBRELLA_BEACH.cells, BT_CELLS = BAMBOO_TABLE.cells;
|
|
const UM_BASE = UMBRELLA_BAMBOO.prefix, BU_BASE = UMBRELLA_BEACH.prefix, BT_BASE = BAMBOO_TABLE.prefix;
|
|
|
|
const KOINO_C = ["rzb_dcs:sp15_", "rzb_dcs:sp15_2_"];
|
|
function koinoParse(id) {
|
|
if (!id) return null;
|
|
if (id.startsWith("rzb_dcs:sp15_2_")) { const cell = id.slice(15); return (cell === "pole" || cell === "fish") ? { idx: 1, cell } : null; }
|
|
if (id.startsWith("rzb_dcs:sp15_")) { const cell = id.slice(13); return (cell === "pole" || cell === "fish") ? { idx: 0, cell } : null; }
|
|
return null;
|
|
}
|
|
// Grid tree recolor (christmas / light): swap the WHOLE tree between the rainbow and white block sets. They're independent
|
|
// ids differing only by a "_w" suffix (geometry/collision identical), so we just re-resolve every cell to the other id.
|
|
function convertGridTree(block, player, v) { // v: {CELLS, parse, cellWorld, anchorFrom} of the picked variant
|
|
const cell = v.parse(block.typeId); if (!cell) return;
|
|
const dir = getDir(block.permutation), a = v.anchorFrom(cell, block.x, block.y, block.z, dir), dim2 = block.dimension, loc = block.location;
|
|
const toWhite = !cell.endsWith("_w"), pcol = toWhite ? "white" : "yellow";
|
|
system.runTimeout(() => { for (const c of Object.keys(v.CELLS)) { const p = v.cellWorld(c, a.x, a.y, a.z, dir), nb = safeGetBlock(dim2, p); if (nb && v.parse(nb.typeId) === c) { const other = toWhite ? c + "_w" : c.slice(0, -2); try { nb.setPermutation(BlockPermutation.resolve("rzb_dcs:" + other, { "minecraft:cardinal_direction": dir })); } catch {} } } brushFx(dim2, loc, pcol, player); }, 1);
|
|
}
|
|
// Grid-vehicle cell recolor (surfboard-lay / basket bike / sled): cycle ORDER, repaint every cell keeping facing.
|
|
function gridCellTargets(block, dir, parse, ORDER, CELLS, PREFIX, cellWorld, anchorFrom) {
|
|
const pk = parse(block.typeId); if (!pk) return null;
|
|
const i = ORDER.indexOf(pk.color); if (i < 0) return null;
|
|
const nextC = ORDER[(i + 1) % ORDER.length];
|
|
const a = anchorFrom(pk.cell, block.x, block.y, block.z, dir ?? "south");
|
|
return { targets: Object.keys(CELLS).map((c) => ({ pos: cellWorld(c, a.x, a.y, a.z, dir ?? "south"), id: PREFIX[c] + nextC, partner: c !== pk.cell })), colorSrc: nextC };
|
|
}
|
|
|
|
function cycleVariant(block, player) {
|
|
if (debounced(_brushRecent, block.x + "," + block.y + "," + block.z, 6)) return true; // ~0.3s per-cell cooldown: click swaps once, hold doesn't spam
|
|
if (ctParse(block.typeId)) { convertGridTree(block, player, { CELLS: CT_CELLS, parse: ctParse, cellWorld: ctCellWorld, anchorFrom: ctAnchorFrom }); return true; }
|
|
if (ctParseW(block.typeId)) { convertGridTree(block, player, { CELLS: CT_CELLS_W, parse: ctParseW, cellWorld: ctCellWorldW, anchorFrom: ctAnchorFromW }); return true; }
|
|
if (ltParse(block.typeId)) { convertGridTree(block, player, { CELLS: LT_CELLS, parse: ltParse, cellWorld: ltCellWorld, anchorFrom: ltAnchorFrom }); return true; }
|
|
if (ltParseW(block.typeId)) { convertGridTree(block, player, { CELLS: LT_CELLS_W, parse: ltParseW, cellWorld: ltCellWorldW, anchorFrom: ltAnchorFromW }); return true; }
|
|
if (pctParse(block.typeId) || pctParseW(block.typeId)) {
|
|
const white = !!pctParseW(block.typeId), anchorId = white ? PCT_ANCHOR_W : PCT_ANCHOR, parse = white ? pctParseW : pctParse;
|
|
const ay = block.typeId === anchorId ? block.y : block.y - 1, dim2 = block.dimension, loc = block.location, toWhite = !white, pcol = toWhite ? "white" : "yellow";
|
|
let dir = "south"; try { dir = block.permutation.getState("minecraft:cardinal_direction") ?? "south"; } catch {}
|
|
system.runTimeout(() => { for (const dy of [0, 1]) { const nb = safeGetBlock(dim2, { x: block.x, y: ay + dy, z: block.z }); if (nb && parse(nb.typeId)) { const other = toWhite ? nb.typeId + "_w" : nb.typeId.slice(0, -2); try { nb.setPermutation(BlockPermutation.resolve(other, { "minecraft:cardinal_direction": dir })); } catch {} } } brushFx(dim2, loc, pcol, player); }, 1);
|
|
return true;
|
|
}
|
|
if (koinoAnchorBrushable(block.typeId)) {
|
|
const b = block, dim2 = block.dimension, loc = block.location;
|
|
system.runTimeout(() => { koinoAnchorRecolor(b); brushFx(dim2, loc, "pink", player); }, 1);
|
|
return true;
|
|
}
|
|
if (displayAnchorBrushable(block.typeId)) {
|
|
const b = block, dim2 = block.dimension, loc = block.location;
|
|
system.runTimeout(() => { const col = displayAnchorRecolor(b); brushFx(dim2, loc, col || "white", player); }, 1);
|
|
return true;
|
|
}
|
|
if (SPINNER_NEXT[block.typeId]) { // pinwheel / spinner bird: cycle colour in place, keep spin state + facing, respawn entity if spinning
|
|
const cur = block.typeId, next = SPINNER_NEXT[cur], dim2 = block.dimension, loc = block.location, x = block.x, y = block.y, z = block.z;
|
|
let sdir = "south"; try { sdir = block.permutation.getState("minecraft:cardinal_direction") ?? "south"; } catch {}
|
|
const spinning = spinnerIsSpin(block.permutation);
|
|
system.runTimeout(() => {
|
|
const b = safeGetBlock(dim2, { x, y, z }); if (b) { try { b.setPermutation(BlockPermutation.resolve(next, { "minecraft:cardinal_direction": sdir, "rzb_dcs:spin": spinning })); } catch {} }
|
|
if (spinning) { spinnerDespawnAt(dim2, cur, x, y, z); spinnerSpawnAt(dim2, next, x, y, z, DISPLAY_DIR_YAW[sdir] ?? 0); }
|
|
brushFx(dim2, loc, particleFor(next), player);
|
|
}, 1);
|
|
return true;
|
|
}
|
|
const kp = koinoParse(block.typeId); // koinobori children: brushing a cell swaps ALL cells to the next colour variant
|
|
if (kp) {
|
|
const kdir = getDir(block.permutation), a = KOINO_CHILDREN.anchorFrom(kp.cell, block.x, block.y, block.z, kdir), dim2 = block.dimension, loc = block.location;
|
|
const nextPrefix = KOINO_C[(kp.idx + 1) % KOINO_C.length];
|
|
system.runTimeout(() => { for (const c of Object.keys(KOINO_CHILDREN.cells)) { const p = KOINO_CHILDREN.cellWorld(c, a.x, a.y, a.z, kdir), nb = safeGetBlock(dim2, p); if (nb && koinoParse(nb.typeId)?.cell === c) { try { nb.setPermutation(BlockPermutation.resolve(nextPrefix + c, { "minecraft:cardinal_direction": kdir })); } catch {} } } brushFx(dim2, loc, "pink", player); }, 1);
|
|
return true;
|
|
}
|
|
let dir;
|
|
try { dir = block.permutation.getState("minecraft:cardinal_direction"); } catch {}
|
|
const loc = block.location, dim = block.dimension;
|
|
|
|
let targets, colorSrc;
|
|
const isL = block.typeId.startsWith(FAIRY_L), isR = block.typeId.startsWith(FAIRY_R);
|
|
if (isL || isR) {
|
|
const theme = block.typeId.slice((isL ? FAIRY_L : FAIRY_R).length);
|
|
const i = FAIRY_THEMES.indexOf(theme);
|
|
if (i < 0) return false;
|
|
const next = FAIRY_THEMES[(i + 1) % FAIRY_THEMES.length];
|
|
const off = FAIRY_RIGHT_OFF[dir ?? "south"] || [1, 0, 0];
|
|
const lp = isL ? loc : { x: block.x - off[0], y: block.y - off[1], z: block.z - off[2] };
|
|
const rp = isL ? { x: block.x + off[0], y: block.y + off[1], z: block.z + off[2] } : loc;
|
|
targets = [{ pos: lp, id: FAIRY_L + next, partner: isR }, { pos: rp, id: FAIRY_R + next, partner: isL }];
|
|
colorSrc = FAIRY_L + next;
|
|
} else if (block.typeId.startsWith(WEB4_L) || block.typeId.startsWith(WEB4_R)) {
|
|
const isWL = block.typeId.startsWith(WEB4_L), isWR = block.typeId.startsWith(WEB4_R);
|
|
const variant = block.typeId.slice((isWL ? WEB4_L : WEB4_R).length);
|
|
const i = WEB4_VARIANTS.indexOf(variant); if (i < 0) return false;
|
|
const next = WEB4_VARIANTS[(i + 1) % WEB4_VARIANTS.length];
|
|
const off = WEB4_RIGHT_OFF[dir ?? "south"] || [1, 0, 0];
|
|
const lp = isWL ? loc : { x: block.x - off[0], y: block.y - off[1], z: block.z - off[2] };
|
|
const rp = isWL ? { x: block.x + off[0], y: block.y + off[1], z: block.z + off[2] } : loc;
|
|
targets = [{ pos: lp, id: WEB4_L + next, partner: isWR }, { pos: rp, id: WEB4_R + next, partner: isWL }];
|
|
colorSrc = WEB4_L + next;
|
|
} else if (swingParse(block.typeId)) {
|
|
const sw = swingParse(block.typeId);
|
|
const i = SWING_VARIANTS.indexOf(sw.variant);
|
|
if (i < 0) return false;
|
|
const nextV = SWING_VARIANTS[(i + 1) % SWING_VARIANTS.length];
|
|
const a = swingAnchor(sw.piece, block.x, block.y, block.z, dir ?? "south");
|
|
const cells = swingCells(a.x, a.y, a.z, dir ?? "south");
|
|
targets = SWING_PIECES.map((p) => ({ pos: cells[p], id: SWING_BASE + p + "_" + nextV, partner: p !== sw.piece }));
|
|
colorSrc = SWING_BASE + sw.piece + "_" + nextV;
|
|
} else if (ropeParse(block.typeId)) {
|
|
const rp = ropeParse(block.typeId);
|
|
const i = ROPE_VARIANTS.indexOf(rp.variant);
|
|
if (i < 0) return false;
|
|
const nextV = ROPE_VARIANTS[(i + 1) % ROPE_VARIANTS.length];
|
|
const bot = ropeBottom(rp.piece, block.x, block.y, block.z);
|
|
targets = [
|
|
{ pos: bot, id: ROPE_BASE + "b_" + nextV, partner: rp.piece !== "b" },
|
|
{ pos: { x: bot.x, y: bot.y + 1, z: bot.z }, id: ROPE_BASE + "t_" + nextV, partner: rp.piece !== "t" }
|
|
];
|
|
colorSrc = "pink";
|
|
} else if (umParse(block.typeId) || buParse(block.typeId)) {
|
|
const bamboo = !!umParse(block.typeId);
|
|
const CELLS = bamboo ? UM_CELLS : BU_CELLS, BASE = bamboo ? UM_BASE : BU_BASE;
|
|
const cols = bamboo ? ["yellow", "green"] : ["black", "white", "orange", "magenta", "light_blue", "yellow", "lime", "pink", "gray", "light_gray", "cyan", "purple", "blue", "green", "red", "rainbow"]; // DecoCraft canonical order (rainbow appended)
|
|
const up = bamboo ? umParse(block.typeId) : buParse(block.typeId);
|
|
const i = cols.indexOf(up.color); if (i < 0) return false;
|
|
const nextC = cols[(i + 1) % cols.length];
|
|
const o0 = CELLS[up.cell], ax = block.x - o0[0], ay = block.y - o0[1], az = block.z - o0[2];
|
|
targets = Object.keys(CELLS).map((cell) => { const oo = CELLS[cell]; return { pos: { x: ax + oo[0], y: ay + oo[1], z: az + oo[2] }, id: BASE + cell + "_" + nextC, partner: cell !== up.cell }; });
|
|
colorSrc = nextC;
|
|
} else if (block.typeId.startsWith(TW_FRONT) || block.typeId.startsWith(TW_BACK)) {
|
|
const isF = block.typeId.startsWith(TW_FRONT);
|
|
const color = block.typeId.slice((isF ? TW_FRONT : TW_BACK).length);
|
|
const i = TW_COLORS.indexOf(color); if (i < 0) return false;
|
|
const nextC = TW_COLORS[(i + 1) % TW_COLORS.length];
|
|
const off = TW_BACK_OFF[dir ?? "south"] || [0, 0, -1];
|
|
const fp = isF ? loc : { x: block.x - off[0], y: block.y - off[1], z: block.z - off[2] };
|
|
const bp = isF ? { x: block.x + off[0], y: block.y + off[1], z: block.z + off[2] } : loc;
|
|
targets = [{ pos: fp, id: TW_FRONT + nextC, partner: !isF }, { pos: bp, id: TW_BACK + nextC, partner: isF }];
|
|
colorSrc = nextC;
|
|
} else if (block.typeId.startsWith(LO_FRONT) || block.typeId.startsWith(LO_BACK)) {
|
|
const isF = block.typeId.startsWith(LO_FRONT);
|
|
const color = block.typeId.slice((isF ? LO_FRONT : LO_BACK).length);
|
|
const i = LO_COLORS.indexOf(color); if (i < 0) return false;
|
|
const nextC = LO_COLORS[(i + 1) % LO_COLORS.length];
|
|
const off = LO_BACK_OFF[dir ?? "south"] || [0, 0, -1];
|
|
const fp = isF ? loc : { x: block.x - off[0], y: block.y - off[1], z: block.z - off[2] };
|
|
const bp = isF ? { x: block.x + off[0], y: block.y + off[1], z: block.z + off[2] } : loc;
|
|
targets = [{ pos: fp, id: LO_FRONT + nextC, partner: !isF }, { pos: bp, id: LO_BACK + nextC, partner: isF }];
|
|
colorSrc = nextC;
|
|
} else if (btParse(block.typeId)) {
|
|
const p = btParse(block.typeId);
|
|
const i = BT_COLORS.indexOf(p.color); if (i < 0) return false;
|
|
const nextC = BT_COLORS[(i + 1) % BT_COLORS.length];
|
|
const o0 = BT_CELLS[p.cell], ax = block.x - o0[0], ay = block.y - o0[1], az = block.z - o0[2];
|
|
targets = Object.keys(BT_CELLS).map((cell) => { const oo = BT_CELLS[cell]; return { pos: { x: ax + oo[0], y: ay + oo[1], z: az + oo[2] }, id: BT_BASE + cell + "_" + nextC, partner: cell !== p.cell }; });
|
|
colorSrc = nextC;
|
|
} else if (LOUNGER.parse(block.typeId)) { // pool lounger: cycle the whole 3-cell lounger to the next colour (rotating grid)
|
|
const p = LOUNGER.parse(block.typeId);
|
|
const i = LOUNGER_COLORS.indexOf(p.color); if (i < 0) return false;
|
|
const nextC = LOUNGER_COLORS[(i + 1) % LOUNGER_COLORS.length];
|
|
const a = LOUNGER.anchorFrom(p.cell, block.x, block.y, block.z, dir ?? "south");
|
|
targets = Object.keys(LOUNGER.cells).map((cell) => ({ pos: LOUNGER.cellWorld(cell, a.x, a.y, a.z, dir ?? "south"), id: LOUNGER.idFor(cell, nextC), partner: cell !== p.cell }));
|
|
colorSrc = nextC;
|
|
} else if (scParse(block.typeId)) { // scarecrow: cycle the whole DESIGN (fa36_1/2/3) across both vertical cells
|
|
const sp = scParse(block.typeId);
|
|
const i = SC_VARIANTS.indexOf(sp.n); if (i < 0) return false;
|
|
const nextN = SC_VARIANTS[(i + 1) % SC_VARIANTS.length];
|
|
const bp = sp.cell === "b" ? loc : { x: block.x, y: block.y - 1, z: block.z }; // bottom anchor pos
|
|
const tp = { x: bp.x, y: bp.y + 1, z: bp.z };
|
|
targets = [{ pos: bp, id: "rzb_dcs:fa36_" + nextN + "_b", partner: sp.cell !== "b" }, { pos: tp, id: "rzb_dcs:fa36_" + nextN + "_t", partner: sp.cell !== "t" }];
|
|
colorSrc = "white";
|
|
} else if (bsParse(block.typeId)) { // balloons_small: cycle the bunch DESIGN across both vertical cells (within its regular/pastel group)
|
|
const sp = bsParse(block.typeId), cyc = BS_CYCLES[sp.v]; if (!cyc) return false; // halloween/valentines aren't cycled
|
|
const nextV = cyc[(cyc.indexOf(sp.v) + 1) % cyc.length];
|
|
const bp = sp.cell === "b" ? loc : { x: block.x, y: block.y - 1, z: block.z };
|
|
const tp = { x: bp.x, y: bp.y + 1, z: bp.z };
|
|
targets = [{ pos: bp, id: "rzb_dcs:bl_small_" + nextV + "_b", partner: sp.cell !== "b" }, { pos: tp, id: "rzb_dcs:bl_small_" + nextV + "_t", partner: sp.cell !== "t" }];
|
|
colorSrc = "white";
|
|
} else if (bhParse(block.typeId)) { // heart pairs: cycle combo across both cells (regular combos + pastel combos)
|
|
const sp = bhParse(block.typeId), cyc = BH_CYCLES[sp.v]; if (!cyc) return false;
|
|
const nextV = cyc[(cyc.indexOf(sp.v) + 1) % cyc.length];
|
|
const bp = sp.cell === "b" ? loc : { x: block.x, y: block.y - 1, z: block.z };
|
|
const tp = { x: bp.x, y: bp.y + 1, z: bp.z };
|
|
targets = [{ pos: bp, id: "rzb_dcs:bl_hearts_" + nextV + "_b", partner: sp.cell !== "b" }, { pos: tp, id: "rzb_dcs:bl_hearts_" + nextV + "_t", partner: sp.cell !== "t" }];
|
|
colorSrc = "white";
|
|
} else if (block.typeId.startsWith(LC_BASE) || block.typeId.startsWith(LC_AWNING)) {
|
|
const isB = block.typeId.startsWith(LC_BASE);
|
|
const color = block.typeId.slice((isB ? LC_BASE : LC_AWNING).length);
|
|
const i = LC_COLORS.indexOf(color); if (i < 0) return false;
|
|
const nextC = LC_COLORS[(i + 1) % LC_COLORS.length];
|
|
const bp = isB ? loc : { x: block.x, y: block.y - 1, z: block.z };
|
|
const aw = isB ? { x: block.x, y: block.y + 1, z: block.z } : loc;
|
|
targets = [{ pos: bp, id: LC_BASE + nextC, partner: !isB }, { pos: aw, id: LC_AWNING + nextC, partner: isB }];
|
|
colorSrc = nextC;
|
|
} else if (block.typeId.startsWith(SB_BASE) || block.typeId.startsWith(SB_TOP)) {
|
|
const isB = block.typeId.startsWith(SB_BASE);
|
|
const color = block.typeId.slice((isB ? SB_BASE : SB_TOP).length);
|
|
const i = SB_ORDER.indexOf(color); if (i < 0) return false;
|
|
const nextC = SB_ORDER[(i + 1) % SB_ORDER.length];
|
|
const bp = isB ? loc : { x: block.x, y: block.y - 1, z: block.z };
|
|
const tp = isB ? { x: block.x, y: block.y + 1, z: block.z } : loc;
|
|
targets = [{ pos: bp, id: SB_BASE + nextC, partner: !isB }, { pos: tp, id: SB_TOP + nextC, partner: isB }];
|
|
colorSrc = nextC;
|
|
} else if (block.typeId.startsWith(SB_LB) || block.typeId.startsWith(SB_LF)) {
|
|
const r = gridCellTargets(block, dir, sbLayParse, SB_ORDER, SB_LAY_CELLS, SB_LAY_PREFIX, sbLayCellWorld, sbLayAnchorFrom); if (!r) return false; ({ targets, colorSrc } = r);
|
|
} else if (block.typeId.startsWith(BB_B) || block.typeId.startsWith(BB_FR)) {
|
|
const r = gridCellTargets(block, dir, bbParse, BB_ORDER, BB_CELLS, BB_PREFIX, bbCellWorld, bbAnchorFrom); if (!r) return false; ({ targets, colorSrc } = r);
|
|
} else if (block.typeId.startsWith(SLED_B) || block.typeId.startsWith(SLED_FR)) {
|
|
const r = gridCellTargets(block, dir, sledParse, SLED_ORDER, SLED_CELLS, SLED_PREFIX, sledCellWorld, sledAnchorFrom); if (!r) return false; ({ targets, colorSrc } = r);
|
|
} else {
|
|
const seq = VARIANT_SEQ[block.typeId];
|
|
if (!seq) return false;
|
|
const next = seq[(seq.indexOf(block.typeId) + 1) % seq.length];
|
|
targets = [{ pos: loc, id: next }];
|
|
colorSrc = next;
|
|
}
|
|
|
|
const pcol = particleFor(colorSrc);
|
|
system.runTimeout(() => {
|
|
for (const t of targets) {
|
|
const b = dim.getBlock(t.pos);
|
|
if (!b) continue;
|
|
if (t.partner && !(b.typeId.startsWith(WEB4_L) || b.typeId.startsWith(WEB4_R) || b.typeId.startsWith(FAIRY_L) || b.typeId.startsWith(FAIRY_R) || b.typeId.startsWith(SWING_BASE) || b.typeId.startsWith(ROPE_BASE) || b.typeId.startsWith(UM_BASE) || b.typeId.startsWith(BU_BASE) || b.typeId.startsWith("rzb_dcs:tw_") || b.typeId.startsWith("rzb_dcs:lo_") || b.typeId.startsWith(BT_BASE) || b.typeId.startsWith("rzb_dcs:su03_") || b.typeId.startsWith("rzb_dcs:lc_") || b.typeId.startsWith("rzb_dcs:fa36_") || b.typeId.startsWith("rzb_dcs:bl_small_") || b.typeId.startsWith("rzb_dcs:bl_hearts_") || b.typeId.startsWith(SB_BASE) || b.typeId.startsWith(SB_TOP) || b.typeId.startsWith(SB_LB) || b.typeId.startsWith(SB_LF) || b.typeId.startsWith(BB_B) || b.typeId.startsWith(BB_FR) || b.typeId.startsWith(SLED_B) || b.typeId.startsWith(SLED_FR))) continue;
|
|
try { b.setPermutation(BlockPermutation.resolve(t.id, dir !== undefined ? { "minecraft:cardinal_direction": dir } : {})); } catch { try { b.setType(t.id); } catch {} }
|
|
}
|
|
brushFx(dim, loc, pcol, player);
|
|
}, 1);
|
|
return true;
|
|
}
|
|
const brushable = (id) => VARIANT_SEQ[id] || umParse(id) || buParse(id) || btParse(id) || LOUNGER.parse(id) || ctParse(id) || ctParseW(id) || ltParse(id) || ltParseW(id) || pctParse(id) || pctParseW(id) || koinoParse(id) || koinoAnchorBrushable(id) || displayAnchorBrushable(id) || SPINNER_NEXT[id] || scParse(id) || bsParse(id) || bhParse(id) || id.startsWith("rzb_dcs:tw_") || id.startsWith("rzb_dcs:lo_") || id.startsWith("rzb_dcs:lc_");
|
|
|
|
world.beforeEvents.playerBreakBlock?.subscribe((ev) => {
|
|
if (ev.itemStack?.typeId !== "rzb_dcs:brush_color") return;
|
|
if (brushable(ev.block.typeId)) { ev.cancel = true; cycleVariant(ev.block, ev.player); }
|
|
});
|
|
world.beforeEvents.playerInteractWithBlock?.subscribe((ev) => {
|
|
if (ev.itemStack?.typeId !== "rzb_dcs:brush_color") return;
|
|
if (brushable(ev.block.typeId)) { ev.cancel = true; cycleVariant(ev.block, ev.player); }
|
|
});
|