← NaturalezaObjeto 3Dfogon/roca-grande
Peñasco
Arrastrá para girarlo// «Peñasco» · gpt-6-sol (medium)
// A large rocky outcrop: several big angular grey boulders piled together, the tallest in the back, with moss on the upper surfaces and small pebbles around the base. Uses most of the grid.
// A pile of broad, faceted boulders. The tallest stone rises at the back.
const stones = [
{ x: 24, y: 33, z: 19, rx: 12, ry: 10, rz: 20, seed: 1 },
{ x: 11, y: 31, z: 13, rx: 9, ry: 10, rz: 14, seed: 2 },
{ x: 38, y: 32, z: 12, rx: 8, ry: 9, rz: 13, seed: 3 },
{ x: 23, y: 23, z: 10, rx: 13, ry: 10, rz: 11, seed: 4 },
{ x: 12, y: 16, z: 9, rx: 9, ry: 9, rz: 10, seed: 5 },
{ x: 36, y: 18, z: 10, rx: 9, ry: 9, rz: 11, seed: 6 },
{ x: 25, y: 12, z: 7, rx: 9, ry: 8, rz: 8, seed: 7 }
];
const grey = ["#818c8b", "#959f9e", "#aab3b1", "#bec6c2", "#d0d4cc"];
for (const b of stones) {
for (let z = Math.max(0, b.z - b.rz); z <= b.z + b.rz; z++) {
for (let y = b.y - b.ry - 1; y <= b.y + b.ry + 1; y++) {
for (let x = b.x - b.rx - 1; x <= b.x + b.rx + 1; x++) {
const u = (x - b.x) / b.rx;
const v = (y - b.y) / b.ry;
const w = (z - b.z) / b.rz;
const a = Math.abs(u), d = Math.abs(v), c = Math.abs(w);
// Intersect several broad planes to give each rock angular, cut faces.
if (a + .35*d + .27*c > 1.04 ||
.35*a + d + .28*c > 1.05 ||
.42*a + .40*d + c > 1.06 ||
.49*Math.abs(u+v) + .29*c > 1.01 ||
.49*Math.abs(u-v) + .26*c > 1.04) continue;
let shade = 1;
if (w > .12) shade++;
if (w > .62) shade++;
if (u < -.30 && v < .25) shade++;
if (v > .35 || w < -.40) shade--;
if (rand() < .075) shade += rand() < .5 ? -1 : 1;
shade = Math.max(0, Math.min(4, shade));
set(x, y, z, grey[shade]);
}
}
}
}
// Moss gathers in irregular, connected patches on the sunlit upper faces.
for (let x = 2; x <= 45; x++) {
for (let y = 3; y <= 44; y++) {
for (let z = 5; z <= 40; z++) {
if (!get(x, y, z) || get(x, y, z + 1)) continue;
const patch =
.65 * Math.sin(x*.43 + y*.13) +
.48 * Math.cos(y*.53 - x*.18) +
.29 * Math.sin(x*.93 - y*.71);
if (patch < .57) continue;
const moss = rand();
set(x, y, z, moss < .15 ? "#48884c" :
moss < .55 ? "#62a959" :
moss < .88 ? "#79be61" : "#98d174");
// A few little moss fringes spill down exposed edges.
if (z > 6 && !get(x, y - 1, z) && rand() < .25) {
set(x, y, z - 1, rand() < .5 ? "#518f4d" : "#69ae59");
}
}
}
}
// Short, broken seams on the visible front faces.
const cracks = [
[[10, 13], [11, 12], [11, 11], [12, 10], [12, 9]],
[[19, 13], [20, 12], [20, 11], [21, 10]],
[[35, 15], [35, 14], [36, 13], [36, 12], [37, 11]]
];
for (const crack of cracks) {
for (const [x, z] of crack) {
for (let y = 3; y <= 40; y++) {
const color = get(x, y, z);
if (color) {
if (color[1] !== "4" && color[1] !== "6" && color[1] !== "7") {
set(x, y, z, "#788482");
}
break;
}
}
}
}
// Loose, rounded chips around the foot of the outcrop.
const pebbleColors = ["#a1aaa6", "#bac2b9", "#929d99", "#c9cdc2"];
for (let i = 0; i < 90; i++) {
const x = 3 + Math.floor(rand() * 42);
const y = 3 + Math.floor(rand() * 42);
if (get(x, y, 0) || get(x, y, 1)) continue;
// Keep chips near the pile rather than scattering them across empty corners.
if (Math.abs(x - 24) + Math.abs(y - 24) > 34) continue;
const color = pebbleColors[Math.floor(rand() * pebbleColors.length)];
set(x, y, 0, color);
if (rand() < .45) set(x + 1, y, 0, color);
if (rand() < .16) set(x, y, 1, "#d0d3c9");
}import { draw } from "./assets/voxel";
import { rocaGrande } from "./assets/roca-grande";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, rocaGrande, 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 roca-grande
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["roca-grande"] })
# Queda en client/assets/roca-grande.ts
# https://fogon.page/object/roca-grande