Files
minecraft-aiworld/easter-egg-addon/easter_egg_lobby_BP/scripts/main.js
SysAdmin e5568fe1f3
All checks were successful
Deploy Addons / deploy (push) Successful in 15s
fix(easter-egg): persistent cross-server counts via inventory basket; 2-block tall eggs
Player tags are server-local and wiped on transfer, breaking lobby scores.
Switch to an Egg Basket (nether_star, custom nameTag "[j:N,l:N,m:N]") that
travels with the player's inventory so the lobby can always read accurate counts.

Child worlds rebuild lost tags from block-state on each player join (missing
egg block = previously collected), keeping within-world deduplication intact.
Basket is updated on every collection and after every tag rebuild.

Lobby reads basket counts directly instead of tags.

Also make eggs visually egg-shaped: 2 glazed-terracotta blocks tall with
opposing facing_direction (2 bottom, 3 top) to create an oval silhouette.
collectEgg() now clears both Y and Y+1; detection dy extended to 3.0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 02:46:26 +01:00

148 lines
6.0 KiB
JavaScript

import { world, system } from "@minecraft/server";
// ─── Constants ───────────────────────────────────────────────────
const OBJECTIVE = "egg_count";
const BASKET_ITEM = "minecraft:nether_star";
const BASKET_HEAD = "§6Egg Basket ";
const BASKET_REGEX = /\[j:(\d+),l:(\d+),m:(\d+)\]/;
// ─── Scoreboard Setup ────────────────────────────────────────────
function ensureScoreboard() {
const overworld = world.getDimension("overworld");
try {
overworld.runCommand(`scoreboard objectives add ${OBJECTIVE} dummy "§6Easter Eggs §7(total)"`);
} catch (e) {}
try {
overworld.runCommand(`scoreboard objectives setdisplay sidebar ${OBJECTIVE} descending`);
} catch (e) {}
}
// ─── Basket Reading ──────────────────────────────────────────────
// Counts are read from the player's Egg Basket (nether_star with encoded nameTag).
// The basket travels with the player's inventory across server transfers, making
// it the reliable cross-world persistence mechanism — unlike tags which are
// server-local and wiped on each transfer.
function getEggBasket(player) {
try {
const inv = player.getComponent("minecraft:inventory");
if (!inv) return null;
const container = inv.container;
for (let i = 0; i < container.size; i++) {
const item = container.getItem(i);
if (item && item.typeId === BASKET_ITEM &&
item.nameTag && item.nameTag.startsWith(BASKET_HEAD)) {
return item;
}
}
} catch (e) {}
return null;
}
function countPlayerEggs(player) {
const basket = getEggBasket(player);
if (!basket || !basket.nameTag) return { total: 0, j: 0, l: 0, m: 0 };
const match = basket.nameTag.match(BASKET_REGEX);
if (!match) return { total: 0, j: 0, l: 0, m: 0 };
const j = parseInt(match[1], 10);
const l = parseInt(match[2], 10);
const m = parseInt(match[3], 10);
return { total: j + l + m, j, l, m };
}
// ─── Score Update ────────────────────────────────────────────────
function updatePlayerScore(player) {
const counts = countPlayerEggs(player);
try {
player.runCommand(`scoreboard players set @s ${OBJECTIVE} ${counts.total}`);
} catch (e) {}
return counts;
}
// ─── Player Spawn ────────────────────────────────────────────────
// Wait 40 ticks (2 s) after spawn for inventory to fully load,
// then update scoreboard and show a welcome subtitle.
world.afterEvents.playerSpawn.subscribe((event) => {
const player = event.player;
system.runTimeout(() => {
try {
const { total, j, l, m } = updatePlayerScore(player);
if (total > 0) {
player.runCommand(
`titleraw @s subtitle {"rawtext":[{"text":"§7Eggs found: §6${total}§7/150 §8(§aJ:${j} §dL:${l} §bM:${m}§8)"}]}`
);
}
if (total === 150) {
world.sendMessage(`§6§l🥚 ${player.name} has found ALL 150 easter eggs! 🥚`);
}
} catch (e) {}
}, 40);
});
// ─── Refresh on Interval ─────────────────────────────────────────
// Keep scores up-to-date as players arrive from child worlds.
system.runInterval(() => {
for (const player of world.getAllPlayers()) {
updatePlayerScore(player);
}
}, 100); // every 5 seconds
// ─── Chat Commands ───────────────────────────────────────────────
function handleLobbyEggCommand(player) {
const { total, j, l, m } = countPlayerEggs(player);
player.sendMessage("§6[Easter Eggs] §fYour collection:");
player.sendMessage(` §aJamie's World: §e${j}§7/50 ${"§2▓".repeat(Math.floor(j / 5))}§8${"░".repeat(10 - Math.floor(j / 5))}`);
player.sendMessage(` §dLyla's World: §e${l}§7/50 ${"§5▓".repeat(Math.floor(l / 5))}§8${"░".repeat(10 - Math.floor(l / 5))}`);
player.sendMessage(` §bMya's World: §e${m}§7/50 ${"§3▓".repeat(Math.floor(m / 5))}§8${"░".repeat(10 - Math.floor(m / 5))}`);
player.sendMessage(` §6Total: §e${total}§7/150`);
if (total === 0) {
player.sendMessage("§7Visit Jamie, Lyla, and Mya's worlds to find hidden eggs! Look in flower patches, on barrels, hay bales, logs and more.");
} else if (total === 150) {
player.sendMessage("§a§l🥚 YOU FOUND ALL 150 EGGS! You are the Easter Champion! 🥚");
} else if (total >= 100) {
player.sendMessage("§6Almost there! Just " + (150 - total) + " more to go!");
} else if (total >= 50) {
player.sendMessage("§eGreat progress! Keep exploring each world.");
}
}
try {
world.beforeEvents.chatSend.subscribe((event) => {
const msg = event.message.trim().toLowerCase();
if (msg === "!eggs") {
event.cancel = true;
const player = event.sender;
system.run(() => handleLobbyEggCommand(player));
}
});
} catch (e) {}
system.afterEvents.scriptEventReceive.subscribe((event) => {
if (event.id !== "eggs:cmd") return;
const player = event.sourceEntity;
if (!player) return;
if ((event.message || "").trim().toLowerCase() === "eggs") {
handleLobbyEggCommand(player);
}
});
// ─── Startup ─────────────────────────────────────────────────────
system.runTimeout(() => {
ensureScoreboard();
for (const player of world.getAllPlayers()) {
updatePlayerScore(player);
}
}, 20);
system.run(() => {
world.sendMessage("§6[Easter Eggs] §fLeaderboard active! Type §e!eggs §fto see your full collection.");
});