← FantasíaObjeto 3Dfogon/espada-en-la-piedra
Espada en la piedra
Arrastrá para girarlo// «Espada en la piedra» · gpt-6-sol (medium)
// A legendary sword stuck point-down in a big mossy grey boulder: a long shining silver blade, a gold crossguard with a blue gem, a leather-wrapped grip and a round gold pommel, with a few flowers and grass at the foot of the rock.
const stone = ["#626773", "#707680", "#7c828a", "#90959a"];
const moss = ["#355e47", "#477655", "#629263", "#87aa70"];
const gold = "#d99838";
const goldLight = "#ffda72";
const silver = "#cbdce2";
function rockTop(x, y) {
for (let z = 20; z >= 0; z--) if (get(x, y, z)) return z;
return -1;
}
function rockFront(x, z) {
for (let y = 5; y <= 42; y++) if (get(x, y, z)) return y;
return -1;
}
// A broad, irregular boulder with a solid, grounded base.
for (let x = 4; x <= 44; x++) {
for (let y = 6; y <= 42; y++) {
for (let z = 0; z <= 18; z++) {
const dx = x - 24, dy = y - 24;
const rough = 0.045 * Math.sin(x * 1.7 + y * 0.8) +
0.035 * Math.sin(y * 2.1 - z * 1.3);
const crown = dx*dx/361 + dy*dy/256 + (z-7)*(z-7)/110 < 1 + rough;
const foot = dx*dx/324 + dy*dy/225 + (z-3)*(z-3)/25 < 1;
if (crown || foot) {
let c = stone[z > 12 ? 3 : z > 6 ? 2 : 1];
if (rand() < 0.21) c = stone[Math.floor(rand() * stone.length)];
if (y > 30 && rand() < 0.22) c = stone[0];
set(x, y, z, c);
}
}
}
}
// Moss follows the top of the rock in broad, broken patches.
const mossBlocks = [];
for (let x = 6; x <= 42; x++) for (let y = 8; y <= 40; y++) {
const z = rockTop(x, y);
if (z < 5 || z > 18) continue;
const patch = Math.sin(x * 0.48 + y * 0.19) +
Math.cos(y * 0.53 - x * 0.12);
if (patch > 0.25 && rand() < 0.78)
mossBlocks.push([x, y, z + 1, moss[Math.floor(rand() * moss.length)]]);
}
for (const b of mossBlocks) set(...b);
// A few mossy drips on the near face.
for (let x = 9; x <= 39; x++) for (let z = 3; z <= 12; z++) {
const y = rockFront(x, z);
if (y < 0) continue;
const patch = Math.sin(x * 0.49 + z * 0.21) + Math.cos(x * 0.17 - z * 0.81);
if (patch > 1.12 && rand() < 0.65)
set(x, y - 1, z, moss[Math.floor(rand() * moss.length)]);
}
// A pair of faint magical fissures on the face of the stone.
for (const [x, z] of [[18,3],[18,4],[19,5],[19,6],[20,7],
[31,3],[30,4],[30,5],[29,6]]) {
const y = rockFront(x, z);
if (y >= 0) set(x, y - 1, z, "#46c9bb");
}
// The blade tapers to a point within the rock. Its broad face looks forward.
for (let z = 14; z <= 34; z++) {
const half = z < 17 ? z - 14 : z < 20 ? 3 : 4;
box(24-half, 22, z, 24+half, 25, z, silver);
for (let x = 24-half; x <= 24+half; x++) {
const edge = Math.abs(x - 24) === half;
set(x, 22, z, edge ? "#f3faff" : x < 24 ? "#a7beca" : "#dceaf0");
if (z >= 20 && !edge) set(x, 25, z, "#8ea6b0");
}
// Raised, bright central ridge and a slender enchanted fuller.
if (z >= 19) {
set(24, 21, z, "#f8ffff");
if (z >= 22 && z <= 31) set(23, 21, z, "#7ce9eb");
}
}
line(20, 22, 20, 20, 22, 34, 0, "#f7ffff");
line(28, 22, 20, 28, 22, 34, 0, "#e4f9ff");
// Curving, chunky golden crossguard with raised ends.
box(15, 21, 34, 33, 26, 36, gold);
box(17, 20, 36, 31, 25, 36, goldLight);
box(19, 20, 33, 29, 26, 35, "#b97b2b");
box(20, 19, 35, 28, 20, 36, "#ffd267");
for (const x of [14, 34]) {
sphere(x, 23, 36, 2.4, gold);
box(x-1, 21, 37, x+1, 24, 38, goldLight);
set(x, 20, 36, "#fff0a2");
}
line(16, 20, 36, 21, 19, 36, 0, "#fff0a2");
line(27, 19, 36, 32, 20, 36, 0, "#fff0a2");
// Large blue jewel in the center of the guard.
sphere(24, 19, 35, 2.3, "#175397");
sphere(24, 18, 35, 1.5, "#267ed3");
set(23, 17, 36, "#b8ffff");
set(24, 17, 35, "#5cebf3");
set(25, 18, 34, "#102e78");
// Dark leather grip, with diagonal honey-brown wrapping.
box(22, 22, 38, 26, 25, 43, "#49352f");
box(22, 21, 38, 26, 21, 43, "#68483a");
for (let z = 38; z <= 43; z++) {
for (let x = 22; x <= 26; x++) {
if ((x + z * 2) % 4 < 2)
set(x, 20, z, "#a3744a");
}
}
box(22, 21, 38, 26, 25, 38, gold);
box(22, 21, 43, 26, 25, 43, goldLight);
// Oversized round pommel.
sphere(24, 23, 45, 2.3, gold);
sphere(24, 22, 45, 1.4, goldLight);
set(23, 20, 46, "#fff0aa");
// Grass, flowers and tiny friendly mushrooms around the stone.
function grass(x, y, height) {
const z = Math.max(0, rockTop(x, y) + 1);
for (let i = 0; i < height; i++)
set(x, y, z+i, i === height-1 ? "#91bc71" : "#39744d");
set(x-1, y, z+1, "#578e58");
set(x+1, y, z, "#447e50");
}
for (const [x,y,h] of [
[9,13,3],[13,10,4],[18,8,3],[29,8,3],[35,10,4],[39,15,3],
[8,27,3],[38,31,3],[13,35,3],[33,37,3]
]) grass(x,y,h);
function flower(x, y, petal) {
const z = Math.max(0, rockTop(x, y) + 1);
box(x, y, z, x, y, z+3, "#508d58");
set(x-1, y, z+1, "#76a969");
set(x+1, y, z+2, "#76a969");
for (const [dx,dy,dz] of [[-1,0,0],[1,0,0],[0,-1,0],[0,1,0],[0,0,1]])
set(x+dx, y+dy, z+4+dz, petal);
set(x, y-1, z+4, "#ffdd69");
}
flower(13, 12, "#ee8fc0");
flower(36, 13, "#a995eb");
flower(11, 30, "#f4d6ec");
flower(36, 32, "#f0a3cf");
function mushroom(x, y) {
const z = rockTop(x, y) + 1;
if (z < 1) return;
box(x, y, z, x, y, z+2, "#f2dfbf");
sphere(x, y, z+3, 2, "#b75d78");
set(x-1, y-2, z+3, "#ffe7b3");
set(x+1, y-1, z+4, "#ffe7b3");
}
mushroom(14, 18);
mushroom(34, 19);import { draw } from "./assets/voxel";
import { espadaEnLaPiedra } from "./assets/espada-en-la-piedra";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, espadaEnLaPiedra, 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 espada-en-la-piedra
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["espada-en-la-piedra"] })
# Queda en client/assets/espada-en-la-piedra.ts
# https://fogon.page/object/espada-en-la-piedra