← MedievalObjeto 3Dfogon/tienda-de-caballeros
Tienda de caballeros
Arrastrá para girarlo// «Tienda de caballeros» · gpt-6-sol (medium)
// A round medieval knights' tournament tent: a cylindrical cloth wall with vertical stripes in deep red and cream, a conical striped roof with a scalloped hanging trim in gold, a tall pole on top with a blue pennant, and an open triangular front flap tied back showing a dark interior. A heraldic shield (blue with a gold lion) hangs beside the entrance.
const C = { x: 24, y: 24 };
const red = ["#80242e", "#982f37", "#ab3d42"];
const cream = ["#d9c69e", "#ead9b3", "#f3e5c3"];
const gold = "#d5a94d";
const goldLight = "#f0cf73";
const goldDark = "#9d6d30";
function stripe(x, y) {
let a = Math.atan2(y - C.y, x - C.x);
return Math.floor((a + Math.PI) / (2 * Math.PI) * 16) % 2;
}
function cloth(x, y, z, roof = false) {
const pale = stripe(x, y);
const n = rand();
if (pale) return n < 0.12 ? cream[0] : n > 0.85 || roof && z > 27 ? cream[2] : cream[1];
return n < 0.13 ? red[0] : n > 0.86 || roof && z > 27 ? red[2] : red[1];
}
function frontY(x) {
const dx = x - C.x;
return Math.round(C.y - Math.sqrt(Math.max(0, 16.5 * 16.5 - dx * dx)));
}
// A low, woven floor.
for (let x = 6; x <= 42; x++) for (let y = 6; y <= 42; y++) {
const d = Math.hypot(x - C.x, y - C.y);
if (d <= 16.7) {
set(x, y, 0, d > 15.5 ? "#64432d" :
(x + Math.floor(y / 3)) % 5 === 0 ? "#a88759" : "#98784c");
}
}
// Cylindrical canvas wall. The widening opening makes a pointed doorway.
for (let z = 1; z <= 19; z++) {
for (let x = 6; x <= 42; x++) for (let y = 6; y <= 42; y++) {
const d = Math.hypot(x - C.x, y - C.y);
if (d < 14.5 || d > 16.7) continue;
const opening = y < C.y &&
Math.abs(x - C.x) < 6.7 * (1 - z / 19.5);
if (opening) continue;
set(x, y, z, cloth(x, y, z));
}
}
// A recessed, shadowy interior, visible through the doorway.
for (let z = 1; z <= 17; z++) {
const half = Math.max(0, Math.floor(6.2 * (1 - z / 19)));
for (let x = C.x - half; x <= C.x + half; x++) {
for (let y = 12; y <= 13; y++)
set(x, y, z, (x + z) % 7 === 0 ? "#342a26" : "#211c20");
}
}
box(21, 12, 1, 21, 12, 10, "#4b3429");
box(27, 12, 1, 27, 12, 10, "#4b3429");
// Folded-back flaps follow the sloping edges of the open canvas.
for (let z = 2; z <= 17; z++) {
const w = 6.7 * (1 - z / 19.5);
for (const side of [-1, 1]) {
const edgeX = Math.round(C.x + side * w);
const y = frontY(edgeX) - 1;
set(edgeX, y, z, z % 4 === 0 ? "#c04b45" : "#a83239");
set(edgeX + side, y, z, "#76242d");
if (z < 12 && z > 4)
set(edgeX - side, y - 1, z,
z % 3 === 0 ? "#e8d5ad" : "#d4bc92");
}
}
// Ropes gathering the flaps at the waist.
for (const side of [-1, 1]) {
const x = 24 + side * 4;
line(x, frontY(x) - 3, 9, x + side * 3, frontY(x + side * 3) - 2, 11, 0, goldLight);
box(x + side, frontY(x) - 4, 9, x + side, frontY(x) - 3, 10, goldDark);
}
// Conical roof, with the stripes continuing up from the walls.
for (let z = 20; z <= 35; z++) {
const radius = 18.7 * (35.5 - z) / 15.5;
for (let x = 5; x <= 43; x++) for (let y = 5; y <= 43; y++) {
const d = Math.hypot(x - C.x, y - C.y);
if (d <= radius && d >= Math.max(0, radius - 2.1))
set(x, y, z, cloth(x, y, z, true));
}
}
// Overhanging, scalloped gold valance.
for (let x = 5; x <= 43; x++) for (let y = 5; y <= 43; y++) {
const d = Math.hypot(x - C.x, y - C.y);
if (d < 17.3 || d > 19.1) continue;
const angle = Math.atan2(y - C.y, x - C.x);
const scallop = Math.cos(angle * 24);
const bottom = scallop > 0.35 ? 16 : scallop > -0.5 ? 17 : 18;
for (let z = bottom; z <= 20; z++)
set(x, y, z, z === bottom ? goldDark :
z === 19 || z === 20 ? goldLight : gold);
}
// Roof peak and tall, iron-bound wooden flagpole.
cylinder(24, 24, 34, 2, 3, goldDark);
box(24, 24, 36, 24, 24, 44, "#513323");
for (const z of [36, 40, 43]) set(24, 23, z, "#29292c");
set(24, 24, 45, goldLight);
// A shield hanging just to the right of the entrance.
line(34, 12, 18, 34, 9, 18, 0, "#3d2923");
for (let z = 7; z <= 18; z++) {
const half = z >= 12 ? 3 : z >= 9 ? 2 : z === 8 ? 1 : 0;
for (let x = 34 - half; x <= 34 + half; x++) {
set(x, 10, z, "#392a28");
set(x, 9, z, "#214c88");
if (x === 34 - half || x === 34 + half || z === 18 || z === 7)
set(x, 8, z, z === 18 ? goldLight : gold);
else set(x, 8, z, "#245899");
}
}
// Chunky, rearing gold lion: head, mane, body, paws and curling tail.
for (const [x, z] of [
[33, 16], [34, 16], [32, 15], [33, 15], [34, 15],
[33, 14], [34, 14], [35, 14], [34, 13], [35, 13],
[34, 12], [35, 12], [33, 11], [35, 11],
[36, 14], [37, 15], [36, 16]
]) set(x, 7, z, z === 16 ? goldLight : gold);
set(32, 7, 16, "#422b27");
// Canvas pegs and side guy ropes.
for (const side of [-1, 1]) {
const sx = 24 + side * 15;
const ex = 24 + side * 21;
line(sx, 24, 19, ex, 24, 1, 0, "#b89459");
box(ex, 23, 0, ex, 25, 2, "#634630");
set(ex, 24, 3, "#302b29");
}
// The pennant alone pivots around its fastening on the pole.
part("pennant", [24, 24, 42]);
for (let x = 25; x <= 38; x++) {
const halfHeight = x < 31 ? 2 : x < 36 ? 1 : 0;
const y = 24 + (x >= 33 ? 1 : 0);
for (let z = 42 - halfHeight; z <= 42 + halfHeight; z++) {
set(x, y, z, z === 42 + halfHeight ? "#5286c5" :
(x + z) % 4 === 0 ? "#173e80" : "#245ba4");
}
}
set(25, 23, 42, goldLight);
set(38, 25, 42, goldLight);
animate("pennant", { type: "swing", axis: "z", speed: 3, amplitude: 0.3 });
part("body", [24, 24, 0]);import { draw } from "./assets/voxel";
import { tiendaDeCaballeros } from "./assets/tienda-de-caballeros";
// En cada cuadro: parado sobre (x, y), el centro de su base.
draw(ctx, tiendaDeCaballeros, 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 tienda-de-caballeros
# Con las herramientas de Fogón (conector)
add_assets({ slug: "<tu-juego>", ids: ["tienda-de-caballeros"] })
# Queda en client/assets/tienda-de-caballeros.ts
# https://fogon.page/object/tienda-de-caballeros