import { world, system, BlockPermutation } from "@minecraft/server";

/* =========================
   📦 STORAGE
      ========================= */
const pos1 = new Map();
const pos2 = new Map();

const undoStack = new Map();
const redoStack = new Map();

const clipboard = new Map();

const particleToggle = new Map(); // ⭐ pa ON/OFF

const AXE = "minecraft:wooden_axe";
const COMPASS = "minecraft:compass";

/* =========================
   🪓 START ITEMS
      ========================= */
function giveStartItems(p) {
    system.run(() => {
        p.runCommand("give @s wooden_axe 1");
        p.runCommand("give @s compass 1");
    });
}

world.afterEvents.playerSpawn.subscribe(ev => {
    const p = ev.player;

    system.run(() => {
        giveStartItems(p);
        particleToggle.set(p.id, true); // ⭐ 여기 추가
    });
});


/* ============================
   🪓 POS SYSTEM
      ========================= */
world.beforeEvents.playerBreakBlock.subscribe(ev => {
    const p = ev.player;

    const item = p.getComponent("inventory")?.container
        .getItem(p.selectedSlotIndex);

    if (!item || item.typeId !== AXE) return;

    ev.cancel = true;

    const loc = ev.block.location;

    if (p.isSneaking) {
        pos2.set(p.id, loc);
        p.sendMessage(`§bPos2 §f${loc.x} ${loc.y} ${loc.z}`);
    } else {
        pos1.set(p.id, loc);
        p.sendMessage(`§aPos1 §f${loc.x} ${loc.y} ${loc.z}`);
    }

    drawBox(p);
});

/* =========================
   ✨ PARTICLE BOX (OUTLINE ONLY)
      ========================= */
function drawBox(p) {
    if (!particleToggle.get(p.id)) return;

    const a = pos1.get(p.id);
    const b = pos2.get(p.id);
    if (!a || !b) return;

    const dim = p.dimension;

    const minX = Math.min(a.x, b.x);
    const maxX = Math.max(a.x, b.x);
    const minY = Math.min(a.y, b.y); // ✅ FIX
    const maxY = Math.max(a.y, b.y);
    const minZ = Math.min(a.z, b.z);
    const maxZ = Math.max(a.z, b.z);

    system.run(() => {

        const spawn = (x, y, z, blockId) => {

            const colorMap = {
                "minecraft:stone": { r: 0.5, g: 0.5, b: 0.5 },
                "minecraft:grass_block": { r: 0.3, g: 1, b: 0.3 },
                "minecraft:dirt": { r: 0.5, g: 0.3, b: 0.1 },
                "minecraft:water": { r: 0.2, g: 0.4, b: 1 }
            };

            const c = colorMap[blockId] || { r: 1, g: 0.6, b: 0.2 };

            // 🔥 flame (basic_flame)
            dim.spawnParticle("minecraft:basic_flame_particle", {
                x: x + 0.5,
                y: y + 0.5,
                z: z + 0.5
            });

            // ✨ outline 느낌
            dim.spawnParticle("minecraft:endrod", {
                x: x + 0.5,
                y: y + 0.5,
                z: z + 0.5
            });
        };

        // bottom / top
        for (let x = minX; x <= maxX; x++) {
            for (let z = minZ; z <= maxZ; z++) {
                const b1 = dim.getBlock({ x, y: minY, z });
                const b2 = dim.getBlock({ x, y: maxY, z });

                spawn(x, minY, z, b1?.typeId);
                spawn(x, maxY, z, b2?.typeId);
            }
        }

        // vertical edges
        for (let y = minY; y <= maxY; y++) {
            for (let x of [minX, maxX]) {
                for (let z of [minZ, maxZ]) {
                    const b = dim.getBlock({ x, y, z });
                    spawn(x, y, z, b?.typeId);
                }
            }
        }
    });
}

/*=========================*/

system.runInterval(() => {
    for (const p of world.getAllPlayers()) {
        drawBox(p);
    }
}, 2); // 2틱마다 갱신 (성능 + 부드러움 밸런스)


/* =========================
   🧱 SET
      ========================= */
