feat(easter-eggs): add easter egg hunt addon across all worlds
All checks were successful
Deploy Addons / deploy (push) Successful in 17s
All checks were successful
Deploy Addons / deploy (push) Successful in 17s
50 eggs hidden per child world (Jamie/Lyla/Mya) in 8 creative hiding scenes (barrels, hay bales, flower patches, logs, fences, crafting tables, mossy ledges, leaf clusters). Each world uses a distinct glazed terracotta colour. Found eggs are tracked via player tags which persist across server transfers; lobby reads tags and maintains a scoreboard leaderboard (egg_count objective). Gitea deploy workflow updated to include easter-egg-addon/ and docker-compose.yml in checkout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
129
easter-egg-addon/easter_egg_lobby_BP/scripts/main.js
Normal file
129
easter-egg-addon/easter_egg_lobby_BP/scripts/main.js
Normal file
@@ -0,0 +1,129 @@
|
||||
import { world, system } from "@minecraft/server";
|
||||
|
||||
// ─── Constants ───────────────────────────────────────────────────
|
||||
|
||||
const OBJECTIVE = "egg_count";
|
||||
const EGG_PATTERN = /^egg_[jlm]_\d+$/;
|
||||
|
||||
// ─── Scoreboard Setup ────────────────────────────────────────────
|
||||
|
||||
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
|
||||
}
|
||||
try {
|
||||
overworld.runCommand(`scoreboard objectives setdisplay sidebar ${OBJECTIVE} descending`);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// ─── Egg Counting ────────────────────────────────────────────────
|
||||
|
||||
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++;
|
||||
}
|
||||
return { total: j + l + m, j, l, m };
|
||||
}
|
||||
|
||||
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 seconds) after spawn for player state/tags to fully load,
|
||||
// then update their scoreboard position 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 === 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!");
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
// beforeEvents.chatSend requires beta API — chat commands unavailable
|
||||
}
|
||||
|
||||
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();
|
||||
// Update scores for anyone already online
|
||||
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.");
|
||||
});
|
||||
Reference in New Issue
Block a user