← FantasíaObjeto 3Dfogon/ruinas-magicas
Ruinas mágicas
Arrastrá para girarlo// «Ruinas mágicas» · gpt-6-sol (medium)
// Ancient magic ruins: a broken circle of mossy grey stone pillars of different heights, one fallen pillar lying on the ground, a cracked stone floor with glowing violet runes in the center, and ivy climbing the columns.
const stone = ["#777f7b", "#858d88", "#969e97", "#6b746f"];
const darkStone = "#535d59";
const moss = ["#537852", "#668d59", "#83a66a"];
const glow = ["#a95cff", "#ed65df", "#58e9e3"];
const cx = 24, cy = 24;
function disk(x, y, z, r, color) {
for (let dx = -Math.ceil(r); dx <= r; dx++)
for (let dy = -Math.ceil(r); dy <= r; dy++)
if (dx * dx + dy * dy <= r * r) set(x + dx, y + dy, z, color);
}
// An uneven, tiled foundation.
for (let x = 4; x <= 44; x++) {
for (let y = 4; y <= 44; y++) {
const d = Math.hypot(x - cx, y - cy);
if (d > 19.5 || (d > 18.1 && rand() < 0.3)) continue;
box(x, y, 0, x, y, 1, d > 17.5 ? darkStone : stone[0]);
const seam = (x + Math.floor(y / 7) % 2) % 7 === 0 || y % 7 === 0;
set(x, y, 2, seam ? "#555f5c" : stone[rand() < 0.16 ? 2 : rand() < 0.3 ? 0 : 1]);
if (d > 9 && rand() < 0.075) set(x, y, 2, moss[Math.floor(rand() * moss.length)]);
}
}
// Short surviving pieces of the circular outer wall.
for (let a = 0; a < Math.PI * 2; a += 0.065) {
if ((a > 0.18 && a < 0.55) || (a > 2.5 && a < 3.1) ||
(a > 4.1 && a < 5.5)) continue;
const x = Math.round(cx + 16 * Math.cos(a));
const y = Math.round(cy + 16 * Math.sin(a));
set(x, y, 3, rand() < 0.2 ? moss[0] : stone[0]);
if (rand() < 0.5) set(x, y, 4, stone[1]);
}
// Raised, dark altar beneath the luminous inscription.
disk(cx, cy, 3, 9, "#59615f");
disk(cx, cy, 4, 8, "#464c55");
for (let a = 0; a < Math.PI * 2; a += 0.045) {
const x = Math.round(cx + 8.5 * Math.cos(a));
const y = Math.round(cy + 8.5 * Math.sin(a));
set(x, y, 4, a % 0.45 < 0.09 ? "#a6a49b" : "#77817e");
}
// Cracks radiating into the old flagstones.
const cracks = [
[[16, 19], [13, 18], [11, 16]],
[[30, 18], [33, 16], [36, 17]],
[[17, 29], [14, 31], [12, 33]],
[[29, 32], [32, 34], [34, 37]],
[[23, 34], [22, 37], [20, 39]]
];
for (const chain of cracks) {
for (let i = 1; i < chain.length; i++) {
const [x0, y0] = chain[i - 1], [x1, y1] = chain[i];
line(x0, y0, 2, x1, y1, 2, 0, "#424b4b");
}
}
function mushroom(x, y, cap) {
box(x, y, 3, x, y, 4, "#dfd2af");
box(x - 1, y, 5, x + 1, y, 5, cap);
box(x, y - 1, 5, x, y + 1, 5, cap);
set(x, y, 6, "#f4adb8");
}
function pillar(x, y, top, seed) {
// Broad foot, rounded-looking square shaft, and inset front face.
box(x - 3, y - 3, 3, x + 3, y + 3, 5, darkStone);
box(x - 3, y - 3, 5, x + 3, y + 3, 6, stone[2]);
box(x - 2, y - 2, 7, x + 2, y + 2, top - 2, stone[0]);
box(x - 2, y - 2, 7, x + 1, y - 2, top - 2, stone[1]);
box(x - 2, y - 2, 7, x - 2, y + 2, top - 2, stone[2]);
box(x + 2, y - 1, 7, x + 2, y + 2, top - 2, stone[3]);
for (let z = 10; z < top - 2; z += 6) {
box(x - 2, y - 2, z, x + 2, y - 2, z, "#65706b");
set(x + (z % 2 ? 1 : -1), y - 2, z, "#a1a79d");
}
// Deliberately irregular broken ends.
box(x - 2, y - 2, top - 1, x + 1, y + 2, top - 1, stone[1]);
box(x - 2, y, top, x, y + 2, top, stone[2]);
set(x + 1, y + 1, top, stone[0]);
if (seed % 3 === 0) {
box(x - 3, y - 3, top - 3, x + 3, y + 3, top - 2, stone[2]);
box(x - 3, y + 2, top - 3, x + 3, y + 3, top - 3, stone[3]);
}
// Ivy follows one face in a winding, leafy strand.
const ivyTop = Math.min(top - 3, 12 + seed * 2);
for (let z = 6; z <= ivyTop; z++) {
const ix = x - 1 + Math.round(Math.sin(z * 0.74 + seed) * 1.4);
set(ix, y - 3, z, z % 4 === 0 ? "#756b48" : moss[0]);
if (z % 3 === 0) {
box(ix - 1, y - 3, z, ix, y - 3, z + 1, moss[1]);
set(ix + 1, y - 3, z + 1, moss[2]);
}
}
for (let i = 0; i < 10; i++) {
const mx = x + Math.floor(rand() * 6) - 3;
const my = y + Math.floor(rand() * 6) - 3;
set(mx, my, rand() < 0.5 ? 6 : top, moss[Math.floor(rand() * 3)]);
}
}
[
[10, 13, 25], [8, 24, 30], [12, 35, 22],
[24, 39, 36], [36, 35, 27], [40, 24, 32],
[38, 13, 19]
].forEach(([x, y, h], i) => pillar(x, y, h, i + 1));
// A toppled column across the near side of the courtyard.
cylinder(15, 9, 5, 3, 16, "#78827c", "x");
cylinder(15, 9, 5, 2, 16, "#929a91", "x");
for (const x of [16, 21, 27]) {
box(x, 6, 3, x + 1, 12, 7, "#646e69");
box(x, 7, 8, x + 1, 11, 8, "#9da59a");
}
box(14, 7, 3, 14, 11, 7, "#555e5c");
box(30, 8, 3, 31, 10, 6, "#636d68");
for (let i = 0; i < 18; i++) {
const x = 15 + Math.floor(rand() * 16);
const y = 7 + Math.floor(rand() * 5);
set(x, y, 8, moss[Math.floor(rand() * 3)]);
}
mushroom(7, 16, "#cd648c");
mushroom(35, 10, "#bd70d5");
mushroom(39, 30, "#d57893");
mushroom(13, 39, "#b967bb");
// Only the magic inscription changes color.
part("runes", [24, 24, 5]);
for (const radius of [7.1, 5.1]) {
for (let a = 0; a < Math.PI * 2; a += 0.018) {
if (radius < 6 && Math.floor(a * 8) % 17 === 0) continue;
set(Math.round(cx + radius * Math.cos(a)),
Math.round(cy + radius * Math.sin(a)), 5,
glow[Math.floor(a * 3) % 3]);
}
}
for (let i = 0; i < 8; i++) {
const a = i * Math.PI / 4;
const x0 = Math.round(cx + 5.6 * Math.cos(a));
const y0 = Math.round(cy + 5.6 * Math.sin(a));
const x1 = Math.round(cx + 6.7 * Math.cos(a));
const y1 = Math.round(cy + 6.7 * Math.sin(a));
line(x0, y0, 5, x1, y1, 5, 0, glow[i % 3]);
const px = Math.round(cx + 6.2 * Math.cos(a + 0.17));
const py = Math.round(cy + 6.2 * Math.sin(a + 0.17));
set(px, py, 5, glow[(i + 1) % 3]);
}
// Large, legible four-pointed sigil.
for (let d = -3; d <= 3; d++) {
set(cx + d, cy, 5, glow[1]);
set(cx, cy + d, 5, glow[0]);
}
for (const [dx, dy] of [[-2,-2], [-2,2], [2,-2], [2,2]])
set(cx + dx, cy + dy, 5, glow[2]);
set(cx, cy, 6, "#f6b8ff");
animate("runes", {
type: "colors",
palette: ["#994dff", "#f05adf", "#57e9ee"],
pattern: "wave",
glow: 0.8
});import { draw } from "./assets/voxel";
import { ruinasMagicas } from "./assets/ruinas-magicas";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, ruinasMagicas, 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 ruinas-magicas
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["ruinas-magicas"] })
# Queda en client/assets/ruinas-magicas.ts
# https://fogon.page/object/ruinas-magicas