function setBlocks(p, blockId) {
    const a = pos1.get(p.id);
    const b = pos2.get(p.id);
    if (!a || !b) return;

    const dim = p.dimension;
    const type = blockId.startsWith("minecraft:") ? blockId : "minecraft:" + blockId;

    let changes = [];

    system.run(() => {
        for (let x = Math.min(a.x, b.x); x <= Math.max(a.x, b.x); x++)
            for (let y = Math.min(a.y, b.y); y <= Math.max(a.y, b.y); y++)
                for (let z = Math.min(a.z, b.z); z <= Math.max(a.z, b.z); z++) {

                    const pos = { x, y, z };
                    const block = dim.getBlock(pos);
                    if (!block) continue;

                    changes.push({
                        pos,
                        old: block.typeId,
                        new: type
                    });

                    block.setPermutation(BlockPermutation.resolve(type));
                }

        pushUndo(p, changes);
    });
}

/* =========================
   🔁 REPLACE
      ========================= */
function replaceBlocks(p, from, to) {
    const a = pos1.get(p.id);
    const b = pos2.get(p.id);
    if (!a || !b) return;

    const dim = p.dimension;

    from = from.startsWith("minecraft:") ? from : "minecraft:" + from;
    to = to.startsWith("minecraft:") ? to : "minecraft:" + to;

    let changes = [];

    system.run(() => {
        for (let x = Math.min(a.x, b.x); x <= Math.max(a.x, b.x); x++)
            for (let y = Math.min(a.y, b.y); y <= Math.max(a.y, b.y); y++)
                for (let z = Math.min(a.z, b.z); z <= Math.max(a.z, b.z); z++) {

                    const pos = { x, y, z };
                    const block = dim.getBlock(pos);
                    if (!block) continue;

                    if (block.typeId !== from) continue;

                    changes.push({
                        pos,
                        old: block.typeId,
                        new: to
                    });

                    block.setPermutation(BlockPermutation.resolve(to));
                }

        pushUndo(p, changes);
    });
}

/* =========================
   📋 COPY / PASTE
      ========================= */
function copy(p) {
    const a = pos1.get(p.id);
    const b = pos2.get(p.id);
    if (!a || !b) return;

    const dim = p.dimension;
    let data = [];

    for (let x = Math.min(a.x, b.x); x <= Math.max(a.x, b.x); x++)
        for (let y = Math.min(a.y, b.y); y <= Math.max(a.y, b.y); y++)
            for (let z = Math.min(a.z, b.z); z <= Math.max(a.z, b.z); z++) {

                const block = dim.getBlock({ x, y, z });
                if (!block) continue;

                data.push({
                    dx: x - a.x,
                    dy: y - a.y,
                    dz: z - a.z,
                    type: block.typeId
                });
            }

    clipboard.set(p.id, data);
}

function paste(p) {
    const data = clipboard.get(p.id);
    if (!data) return;

    const dim = p.dimension;
    const base = p.location;

    let changes = [];

    system.run(() => {
        for (const b of data) {

            const pos = {
                x: Math.floor(base.x + b.dx),
                y: Math.floor(base.y + b.dy),
                z: Math.floor(base.z + b.dz)
            };

            const block = dim.getBlock(pos);
            if (!block) continue;

            changes.push({
                pos,
                old: block.typeId,
                new: b.type
            });

            block.setPermutation(BlockPermutation.resolve(b.type));
        }

        pushUndo(p, changes);
    });
}

/* =========================
   🔄 ROTATE
      ========================= */
function rotateSelection(p, angle = 90) {
    const a = pos1.get(p.id);
    const b = pos2.get(p.id);
    if (!a || !b) return;

    const dim = p.dimension;

    const minX = Math.min(a.x, b.x);
    const minY = Math.min(a.y, b.y);
    const minZ = Math.min(a.z, b.z);

    const maxX = Math.max(a.x, b.x);
    const maxY = Math.max(a.y, b.y);
    const maxZ = Math.max(a.z, b.z);

    let blocks = [];

    for (let x = minX; x <= maxX; x++)
        for (let y = minY; y <= maxY; y++)
            for (let z = minZ; z <= maxZ; z++) {

                const b = dim.getBlock({ x, y, z });
                if (!b) continue;

                blocks.push({
                    x: x - minX,
                    y: y - minY,
                    z: z - minZ,
                    type: b.typeId
                });
            }

    const times = angle === 90 ? 1 : angle === 180 ? 2 : 3;

    for (let i = 0; i < times; i++) {
        blocks = blocks.map(b => ({
            x: b.z,
            y: b.y,
            z: -b.x,
            type: b.type
        }));
    }

    system.run(() => {
        for (const b of blocks) {
            const pos = {
                x: minX + b.x,
                y: minY + b.y,
                z: minZ + b.z
            };

            const block = dim.getBlock(pos);
            if (block) block.setPermutation(BlockPermutation.resolve(b.type));
        }
    });
}

