fix(easter-egg): persistent cross-server counts via inventory basket; 2-block tall eggs
All checks were successful
Deploy Addons / deploy (push) Successful in 15s

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>
This commit is contained in:
2026-04-05 02:46:26 +01:00
parent 793c1f5f3d
commit e5568fe1f3
2 changed files with 203 additions and 97 deletions

View File

@@ -2,8 +2,10 @@ import { world, system } from "@minecraft/server";
// ─── Constants ───────────────────────────────────────────────────
const OBJECTIVE = "egg_count";
const EGG_PATTERN = /^egg_[jlm]_\d+$/;
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 ────────────────────────────────────────────
@@ -11,28 +13,47 @@ function ensureScoreboard() {
const overworld = world.getDimension("overworld");
try {
overworld.runCommand(`scoreboard objectives add ${OBJECTIVE} dummy "§6Easter Eggs §7(total)"`);
} catch (e) {
// Objective already exists — not an error
}
} catch (e) {}
try {
overworld.runCommand(`scoreboard objectives setdisplay sidebar ${OBJECTIVE} descending`);
} catch (e) {}
}
// ─── Egg Counting ────────────────────────────────────────────────
// ─── 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 tags = player.getTags();
let j = 0, l = 0, m = 0;
for (const tag of tags) {
if (!EGG_PATTERN.test(tag)) continue;
if (tag.startsWith("egg_j_")) j++;
else if (tag.startsWith("egg_l_")) l++;
else if (tag.startsWith("egg_m_")) m++;
}
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 {
@@ -42,8 +63,8 @@ function updatePlayerScore(player) {
}
// ─── Player Spawn ────────────────────────────────────────────────
// Wait 40 ticks (2 seconds) after spawn for player state/tags to fully load,
// then update their scoreboard position and show a welcome subtitle.
// 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;
@@ -81,14 +102,14 @@ function handleLobbyEggCommand(player) {
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 === 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.");
} else if (total === 0) {
player.sendMessage("§7Visit Jamie, Lyla, and Mya's worlds to find hidden eggs!");
}
}
@@ -101,9 +122,7 @@ try {
system.run(() => handleLobbyEggCommand(player));
}
});
} catch (e) {
// beforeEvents.chatSend requires beta API — chat commands unavailable
}
} catch (e) {}
system.afterEvents.scriptEventReceive.subscribe((event) => {
if (event.id !== "eggs:cmd") return;
@@ -118,7 +137,6 @@ system.afterEvents.scriptEventReceive.subscribe((event) => {
system.runTimeout(() => {
ensureScoreboard();
// Update scores for anyone already online
for (const player of world.getAllPlayers()) {
updatePlayerScore(player);
}