← Fantasy3D assetfogon/torre-elfica
Torre élfica
Drag to turn it// «Torre élfica» · gpt-6-sol (medium)
// An elegant slender elven tower: smooth white stone with graceful curves, tall narrow arched windows glowing soft teal, gold and leaf-green ornaments spiraling up, a pointed silver-green roof with a tiny gold spire, and climbing vines with white flowers around the base.
const cx = 24, cy = 24;
const stone = ["#c6d3cb", "#dce7de", "#eef4e9", "#f8faf0"];
const gold = "#d8b866";
const darkGold = "#a98748";
const leaf = ["#548c69", "#75a981", "#a1c590"];
const teal = ["#164c55", "#247e86", "#55d5ce", "#a5fff0"];
function disc(z, r, color) {
for (let x = Math.floor(cx - r); x <= Math.ceil(cx + r); x++)
for (let y = Math.floor(cy - r); y <= Math.ceil(cy + r); y++)
if ((x - cx) ** 2 + (y - cy) ** 2 <= r * r)
set(x, y, z, color);
}
// Rounded, moss-touched foundation.
disc(0, 10.8, "#77877d");
disc(1, 10.3, "#98a99b");
disc(2, 9.4, "#c0ccc0");
disc(3, 8.7, "#d9e3d7");
for (let z = 0; z <= 3; z++) {
let r = [10.8, 10.3, 9.4, 8.7][z];
for (let a = 0; a < Math.PI * 2; a += 0.095) {
let x = Math.round(cx + r * Math.cos(a));
let y = Math.round(cy + r * Math.sin(a));
if (rand() < 0.31) set(x, y, z, z < 2 ? "#728e79" : "#aabca9");
}
}
// Slightly tapered, softly curved solid tower.
function wallRadius(z) {
return 7.65 + 0.55 * Math.sin((z - 3) * Math.PI / 27)
+ (z > 26 ? (z - 26) * 0.10 : 0);
}
for (let z = 4; z <= 31; z++) {
const r = wallRadius(z);
for (let x = 15; x <= 33; x++)
for (let y = 15; y <= 33; y++) {
let d = Math.hypot(x - cx, y - cy);
if (d > r) continue;
let c = d < r - 1.5 ? "#dce7de" :
(y < cy - 2 || z > 26) ? stone[2] : stone[1];
if (d > r - 1.4 && rand() < 0.075) c = stone[rand() < 0.6 ? 0 : 3];
set(x, y, z, c);
}
}
// Fine horizontal courses and a flared, crowned cornice.
for (let z of [5, 19, 30]) {
let r = wallRadius(z) + 0.25;
for (let a = 0; a < Math.PI * 2; a += 0.035)
set(cx + r * Math.cos(a), cy + r * Math.sin(a), z,
z === 30 ? "#e9efdf" : "#d1dbc9");
}
disc(32, 9.2, "#edf2e5");
disc(33, 10.1, "#e5eee0");
for (let a = 0; a < Math.PI * 2; a += 0.025) {
set(cx + 9.9 * Math.cos(a), cy + 9.9 * Math.sin(a), 33, gold);
set(cx + 9.4 * Math.cos(a), cy + 9.4 * Math.sin(a), 32, darkGold);
}
// Slender fluted columns, framing the front and flanking the back.
for (let x of [19, 29]) for (let y of [18, 30]) {
cylinder(x, y, 4, 1.25, 27, "#eaf2e7");
box(x - 1, y - 1, 5, x + 1, y + 1, 5, "#d5dfd3");
box(x - 1, y - 1, 29, x + 1, y + 1, 30, "#f5f6e9");
for (let z = 7; z <= 28; z++)
set(x, y < 24 ? y - 1 : y + 1, z, z % 8 === 0 ? gold : "#f9faf0");
set(x, y < 24 ? y - 2 : y + 2, 30, gold);
}
// Tall, pointed arched windows: dark recess, gold tracery, teal glow.
function windowWidth(z, bottom, top) {
if (z < bottom || z > top) return -1;
if (z === top) return 0;
if (z === top - 1) return 1;
return 2;
}
function frontWindow(bottom, top, back = false) {
for (let z = bottom - 1; z <= top + 1; z++) {
let w = windowWidth(z, bottom, top);
let outer = w < 0 ? (z === bottom - 1 ? 2 : 0) : w + 1;
for (let dx = -outer; dx <= outer; dx++) {
let frame = w < 0 || Math.abs(dx) > w || z === bottom;
let y0 = back ? 31 : 15;
for (let depth = 0; depth <= 3; depth++) {
let y = back ? y0 + depth : y0 + depth;
set(cx + dx, y, z, frame
? (depth === 0 ? gold : "#aebbaf")
: (depth < 2 ? "#315b61" : teal[2]));
}
if (!frame) set(cx + dx, back ? 34 : 14, z,
Math.abs(dx) === 2 ? teal[1] : teal[z % 3 === 0 ? 3 : 2]);
}
}
// A diamond-shaped gold clasp between the two glass lancets.
for (let z of [bottom + 3, bottom + 4])
set(cx, back ? 34 : 14, z, "#f5d98b");
}
frontWindow(9, 17);
frontWindow(22, 29);
frontWindow(10, 17, true);
frontWindow(22, 29, true);
// Side-facing stained glass, narrower than the main windows.
for (let side of [-1, 1]) for (let [lo, hi] of [[11, 17], [23, 29]]) {
for (let z = lo - 1; z <= hi; z++) {
let w = z === hi ? 0 : (z === hi - 1 ? 1 : 2);
for (let dy = -w - 1; dy <= w + 1; dy++) {
let frame = Math.abs(dy) > w || z < lo;
for (let depth = 0; depth < 3; depth++)
set(cx + side * (7 + depth), cy + dy, z,
frame ? (depth === 2 ? gold : "#c2d5c6") :
(depth === 2 ? teal[2] : "#2b6c72"));
if (!frame)
set(cx + side * 10, cy + dy, z,
Math.abs(dy) === 2 ? teal[1] : teal[z % 3 === 0 ? 3 : 2]);
}
}
}
// A little golden sunburst between the two front windows.
set(24, 14, 19, "#fff0aa");
for (let [dx, dz] of [[-2, 0], [2, 0], [0, -2], [0, 2],
[-1, -1], [1, -1], [-1, 1], [1, 1]])
set(24 + dx, 15, 19 + dz, gold);
// Silver-green pointed roof, with individual highlighted shingles.
for (let z = 34; z <= 43; z++) {
let r = 10.1 - (z - 34) * 0.94;
for (let x = Math.floor(cx - r); x <= Math.ceil(cx + r); x++)
for (let y = Math.floor(cy - r); y <= Math.ceil(cy + r); y++) {
if (Math.hypot(x - cx, y - cy) > r) continue;
let c = (x + 2 * y + z) % 7 === 0 ? "#c1d3c1" :
y < cy ? "#a9c2ad" : "#859f96";
if (rand() < 0.08) c = "#d3ddc8";
set(x, y, z, c);
}
for (let i = 0; i < 8; i++) {
let a = i * Math.PI / 4;
set(cx + r * Math.cos(a), cy + r * Math.sin(a), z,
i % 2 ? "#d6e1ce" : "#718f82");
}
}
for (let a = 0; a < Math.PI * 2; a += 0.065)
set(cx + 10.1 * Math.cos(a), cy + 10.1 * Math.sin(a), 34, gold);
box(24, 24, 43, 24, 24, 45, gold);
set(24, 24, 46, "#fff1ab");
for (let a = 0; a < Math.PI * 2; a += Math.PI / 2)
set(cx + 1.5 * Math.cos(a), cy + 1.5 * Math.sin(a), 43, "#e8d28a");
// Gold and leaf-green tendrils winding around the stonework.
for (let i = 0; i <= 470; i++) {
let t = i / 470;
let z = 5 + t * 25;
let a = 1.05 + t * Math.PI * 4.2;
let r = wallRadius(z) + 0.85;
let x = cx + r * Math.cos(a), y = cy + r * Math.sin(a);
set(x, y, z, i % 17 < 5 ? "#7ca87b" : gold);
if (i % 39 === 0) {
let leafA = a + 0.4;
ellipsoid(cx + (r + 1.3) * Math.cos(leafA),
cy + (r + 1.3) * Math.sin(leafA), z + 1,
1.15, 1.15, 1.55, leaf[(i / 39) % 3 | 0]);
}
}
// Climbing ivy at the foot, with broad leaves and small white blossoms.
for (let k = 0; k < 7; k++) {
let a0 = k * Math.PI * 2 / 7 + 0.19;
let height = 5 + (k % 3) * 2;
let px = cx + 9.3 * Math.cos(a0), py = cy + 9.3 * Math.sin(a0);
for (let j = 0; j <= height * 7; j++) {
let z = j / 7;
let a = a0 + z * 0.115;
let r = 9.4 - z * 0.26;
let x = cx + r * Math.cos(a), y = cy + r * Math.sin(a);
line(px, py, Math.max(0, z - 0.15), x, y, z, 0, "#477c5e");
px = x; py = y;
if (j > 6 && j % 12 === 0) {
let outward = a + (k % 2 ? 0.35 : -0.35);
ellipsoid(cx + (r + 1.1) * Math.cos(outward),
cy + (r + 1.1) * Math.sin(outward), z,
1.2, 0.9, 0.7, leaf[(k + j) % 3]);
}
}
let fa = a0 + height * 0.115;
let fx = Math.round(cx + (8.6 - height * 0.13) * Math.cos(fa));
let fy = Math.round(cy + (8.6 - height * 0.13) * Math.sin(fa));
let fz = height;
for (let [dx, dy, dz] of [[-1, 0, 0], [1, 0, 0], [0, -1, 0],
[0, 1, 0], [0, 0, 1]])
set(fx + dx, fy + dy, fz + dz, "#fffaf0");
set(fx, fy, fz, "#f4d979");
}
// A few tiny mossy stones and flowers nestled against the foundation.
for (let i = 0; i < 24; i++) {
let a = rand() * Math.PI * 2, r = 10 + rand() * 1.1;
let x = Math.round(cx + r * Math.cos(a));
let y = Math.round(cy + r * Math.sin(a));
set(x, y, 0, rand() < 0.55 ? "#719679" : "#a8b6a5");
if (rand() < 0.3) {
set(x, y, 1, "#fff9eb");
set(x, y, 2, "#f2d386");
}
}import { draw } from "./assets/voxel";
import { torreElfica } from "./assets/torre-elfica";
// Every frame: standing on (x, y), the centre of its base.
draw(ctx, torreElfica, x, y, { size: 64, angle: 0, t: performance.now() / 1000 });
// size: its longest side, in px
// angle: turn on the floor, in radians
// t: seconds; if it moves, that animates it# In a terminal, in the game's folder
fogon assets add torre-elfica
# With Fogón's tools (connector)
add_assets({ slug: "<your-game>", ids: ["torre-elfica"] })
# It lands in client/assets/torre-elfica.ts
# https://fogon.page/object/torre-elfica