/* =========================
   🔁 UNDO / REDO
      ========================= */
function pushUndo(p, changes) {
    const u = undoStack.get(p.id) || [];
    u.push(JSON.parse(JSON.stringify(changes)));
    undoStack.set(p.id, u);
    redoStack.set(p.id, []);
}

function undo(p) {
    const stack = undoStack.get(p.id);
    if (!stack?.length) return;

    const changes = stack.pop();
    undoStack.set(p.id, stack);

    system.run(() => {
        for (const c of changes) {
            const b = p.dimension.getBlock(c.pos);
            if (b) b.setPermutation(BlockPermutation.resolve(c.old));
        }
    });

    const r = redoStack.get(p.id) || [];
    r.push(JSON.parse(JSON.stringify(changes)));
    redoStack.set(p.id, r);
}

function redo(p) {
    const stack = redoStack.get(p.id);
    if (!stack?.length) return;

    const changes = stack.pop();
    redoStack.set(p.id, stack);

    system.run(() => {
        for (const c of changes) {
            const b = p.dimension.getBlock(c.pos);
            if (b) b.setPermutation(BlockPermutation.resolve(c.new));
        }
    });

    const u = undoStack.get(p.id) || [];
    u.push(JSON.parse(JSON.stringify(changes)));
    undoStack.set(p.id, u);
}

/* =========================
   🧭 COMPASS TP (FIXED)
      ========================= */
world.beforeEvents.itemUse.subscribe(ev => {
    const p = ev.source;
    const item = ev.itemStack;

    if (!item || item.typeId !== "minecraft:compass") return;

    ev.cancel = true;

    system.run(() => {
        const dim = p.dimension;
        const start = p.getHeadLocation ? p.getHeadLocation() : p.location;
        const dir = p.getViewDirection();

        let hit = null;

        for (let d = 0; d < 128; d += 0.25) {

            const pos = {
                x: Math.floor(start.x + dir.x * d),
                y: Math.floor(start.y + dir.y * d),
                z: Math.floor(start.z + dir.z * d)
            };

            const block = dim.getBlock(pos);
            if (block && block.typeId !== "minecraft:air") {
                hit = pos;
                break;
            }
        }

        if (!hit) return p.sendMessage("§c앞에 블록 없음!");

        p.teleport(
            { x: hit.x + 0.5, y: hit.y + 1, z: hit.z + 0.5 },
            { dimension: dim }
        );
    });
});

/* =========================
   💬 COMMANDS
      ========================= */
world.beforeEvents.chatSend.subscribe(ev => {
    const p = ev.sender;
    const msg = ev.message;

    ev.cancel = true;

    if (msg === ";undo") undo(p);
    if (msg === ";redo") redo(p);
    if (msg === ";copy") copy(p);
    if (msg === ";paste") paste(p);

    if (msg.startsWith(";set ")) setBlocks(p, msg.split(" ")[1]);

    if (msg.startsWith(";replace ")) {
        const a = msg.split(" ");
        replaceBlocks(p, a[1], a[2]);
    }

    if (msg.startsWith(";rotate ")) {
        rotateSelection(p, parseInt(msg.split(" ")[1]) || 90);
    }

    if (msg === ";pa") {
        const v = particleToggle.get(p.id) ?? true;
        particleToggle.set(p.id, !v);

        p.sendMessage("§aParticle: " + (!v ? "ON" : "OFF"));
    }

    if (msg === ";help") {
        p.sendMessage(`§6===== PRO WE =====
;set ;replace
;copy ;paste
;undo ;redo
;rotate
;pa (toggle particle)
==================`);
    }
});

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: