← BásicoObjeto 3Dfogon/portal
Portal mágico
Arrastrá para girarlo// «Portal mágico» · gpt-6-sol (medium)
// A magic portal: a tall rectangular frame of dark obsidian blocks standing on a small square of grass and dirt, with swirling purple energy filling the opening, a few small plants and a red mushroom around it.
// A patch of earth and grass.
box(4, 7, 0, 43, 40, 2, "#694329");
box(4, 7, 3, 43, 40, 3, "#67a345");
for (let x = 4; x <= 43; x++) {
for (let y = 7; y <= 40; y++) {
const n = rand();
if (n < 0.18) set(x, y, 3, "#78b552");
else if (n < 0.31) set(x, y, 3, "#5b9841");
else if (n > 0.96) set(x, y, 3, "#8bc15c");
if (y === 7 || y === 40 || x === 4 || x === 43) {
if (rand() < 0.29) set(x, y, 2, "#795035");
if (rand() < 0.20) set(x, y, 0, "#573924");
}
}
}
// The solid obsidian frame. Its front is at y=20; the portal is recessed.
box(12, 21, 4, 17, 27, 42, "#17121f");
box(31, 21, 4, 36, 27, 42, "#17121f");
box(18, 21, 4, 30, 27, 8, "#17121f");
box(18, 21, 37, 30, 27, 42, "#17121f");
// Broad, irregular stone faces and deeply recessed joints.
for (let z = 4; z <= 42; z += 5) {
for (const [a, b] of [[12, 17], [31, 36]]) {
const end = Math.min(z + 4, 42);
box(a, 20, z, b, 20, end, "#0f0d18");
box(a + 1, 20, z + 1, b - 1, 20, Math.min(z + 3, end), "#292039");
if (z + 2 <= end) {
box(a + 1, 20, z + 2, a + 2, 20, z + 2, "#332743");
set(b - 1, 20, z + 1, "#21192e");
}
}
}
for (const z0 of [4, 37]) {
const z1 = z0 === 4 ? 8 : 42;
box(18, 20, z0, 30, 20, z1, "#100e19");
for (let x = 18; x <= 30; x += 4) {
box(x, 20, z0 + 1, Math.min(x + 2, 30), 20, z1 - 1, "#292039");
set(x, 20, z0 + 2, "#352846");
}
}
// Dim violet glints along the inner edges, and a shaded top and side.
for (let z = 9; z <= 36; z++) {
if (z % 5 !== 0) {
set(17, 20, z, "#372245");
set(31, 20, z, "#281c38");
}
}
box(12, 21, 42, 36, 26, 42, "#30243f");
for (let x = 13; x <= 36; x += 4) {
box(x, 22, 42, Math.min(x + 2, 36), 25, 42, "#3a2b4a");
}
box(36, 22, 5, 36, 27, 41, "#211a2d");
// Swirling energy, set back inside the opening.
part("portal", [24, 23, 23]);
for (let z = 9; z <= 36; z++) {
const curl = 24 + 3.2 * Math.sin(z * 0.43 + 0.4)
+ 1.1 * Math.sin(z * 0.91);
for (let x = 18; x <= 30; x++) {
const d = x - curl;
let color = "#8429b7";
if (d > -2.7 && d < -0.4) color = "#501880";
if (d >= -0.4 && d < 0.8) color = "#662099";
if (d >= 1.3 && d < 3.0) color = "#bb42dd";
if (Math.abs(d - 3.1) < 0.65) color = "#d25aea";
if (rand() < 0.13) color = d > 0 ? "#a735cd" : "#71239f";
if (x === 18 || x === 30) color = "#76209f";
box(x, 22, z, x, 24, z, color);
if ((x + z) % 7 === 0 && d > 1) set(x, 21, z, "#b443df");
}
}
animate("portal", {
type: "colors",
palette: ["#4d167f", "#7120af", "#9425cf", "#be37e7", "#e16bfa", "#a82ddd"],
pattern: "noise",
glow: 0.82
});
part("body");
// Sparse grass clumps, kept clear of the portal.
for (const [x, y] of [
[9, 19], [10, 20], [11, 34], [17, 35], [20, 12],
[23, 37], [29, 11], [34, 34], [38, 27], [39, 18]
]) {
set(x, y, 4, "#43833b");
set(x, y, 5, "#569b42");
set(x - 1, y, 4, "#65a748");
if (rand() < 0.65) set(x, y, 6, "#73ad4e");
}
// Two little red mushrooms with pale stems and spotted caps.
for (const [x, y] of [[15, 13], [36, 15]]) {
box(x, y, 4, x, y, 5, "#e4d8b1");
box(x - 1, y - 1, 6, x + 1, y + 1, 6, "#b92232");
box(x, y, 7, x + 1, y, 7, "#dc3640");
set(x - 1, y - 1, 6, "#f4e8d9");
set(x + 1, y, 7, "#fff0dc");
set(x + 1, y + 1, 6, "#821b2b");
}
// Tiny floating sparks in front of and behind the frame.
part("moteA", [18, 20, 17]);
set(18, 19, 17, "#d765fa");
set(18, 19, 18, "#f0b0ff");
set(19, 19, 17, "#8724c1");
animate("moteA", { type: "bob", axis: "z", speed: 1.1, amplitude: 1 });
animate("moteA", {
type: "colors",
palette: ["#8724c1", "#d765fa", "#f0b0ff"],
pattern: "pulse",
glow: 0.8
});
part("moteB", [30, 21, 31]);
set(30, 19, 31, "#e891ff");
set(31, 19, 31, "#bd47eb");
set(30, 19, 32, "#9c32ce");
animate("moteB", { type: "bob", axis: "z", speed: 0.85, amplitude: 1.4 });
animate("moteB", {
type: "colors",
palette: ["#9c32ce", "#e891ff", "#bd47eb"],
pattern: "pulse",
glow: 0.8
});
part("moteC", [21, 30, 36]);
set(21, 30, 36, "#dd7df7");
set(22, 30, 36, "#ac3cda");
animate("moteC", { type: "bob", axis: "z", speed: 1.3, amplitude: 0.8 });
animate("moteC", {
type: "colors",
palette: ["#8a2ab8", "#dd7df7", "#ac3cda"],
pattern: "flicker",
glow: 0.7
});import { draw } from "./assets/voxel";
import { portal } from "./assets/portal";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, portal, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: lo que mide en px su lado más largo
// angle: giro sobre el piso, en radianes
// t: segundos; si se mueve, se anima solo con eso# En una terminal, en la carpeta del juego
fogon assets add portal
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["portal"] })
# Queda en client/assets/portal.ts
# https://fogon.page/object/portal