feat(portals): add craftable portal blocks replacing hardcoded coordinates
Portal frame crafted from obsidian + ender pearl, combined with crystals (emerald/amethyst/prismarine) to create world-specific portal blocks. Stepping on a portal block triggers transfer. Includes resource pack with vanilla textures, block definitions, crafting recipes, and updated script. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,13 @@
|
||||
import { world, system } from "@minecraft/server";
|
||||
import { transferPlayer } from "@minecraft/server-admin";
|
||||
|
||||
// Portal definitions: name, center position, color, and direct transfer target
|
||||
const portals = [
|
||||
{ name: "Jamie's World", x: -15, y: 65, z: -24, host: "10.0.0.247", port: 19133, color: "§a" },
|
||||
{ name: "Lyla's World", x: 0, y: 65, z: -24, host: "10.0.0.247", port: 19134, color: "§d" },
|
||||
{ name: "Mya's World", x: 15, y: 65, z: -24, host: "10.0.0.247", port: 19135, color: "§b" },
|
||||
];
|
||||
// Portal block → transfer target mapping
|
||||
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" },
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
@@ -36,24 +33,28 @@ system.runInterval(() => {
|
||||
const spawnedAt = spawnTicks.get(playerId) || 0;
|
||||
if (system.currentTick - spawnedAt < SPAWN_PROTECTION_TICKS) continue;
|
||||
|
||||
for (const portal of portals) {
|
||||
const dx = Math.abs(pos.x - portal.x);
|
||||
const dy = Math.abs(pos.y - portal.y);
|
||||
const dz = Math.abs(pos.z - portal.z);
|
||||
// Check block at player's feet and one block below
|
||||
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) });
|
||||
|
||||
if (dx < PORTAL_RADIUS_X && dy < PORTAL_RADIUS_Y && dz < PORTAL_RADIUS_Z) {
|
||||
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}...`);
|
||||
try {
|
||||
transferPlayer(player, { hostname: portal.host, port: portal.port });
|
||||
} catch (e) {
|
||||
player.sendMessage(`§cTransfer failed: ${e.message}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
const feetId = blockAtFeet?.typeId;
|
||||
const belowId = blockBelow?.typeId;
|
||||
|
||||
const portal = PORTAL_BLOCKS[feetId] || PORTAL_BLOCKS[belowId];
|
||||
if (!portal) continue;
|
||||
|
||||
cooldowns.set(playerId, system.currentTick);
|
||||
// Teleport player away from the portal block so they don't land on it on return
|
||||
player.teleport({ x: pos.x, y: pos.y, z: pos.z + 3 });
|
||||
// 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}...`);
|
||||
try {
|
||||
transferPlayer(player, { hostname: portal.host, port: portal.port });
|
||||
} catch (e) {
|
||||
player.sendMessage(`§cTransfer failed: ${e.message}`);
|
||||
}
|
||||
}
|
||||
}, 10); // Check every half second
|
||||
@@ -64,45 +65,6 @@ world.afterEvents.playerLeave.subscribe((event) => {
|
||||
spawnTicks.delete(event.playerId);
|
||||
});
|
||||
|
||||
// ─── Place signs above each portal on load ──────────────────────
|
||||
|
||||
function placePortalSigns() {
|
||||
const overworld = world.getDimension("overworld");
|
||||
const signs = [
|
||||
{ x: -15, y: 70, z: -23, name: "Jamie's", color: "§a" },
|
||||
{ x: 0, y: 70, z: -23, name: "Lyla's", color: "§d" },
|
||||
{ x: 15, y: 70, z: -23, name: "Mya's", color: "§b" },
|
||||
];
|
||||
|
||||
for (const sign of signs) {
|
||||
try {
|
||||
// Place wall sign facing south (toward players approaching from +Z)
|
||||
overworld.runCommand(`setblock ${sign.x} ${sign.y} ${sign.z} oak_wall_sign ["facing_direction":3]`);
|
||||
} catch (e) {
|
||||
// Non-fatal — chunks may not be loaded
|
||||
}
|
||||
}
|
||||
|
||||
// Delay to let sign blocks register, then write text
|
||||
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!");
|
||||
world.sendMessage("§6[Hub] §7Portal transfer system loaded! Place portal blocks to create portals.");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user