Files
minecraft-aiworld/lobby-addon/lobby_transfer_BP/scripts/main.js
SysAdmin cc18066c17 fix(lobby,easter-egg): update portal coordinates and egg-check ring
Lobby portal zones and signs moved to the rebuilt portal area at
~(436..488, 65..74, -322..-281), including a second Lyla's-World
"Super Kitties" portal. Signs now use per-portal facing_direction
so they face the hub walkway correctly.

Easter-egg placeAllEggs validation now samples the far-ring eggs
(indices 45-49) before falling back to the near ring — avoids false
"blocks missing" reports when only close-ring eggs have been picked up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 14:19:33 +01:00

154 lines
5.7 KiB
JavaScript

import { world, system } from "@minecraft/server";
import { transferPlayer } from "@minecraft/server-admin";
// Portal block → transfer target mapping (custom blocks — priority detection)
const PORTAL_BLOCKS = {
"silverlabs:portal_jamie": { name: "Jamie's World", host: "10.0.0.247", port: 19133, color: "§a" },
"silverlabs:portal_lyla": { name: "Lyla's World", host: "10.0.0.247", port: 19134, color: "§d" },
"silverlabs:portal_mya": { name: "Mya's World", host: "10.0.0.247", port: 19135, color: "§b" },
};
// Coordinate-based portal zones (fallback detection)
const PORTAL_ZONES = [
{ name: "Jamie's World", x: 436, y: 66, z: -296, host: "10.0.0.247", port: 19133, color: "§a" },
{ name: "Lyla's World", x: 462, y: 65, z: -322, host: "10.0.0.247", port: 19134, color: "§d" },
{ name: "Lyla's World", x: 474, y: 65, z: -281, host: "10.0.0.247", port: 19134, color: "§d" }, // Super Kitties portal
{ name: "Mya's World", x: 488, y: 66, z: -296, host: "10.0.0.247", port: 19135, color: "§b" },
];
const PORTAL_RADIUS_X = 2.5;
const PORTAL_RADIUS_Z = 2.0;
const PORTAL_RADIUS_Y = 2.0;
const COOLDOWN_TICKS = 100; // 5 seconds cooldown
const SPAWN_PROTECTION_TICKS = 200; // 10 seconds — ignore portal detection after spawn
// Track cooldowns per player
const cooldowns = new Map();
// Track when players spawned (to prevent transfer loop on arrival)
const spawnTicks = new Map();
world.afterEvents.playerSpawn.subscribe((event) => {
spawnTicks.set(event.player.id, system.currentTick);
});
/**
* Check if player is standing on/in a custom portal block (priority method).
* Returns the portal config or null.
*/
function checkBlockPortal(player) {
const pos = player.location;
const dimension = player.dimension;
const blockAtFeet = dimension.getBlock({ x: Math.floor(pos.x), y: Math.floor(pos.y), z: Math.floor(pos.z) });
const blockBelow = dimension.getBlock({ x: Math.floor(pos.x), y: Math.floor(pos.y) - 1, z: Math.floor(pos.z) });
const feetId = blockAtFeet?.typeId;
const belowId = blockBelow?.typeId;
return PORTAL_BLOCKS[feetId] || PORTAL_BLOCKS[belowId] || null;
}
/**
* Check if player is within a coordinate-based portal zone (fallback method).
* Returns the portal config or null.
*/
function checkZonePortal(player) {
const pos = player.location;
for (const portal of PORTAL_ZONES) {
const dx = Math.abs(pos.x - portal.x);
const dy = Math.abs(pos.y - portal.y);
const dz = Math.abs(pos.z - portal.z);
if (dx < PORTAL_RADIUS_X && dy < PORTAL_RADIUS_Y && dz < PORTAL_RADIUS_Z) {
return portal;
}
}
return null;
}
system.runInterval(() => {
for (const player of world.getAllPlayers()) {
const playerId = player.id;
// Check cooldown
const lastTransfer = cooldowns.get(playerId) || 0;
if (system.currentTick - lastTransfer < COOLDOWN_TICKS) continue;
// Skip if player just spawned (prevents loop when arriving from child world)
const spawnedAt = spawnTicks.get(playerId) || 0;
if (system.currentTick - spawnedAt < SPAWN_PROTECTION_TICKS) continue;
// Hybrid detection: custom block first, then coordinate fallback
const portal = checkBlockPortal(player) || checkZonePortal(player);
if (!portal) continue;
cooldowns.set(playerId, system.currentTick);
// Show title notification
player.runCommand(`titleraw @s title {"rawtext":[{"text":"${portal.color}${portal.name}"}]}`);
player.runCommand(`titleraw @s subtitle {"rawtext":[{"text":"§7Transferring..."}]}`);
player.sendMessage(`§6Transferring to ${portal.name}...`);
// Teleport player away from portal before transfer so their saved position
// is NOT on the portal (prevents re-transfer loop when they return to lobby)
const safePos = player.location;
safePos.z += 4; // Move 4 blocks away from portals (portals are at z=-24)
player.teleport(safePos);
// Wait 5 ticks for position to save, then transfer
system.runTimeout(() => {
try {
transferPlayer(player, { hostname: portal.host, port: portal.port });
} catch (e) {
player.sendMessage(`§cTransfer failed: ${e.message}`);
}
}, 5);
}
}, 10); // Check every half second
// Clean up tracking when players leave
world.afterEvents.playerLeave.subscribe((event) => {
cooldowns.delete(event.playerId);
spawnTicks.delete(event.playerId);
});
// ─── Place signs above each portal on load ──────────────────────
function placePortalSigns() {
const overworld = world.getDimension("overworld");
const signs = [
{ x: 438, y: 71, z: -296, name: "Jamie's", color: "§a", facing: 5 }, // east-facing
{ x: 462, y: 71, z: -320, name: "Lyla's", color: "§d", facing: 3 }, // south-facing
{ x: 486, y: 71, z: -296, name: "Mya's", color: "§b", facing: 4 }, // west-facing
{ x: 474, y: 74, z: -281, name: "Super Kitties\n§fLyla's", color: "§d", facing: 2 }, // north-facing (Super Kitties portal)
];
for (const sign of signs) {
try {
overworld.runCommand(`setblock ${sign.x} ${sign.y} ${sign.z} oak_wall_sign ["facing_direction":${sign.facing}]`);
} catch (e) {
// Non-fatal — chunks may not be loaded
}
}
system.runTimeout(() => {
for (const sign of signs) {
try {
const block = overworld.getBlock({ x: sign.x, y: sign.y, z: sign.z });
if (!block) continue;
const signComponent = block.getComponent("minecraft:sign");
if (!signComponent) continue;
signComponent.setText(`${sign.color}${sign.name}\n${sign.color}World\n§7▼ Step in ▼`);
} catch (e) {
// Non-fatal
}
}
}, 10);
}
system.runTimeout(() => {
placePortalSigns();
}, 40);
system.run(() => {
world.sendMessage("§6[Hub] §7Portal transfer system loaded!");
});