tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
⎇ c5a232cf24788ab75a850ffe81eca5a7af50e2f8
llimphi-voxel/src/bicho.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
1//! **Seres del bioma (fauna procedural)**: un catálogo de modelitos voxel de
2//! animales (vaca, oveja, ciervo, zorro, oso, caballo…) que **pueblan el mundo
3//! por semilla**, como la flora y las casas. A diferencia de un [`Mueble`](crate::Mueble)
4//! (prop de interior, colocado a mano), un [`Bicho`] lo **esparce el bioma**: el
5//! campo `fauna` del [`Bioma`](crate::Bioma) dice qué animales y con qué densidad,
6//! y [`generate_window`](crate::Bioma::generate_window) los siembra parados sobre
7//! el terreno, orientados al azar.
8//!
9//! Los animales usan **color RGB directo** (no [`Material`](crate::worldgen::Material)):
10//! el rosa del cerdo o el naranja del zorro no viven en la paleta de terreno. Son
11//! mates (sin brillo). Se orientan con `facing` (0=+Z, 1=+X, 2=−Z, 3=−X).
12
13use serde::{Deserialize, Serialize};
14
15use llimphi_3d::VoxelGrid;
16
17/// Catálogo de seres/animales que pueblan un bioma.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub enum Bicho {
20 /// Vaca: cuerpo blanco con manchas negras, cabeza y cuernos.
21 Vaca,
22 /// Oveja: cuerpo lanudo blanco, cara oscura.
23 Oveja,
24 /// Cerdo: cuerpo rosado, hocico y colita.
25 Cerdo,
26 /// Gallina: cuerpo blanco chico, cresta roja y pico.
27 Gallina,
28 /// Ciervo: cuerpo marrón, patas largas y astas.
29 Ciervo,
30 /// Conejo: cuerpo chico con orejas largas.
31 Conejo,
32 /// Zorro: cuerpo naranja, pecho blanco y cola tupida.
33 Zorro,
34 /// Lobo: cuerpo gris alargado, orejas y cola.
35 Lobo,
36 /// Oso: cuerpo marrón voluminoso, cabeza redonda.
37 Oso,
38 /// Caballo: cuerpo marrón alto, cuello, crin y cola.
39 Caballo,
40 /// Pájaro: cuerpo diminuto con alas y pico (posado).
41 Pajaro,
42 /// Pez: cuerpo con aleta caudal (para el agua/orilla).
43 Pez,
44 /// Tortuga: caparazón verde abovedado, cabeza y patas.
45 Tortuga,
46 /// Rana: cuerpo verde agazapado, ojos saltones.
47 Rana,
48 /// Serpiente: cuerpo largo y bajo en zigzag, con cabeza.
49 Serpiente,
50 /// Mariposa: cuerpecito con cuatro alas coloridas (revolotea bajo).
51 Mariposa,
52 // --- Granja / pradera ---
53 /// Pato: cuerpo blanco chico, pico naranja (orilla/pradera).
54 Pato,
55 /// Cabra: cuerpo claro con cuernitos (montaña/granja).
56 Cabra,
57 /// Burro: cuerpo gris con orejas largas (granja).
58 Burro,
59 // --- Bosque ---
60 /// Ardilla: cuerpito con cola tupida enroscada (bosque).
61 Ardilla,
62 /// Búho: cuerpo compacto con ojos grandes, posado (bosque).
63 Buho,
64 /// Erizo: lomo de púas oscuras, cara clara (bosque).
65 Erizo,
66 // --- Desierto ---
67 /// Camello: cuerpo arena con joroba y cuello (desierto).
68 Camello,
69 /// Escorpión: cuerpo bajo con pinzas y cola en alto (desierto).
70 Escorpion,
71 /// Lagarto: cuerpo verde alargado y bajo (desierto/selva).
72 Lagarto,
73 // --- Nieve / agua fría ---
74 /// Pingüino: cuerpo negro con panza blanca, pico naranja (nieve).
75 Pinguino,
76 /// Foca: cuerpo gris tendido con aletas (nieve/agua).
77 Foca,
78 // --- Selva / sabana ---
79 /// Mono: cuerpo marrón con cola larga (selva).
80 Mono,
81 /// Loro: cuerpo verde con pecho rojo y pico (selva).
82 Loro,
83 /// Tigre: cuerpo naranja con rayas negras (selva).
84 Tigre,
85 /// Elefante: cuerpo gris grande con trompa y orejas (sabana).
86 Elefante,
87 /// Cocodrilo: cuerpo verde largo y bajo con fauces (selva/agua).
88 Cocodrilo,
89}
90
91impl Bicho {
92 pub const ALL: [Bicho; 32] = [
93 Bicho::Vaca,
94 Bicho::Oveja,
95 Bicho::Cerdo,
96 Bicho::Gallina,
97 Bicho::Ciervo,
98 Bicho::Conejo,
99 Bicho::Zorro,
100 Bicho::Lobo,
101 Bicho::Oso,
102 Bicho::Caballo,
103 Bicho::Pajaro,
104 Bicho::Pez,
105 Bicho::Tortuga,
106 Bicho::Rana,
107 Bicho::Serpiente,
108 Bicho::Mariposa,
109 Bicho::Pato,
110 Bicho::Cabra,
111 Bicho::Burro,
112 Bicho::Ardilla,
113 Bicho::Buho,
114 Bicho::Erizo,
115 Bicho::Camello,
116 Bicho::Escorpion,
117 Bicho::Lagarto,
118 Bicho::Pinguino,
119 Bicho::Foca,
120 Bicho::Mono,
121 Bicho::Loro,
122 Bicho::Tigre,
123 Bicho::Elefante,
124 Bicho::Cocodrilo,
125 ];
126
127 pub fn label(self) -> &'static str {
128 match self {
129 Bicho::Vaca => "vaca",
130 Bicho::Oveja => "oveja",
131 Bicho::Cerdo => "cerdo",
132 Bicho::Gallina => "gallina",
133 Bicho::Ciervo => "ciervo",
134 Bicho::Conejo => "conejo",
135 Bicho::Zorro => "zorro",
136 Bicho::Lobo => "lobo",
137 Bicho::Oso => "oso",
138 Bicho::Caballo => "caballo",
139 Bicho::Pajaro => "pájaro",
140 Bicho::Pez => "pez",
141 Bicho::Tortuga => "tortuga",
142 Bicho::Rana => "rana",
143 Bicho::Serpiente => "serpiente",
144 Bicho::Mariposa => "mariposa",
145 Bicho::Pato => "pato",
146 Bicho::Cabra => "cabra",
147 Bicho::Burro => "burro",
148 Bicho::Ardilla => "ardilla",
149 Bicho::Buho => "búho",
150 Bicho::Erizo => "erizo",
151 Bicho::Camello => "camello",
152 Bicho::Escorpion => "escorpión",
153 Bicho::Lagarto => "lagarto",
154 Bicho::Pinguino => "pingüino",
155 Bicho::Foca => "foca",
156 Bicho::Mono => "mono",
157 Bicho::Loro => "loro",
158 Bicho::Tigre => "tigre",
159 Bicho::Elefante => "elefante",
160 Bicho::Cocodrilo => "cocodrilo",
161 }
162 }
163
164 pub fn next(self) -> Bicho {
165 let i = Bicho::ALL.iter().position(|&b| b == self).unwrap_or(0);
166 Bicho::ALL[(i + 1) % Bicho::ALL.len()]
167 }
168}
169
170/// **Uso de un ser en un bioma**: qué animal y con qué densidad `[0,1]` aparece
171/// (una candidata por parcela de la grilla de fauna; el bioma lo esparce por
172/// semilla → una fauna procedural, como la flora y las casas).
173#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
174pub struct BichoUso {
175 pub bicho: Bicho,
176 pub densidad: f32,
177}
178
179// Paleta cruda de fauna (colores que la paleta de terreno no tiene).
180const MARRON: [u8; 3] = [120, 84, 50];
181const MARRON_OSC: [u8; 3] = [86, 58, 34];
182const BLANCO: [u8; 3] = [230, 228, 222];
183const NEGRO: [u8; 3] = [42, 42, 46];
184const ROSA: [u8; 3] = [224, 152, 152];
185const NARANJA: [u8; 3] = [206, 108, 46];
186const GRIS: [u8; 3] = [132, 132, 138];
187const VERDE: [u8; 3] = [96, 156, 74];
188const VERDE_OSC: [u8; 3] = [64, 116, 56];
189const ROJO: [u8; 3] = [206, 72, 60];
190const AMARILLO: [u8; 3] = [224, 192, 92];
191const AZUL: [u8; 3] = [72, 116, 186];
192const TAN: [u8; 3] = [204, 172, 120];
193const MARRON_CLR: [u8; 3] = [158, 118, 76];
194const GRIS_OSC: [u8; 3] = [92, 94, 100];
195
196/// Hash barato de la posición → variedad de morfo (color/patrón) por dónde cae el
197/// animal, sin usar la semilla del mundo. Igual que la carrocería de un vehículo o
198/// el tinte del follaje: un rebaño estampado sale VARIADO, determinista por posición.
199#[inline]
200fn morph(x: u32, z: u32) -> u32 {
201 let mut h = x.wrapping_mul(0x9E37_79B9).wrapping_add(z.wrapping_mul(0x85EB_CA77));
202 h ^= h >> 15;
203 h = h.wrapping_mul(0x2C1B_3C6D);
204 (h ^ (h >> 13)) & 0xFFFF
205}
206
207/// Rota un desplazamiento `(dx, dz)` según `facing` (giros de 90°).
208#[inline]
209fn rot(dx: i32, dz: i32, facing: u8) -> (i32, i32) {
210 match facing & 3 {
211 0 => (dx, dz),
212 1 => (-dz, dx),
213 2 => (-dx, -dz),
214 _ => (dz, -dx),
215 }
216}
217
218/// **Detalle sub-voxel de un ser vivo** `(rugosidad, bisel)` — la gramática
219/// cuantizada que rompe la caja rectangular de los animales voxel. Rugosidad suave
220/// (grano de piel/pelaje, escala `VoxelGrid::DETALLE_RUG_MAX`) + bisel alto (redondeo
221/// orgánico de la silueta de sombreado): un ser vivo no es un ladrillo. Es la MISMA
222/// gramática de bisel/campo que el terreno (no una fidelidad distinta → sin valle),
223/// sólo con más redondeo por ser un cuerpo. `generate_window` lo aplica como pincel
224/// de detalle alrededor de cada `stamp_bicho`. El bisel sólo luce si el bisel GLOBAL
225/// de la escena está prendido (activa la isosuperficie); la rugosidad luce siempre.
226pub const DETALLE_SER_VIVO: (f32, f32) = (0.4, 0.85);
227
228/// **Estampa un ser** del `bicho` parado con las patas en `(x, y, z)` (base local
229/// `dy=0` = a ras del suelo), orientado por `facing`. `dz` crece hacia el frente
230/// (hocico), `dx` a lo ancho, `dy` hacia arriba.
231pub fn stamp_bicho(g: &mut VoxelGrid, bicho: Bicho, x: u32, y: u32, z: u32, facing: u8) {
232 let (bx, by, bz) = (x as i32, y as i32, z as i32);
233 let dim = g.dim();
234 let vh = morph(x, z); // morfo de color/patrón por posición (variedad de rebaño)
235 let mut put = |dx: i32, dy: i32, dz: i32, c: [u8; 3]| {
236 let (rx, rz) = rot(dx, dz, facing);
237 let (px, py, pz) = (bx + rx, by + dy, bz + rz);
238 if px >= 0 && py >= 0 && pz >= 0 && (px as u32) < dim[0] && (py as u32) < dim[1] && (pz as u32) < dim[2] {
239 g.set(px as u32, py as u32, pz as u32, c);
240 }
241 };
242
243 match bicho {
244 Bicho::Vaca => {
245 // Morfo de pelaje: holstein / marrón-blanca / negra.
246 let (base, mancha_c) = match vh % 3 {
247 0 => (BLANCO, NEGRO),
248 1 => (BLANCO, MARRON_CLR),
249 _ => (MARRON_CLR, NEGRO),
250 };
251 // Cuerpo (2 de ancho × 3 de largo × 2 de alto) sobre 4 patas.
252 for dx in -1i32..=0 {
253 for dz in -1i32..=1 {
254 for dy in 2..=3 {
255 // Manchas en algunas celdas, base en el resto.
256 let mancha = (dx + dz).rem_euclid(2) == 0 && dz != 1;
257 put(dx, dy, dz, if mancha { mancha_c } else { base });
258 }
259 }
260 }
261 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
262 put(lx, 0, lz, MARRON_OSC);
263 put(lx, 1, lz, MARRON_OSC);
264 }
265 // Cabeza al frente + cuernos.
266 put(0, 3, 2, ROSA);
267 put(-1, 3, 2, ROSA);
268 put(0, 4, 2, base);
269 put(-1, 4, 2, base);
270 put(1, 4, 2, [232, 228, 214]); // cuerno
271 put(-2, 4, 2, [232, 228, 214]); // cuerno
272 }
273 Bicho::Oveja => {
274 // Morfo de lana: blanca / beige / gris.
275 let lana = match vh % 3 {
276 0 => BLANCO,
277 1 => [214, 202, 176],
278 _ => [186, 186, 190],
279 };
280 // Cuerpo lanudo y mullido sobre patas oscuras.
281 for dx in -1..=0 {
282 for dz in -1..=1 {
283 for dy in 2..=3 {
284 put(dx, dy, dz, lana);
285 }
286 }
287 }
288 put(0, 4, 0, lana);
289 put(-1, 4, 0, lana);
290 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
291 put(lx, 0, lz, NEGRO);
292 put(lx, 1, lz, NEGRO);
293 }
294 // Cara oscura al frente.
295 put(0, 2, 2, NEGRO);
296 put(-1, 2, 2, NEGRO);
297 put(0, 3, 2, lana);
298 }
299 Bicho::Cerdo => {
300 // Cuerpo rosado rechoncho, patas cortas, hocico y colita.
301 for dx in -1..=0 {
302 for dz in -1..=1 {
303 put(dx, 1, dz, ROSA);
304 put(dx, 2, dz, ROSA);
305 }
306 }
307 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
308 put(lx, 0, lz, ROSA);
309 }
310 put(0, 1, 2, ROSA); // hocico
311 put(-1, 1, 2, ROSA);
312 put(0, 2, 2, ROSA);
313 put(-1, 2, 2, ROSA);
314 put(0, 2, 3, [232, 170, 170]); // trompa
315 put(-1, 1, -2, ROSA); // colita
316 }
317 Bicho::Gallina => {
318 // Morfo de plumaje: blanca / marrón / negra (con gallo rojizo).
319 let plum = match vh % 3 {
320 0 => BLANCO,
321 1 => MARRON_CLR,
322 _ => [70, 68, 74],
323 };
324 put(0, 1, 0, plum);
325 put(0, 1, -1, plum);
326 put(0, 2, 0, plum);
327 put(0, 2, 1, plum); // pecho/cabeza
328 put(0, 3, 1, ROJO); // cresta
329 put(0, 2, 2, AMARILLO); // pico
330 put(0, 0, 0, AMARILLO); // patas
331 put(0, 0, -1, AMARILLO);
332 put(0, 1, -2, plum); // cola
333 }
334 Bicho::Ciervo => {
335 // Patas largas, cuerpo marrón, cuello y cabeza al frente, astas.
336 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
337 put(lx, 0, lz, MARRON_OSC);
338 put(lx, 1, lz, MARRON_OSC);
339 }
340 for dx in -1..=0 {
341 for dz in -1..=1 {
342 put(dx, 2, dz, MARRON);
343 put(dx, 3, dz, MARRON);
344 }
345 }
346 // Cuello + cabeza.
347 put(0, 3, 2, MARRON);
348 put(0, 4, 2, MARRON);
349 put(0, 5, 2, MARRON);
350 put(0, 5, 3, MARRON); // hocico
351 // Astas.
352 put(0, 6, 2, MARRON_OSC);
353 put(1, 6, 2, MARRON_OSC);
354 put(-1, 6, 2, MARRON_OSC);
355 put(0, 6, 3, MARRON_OSC);
356 }
357 Bicho::Conejo => {
358 // Morfo de pelaje: blanco / marrón / gris.
359 let pelo = match vh % 3 {
360 0 => BLANCO,
361 1 => MARRON_CLR,
362 _ => GRIS,
363 };
364 // Cuerpo chico, orejas largas, cola de algodón.
365 put(0, 1, 0, pelo);
366 put(0, 1, -1, pelo);
367 put(0, 2, 0, pelo);
368 put(0, 2, 1, pelo); // cabeza al frente
369 put(0, 3, 1, pelo); // oreja
370 put(0, 4, 1, pelo);
371 put(0, 3, 0, pelo); // oreja 2
372 put(0, 4, 0, pelo);
373 put(0, 0, 0, pelo);
374 put(0, 0, -1, pelo);
375 put(0, 2, -1, [240, 240, 236]); // colita
376 }
377 Bicho::Zorro => {
378 // Cuerpo naranja, pecho/cola blancos, orejas puntiagudas.
379 for dz in -1..=1 {
380 put(0, 1, dz, NARANJA);
381 put(0, 2, dz, NARANJA);
382 }
383 put(0, 0, -1, MARRON_OSC); // patas
384 put(0, 0, 1, MARRON_OSC);
385 put(0, 2, 2, NARANJA); // cabeza
386 put(0, 3, 2, NARANJA); // oreja
387 put(0, 2, 3, BLANCO); // hocico
388 put(0, 1, 2, BLANCO); // pecho
389 put(0, 2, -2, BLANCO); // cola
390 put(0, 1, -2, NARANJA);
391 }
392 Bicho::Lobo => {
393 // Como el zorro pero gris y más largo/robusto.
394 for dz in -2..=1 {
395 put(0, 1, dz, GRIS);
396 put(0, 2, dz, GRIS);
397 }
398 for &lz in &[-1, 1] {
399 put(0, 0, lz, [96, 96, 102]);
400 }
401 put(0, 2, 2, GRIS); // cabeza
402 put(0, 3, 2, GRIS); // oreja
403 put(0, 2, 3, [110, 110, 116]); // hocico
404 put(0, 2, -3, GRIS); // cola
405 put(0, 3, -3, GRIS);
406 }
407 Bicho::Oso => {
408 // Cuerpo voluminoso (2×3), cabeza redonda con orejas.
409 for dx in -1..=0 {
410 for dz in -1..=1 {
411 for dy in 1..=3 {
412 put(dx, dy, dz, MARRON_OSC);
413 }
414 }
415 }
416 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
417 put(lx, 0, lz, MARRON_OSC);
418 }
419 put(0, 3, 2, MARRON); // cabeza
420 put(-1, 3, 2, MARRON);
421 put(0, 4, 2, MARRON);
422 put(-1, 4, 2, MARRON);
423 put(1, 4, 2, MARRON_OSC); // orejas
424 put(-2, 4, 2, MARRON_OSC);
425 }
426 Bicho::Caballo => {
427 // Morfo de capa: alazán / negro / bayo, con crin de contraste.
428 let (capa, crin) = match vh % 3 {
429 0 => (MARRON, MARRON_OSC),
430 1 => ([58, 54, 56], NEGRO),
431 _ => (TAN, MARRON_OSC),
432 };
433 // Alto: patas largas, lomo, cuello erguido, crin y cola.
434 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
435 put(lx, 0, lz, crin);
436 put(lx, 1, lz, capa);
437 put(lx, 2, lz, capa);
438 }
439 for dx in -1..=0 {
440 for dz in -1..=1 {
441 put(dx, 3, dz, capa);
442 }
443 }
444 // Cuello + cabeza.
445 put(0, 4, 1, capa);
446 put(0, 5, 1, capa);
447 put(0, 5, 2, capa); // testa
448 put(0, 4, 2, capa);
449 put(0, 6, 1, crin); // crin
450 put(0, 5, 0, crin);
451 put(-1, 4, -1, crin); // cola
452 put(-1, 3, -2, crin);
453 }
454 Bicho::Pajaro => {
455 // Diminuto: cuerpo azul, alas, pico, patitas.
456 put(0, 1, 0, AZUL);
457 put(0, 2, 0, AZUL); // cabeza
458 put(0, 2, 1, AMARILLO); // pico
459 put(1, 1, 0, [96, 140, 210]); // ala
460 put(-1, 1, 0, [96, 140, 210]);
461 put(0, 1, -1, AZUL); // cola
462 put(0, 0, 0, AMARILLO); // pata
463 }
464 Bicho::Pez => {
465 // Cuerpo lateral + aleta caudal (se coloca en/junto al agua).
466 put(0, 1, 0, AZUL);
467 put(0, 1, 1, AZUL);
468 put(0, 2, 0, [110, 150, 210]);
469 put(0, 1, 2, [110, 150, 210]); // morro
470 put(0, 1, -1, [150, 180, 220]); // cola
471 put(0, 2, -1, [150, 180, 220]);
472 put(0, 0, -1, [150, 180, 220]);
473 }
474 Bicho::Tortuga => {
475 // Caparazón verde abovedado + cabeza + 4 patas.
476 for dx in -1..=1 {
477 for dz in -1..=1 {
478 put(dx, 1, dz, VERDE_OSC);
479 }
480 }
481 put(0, 2, 0, VERDE); // cúpula del caparazón
482 put(0, 1, 2, [180, 200, 120]); // cabeza
483 for &(lx, lz) in &[(-1, -1), (1, -1), (-1, 1), (1, 1)] {
484 put(lx, 0, lz, [180, 200, 120]); // patas
485 }
486 }
487 Bicho::Rana => {
488 // Cuerpo verde agazapado, ojos saltones, patas dobladas.
489 put(0, 1, 0, VERDE);
490 put(-1, 1, 0, VERDE);
491 put(0, 1, 1, VERDE); // frente
492 put(-1, 1, 1, VERDE);
493 put(0, 2, 1, VERDE_OSC); // ojo
494 put(-1, 2, 1, VERDE_OSC);
495 put(0, 0, -1, VERDE); // pata trasera
496 put(-1, 0, -1, VERDE);
497 put(0, 0, 2, [200, 210, 140]); // pata delantera
498 }
499 Bicho::Serpiente => {
500 // Cuerpo largo y bajo en zigzag + cabeza.
501 let cuerpo = [(0, 0), (0, -1), (-1, -1), (-1, -2), (-2, -2), (-2, -3)];
502 for &(sx, sz) in &cuerpo {
503 put(sx, 1, sz, VERDE_OSC);
504 }
505 put(0, 1, 1, VERDE); // cabeza
506 put(0, 1, 2, VERDE);
507 put(0, 2, 1, ROJO); // lengua/ojo
508 }
509 Bicho::Mariposa => {
510 // Morfo de alas: naranja monarca / azul / amarilla.
511 let (ala1, ala2) = match vh % 3 {
512 0 => (AMARILLO, NARANJA),
513 1 => (AZUL, [140, 180, 230]),
514 _ => (AMARILLO, [230, 170, 60]),
515 };
516 put(0, 3, 0, NEGRO); // cuerpo
517 put(0, 2, 0, NEGRO);
518 put(1, 3, 0, ala1); // alas
519 put(1, 3, 1, ala2);
520 put(-1, 3, 0, ala1);
521 put(-1, 3, 1, ala2);
522 put(1, 2, 0, ala2);
523 put(-1, 2, 0, ala2);
524 }
525
526 // ---- Granja / pradera ----
527 Bicho::Pato => {
528 // Cuerpo blanco chico, cuello, pico naranja, cola.
529 put(0, 1, 0, BLANCO);
530 put(0, 1, -1, BLANCO);
531 put(-1, 1, 0, BLANCO);
532 put(0, 2, 1, BLANCO); // cuello/cabeza
533 put(0, 3, 1, BLANCO);
534 put(0, 3, 2, NARANJA); // pico
535 put(0, 2, -1, BLANCO); // cola
536 put(0, 0, 0, NARANJA); // patas
537 }
538 Bicho::Cabra => {
539 // Morfo: blanca / parda. Cuerpo, patas, cabeza barbada y cuernos.
540 let capa = if vh % 2 == 0 { [222, 216, 202] } else { MARRON_CLR };
541 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
542 put(lx, 0, lz, MARRON_OSC);
543 put(lx, 1, lz, capa);
544 }
545 for dx in -1..=0 {
546 for dz in -1..=1 {
547 put(dx, 2, dz, capa);
548 }
549 }
550 put(0, 2, 2, capa); // cabeza
551 put(0, 3, 2, capa);
552 put(0, 2, 3, capa); // hocico
553 put(0, 4, 2, [232, 228, 214]); // cuerno
554 put(0, 1, 2, [220, 214, 200]); // barbita
555 put(-1, 2, -2, capa); // cola corta
556 }
557 Bicho::Burro => {
558 // Gris, patas, lomo, cuello y orejas largas.
559 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
560 put(lx, 0, lz, GRIS_OSC);
561 put(lx, 1, lz, GRIS);
562 }
563 for dx in -1..=0 {
564 for dz in -1..=1 {
565 put(dx, 2, dz, GRIS);
566 }
567 }
568 put(0, 3, 1, GRIS); // cuello
569 put(0, 3, 2, GRIS); // cabeza
570 put(0, 4, 2, GRIS); // oreja larga
571 put(0, 5, 2, GRIS_OSC);
572 put(-1, 2, -2, GRIS_OSC); // cola
573 }
574
575 // ---- Bosque ----
576 Bicho::Ardilla => {
577 // Cuerpito naranja con cola tupida enroscada hacia arriba.
578 put(0, 1, 0, NARANJA);
579 put(0, 2, 0, NARANJA);
580 put(0, 2, 1, NARANJA); // cabeza
581 put(0, 3, 1, [222, 150, 90]); // oreja
582 put(0, 1, 1, [232, 190, 150]); // pecho claro
583 put(0, 0, 0, MARRON_OSC); // pata
584 put(0, 2, -1, NARANJA); // cola
585 put(0, 3, -1, [222, 150, 90]);
586 put(0, 4, -1, NARANJA);
587 put(0, 4, 0, [222, 150, 90]);
588 }
589 Bicho::Buho => {
590 // Compacto, posado, con ojos grandes y penachos.
591 for dy in 1..=3 {
592 put(0, dy, 0, MARRON);
593 put(-1, dy, 0, MARRON);
594 }
595 put(0, 3, 1, AMARILLO); // ojo
596 put(-1, 3, 1, AMARILLO);
597 put(0, 4, 0, MARRON_OSC); // penacho
598 put(-1, 4, 0, MARRON_OSC);
599 put(0, 2, 1, [206, 180, 140]); // pecho
600 put(-1, 2, 1, [206, 180, 140]);
601 put(0, 0, 0, AMARILLO); // garras
602 put(-1, 0, 0, AMARILLO);
603 }
604 Bicho::Erizo => {
605 // Cuerpo bajo con lomo de púas oscuras y carita clara.
606 for dx in -1..=0 {
607 for dz in -1..=1 {
608 put(dx, 1, dz, MARRON_OSC);
609 put(dx, 2, dz, [64, 52, 40]); // púas
610 }
611 }
612 put(0, 2, 2, [222, 198, 160]); // carita
613 put(-1, 2, 2, [222, 198, 160]);
614 put(0, 1, 3, NEGRO); // hocico
615 for &(lx, lz) in &[(-1, -1), (0, 1)] {
616 put(lx, 0, lz, [120, 96, 72]);
617 }
618 }
619
620 // ---- Desierto ----
621 Bicho::Camello => {
622 // Color arena, patas largas, joroba, cuello y cabeza.
623 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
624 put(lx, 0, lz, [176, 148, 104]);
625 put(lx, 1, lz, TAN);
626 put(lx, 2, lz, TAN);
627 }
628 for dx in -1..=0 {
629 for dz in -1..=1 {
630 put(dx, 3, dz, TAN);
631 }
632 }
633 put(0, 4, 0, TAN); // joroba
634 put(-1, 4, 0, TAN);
635 put(0, 4, 1, TAN); // cuello
636 put(0, 5, 2, TAN); // cabeza
637 put(0, 5, 3, [176, 148, 104]); // hocico
638 put(-1, 3, -2, TAN); // cola
639 }
640 Bicho::Escorpion => {
641 // Cuerpo bajo oscuro, pinzas al frente, cola curva con aguijón.
642 put(0, 1, 0, [70, 58, 40]);
643 put(0, 1, 1, [70, 58, 40]);
644 put(0, 1, 2, [86, 70, 48]); // pinzas
645 put(1, 1, 2, [86, 70, 48]);
646 put(-1, 1, 2, [86, 70, 48]);
647 put(0, 1, -1, [70, 58, 40]); // cola
648 put(0, 2, -1, [70, 58, 40]);
649 put(0, 2, 0, [70, 58, 40]);
650 put(0, 3, 0, ROJO); // aguijón
651 }
652 Bicho::Lagarto => {
653 // Cuerpo verde alargado y bajo, patitas, cabeza, cola larga.
654 for dz in -2..=1 {
655 put(0, 1, dz, VERDE_OSC);
656 }
657 put(0, 1, 2, VERDE); // cabeza
658 put(0, 1, -3, VERDE_OSC); // cola
659 put(0, 1, -4, [80, 130, 66]);
660 put(1, 0, 0, VERDE_OSC); // patas
661 put(-1, 0, 0, VERDE_OSC);
662 put(1, 0, -1, VERDE_OSC);
663 put(-1, 0, -1, VERDE_OSC);
664 }
665
666 // ---- Nieve / agua fría ----
667 Bicho::Pinguino => {
668 // De pie: espalda negra, panza blanca, pico y patas naranjas.
669 for dy in 1..=3 {
670 put(0, dy, 0, NEGRO); // espalda
671 }
672 put(0, 1, 1, BLANCO); // panza
673 put(0, 2, 1, BLANCO);
674 put(0, 4, 0, NEGRO); // cabeza
675 put(0, 4, 1, NARANJA); // pico
676 put(1, 2, 0, NEGRO); // aleta
677 put(-1, 2, 0, NEGRO);
678 put(0, 0, 1, NARANJA); // patas
679 }
680 Bicho::Foca => {
681 // Cuerpo gris tendido, cabeza levantada, aletas.
682 for dz in -2..=1 {
683 put(0, 1, dz, GRIS);
684 put(-1, 1, dz, GRIS);
685 }
686 put(0, 2, 1, GRIS); // cabeza
687 put(-1, 2, 1, GRIS);
688 put(0, 2, 2, [110, 112, 118]); // hocico
689 put(0, 1, -3, [110, 112, 118]); // cola/aleta
690 put(-1, 1, -3, [110, 112, 118]);
691 put(1, 1, 0, [150, 150, 156]); // aleta lateral
692 }
693
694 // ---- Selva / sabana ----
695 Bicho::Mono => {
696 // Cuerpo marrón sentado, cabeza clara, cola larga curva.
697 put(0, 1, 0, MARRON);
698 put(0, 2, 0, MARRON);
699 put(0, 2, 1, [176, 140, 100]); // cara clara
700 put(0, 3, 1, MARRON); // frente
701 put(0, 1, 1, MARRON); // rodillas
702 put(1, 2, 0, MARRON); // brazo
703 put(-1, 2, 0, MARRON);
704 put(0, 1, -1, MARRON); // cola
705 put(0, 2, -1, MARRON);
706 put(0, 2, -2, MARRON);
707 }
708 Bicho::Loro => {
709 // Cuerpo verde vivo, pecho rojo, pico y cola larga.
710 put(0, 1, 0, [70, 168, 72]);
711 put(0, 2, 0, [70, 168, 72]);
712 put(0, 3, 0, [70, 168, 72]); // cabeza
713 put(0, 3, 1, ROJO); // pico
714 put(0, 1, 1, ROJO); // pecho
715 put(1, 2, 0, [90, 190, 90]); // ala
716 put(0, 1, -1, [220, 200, 60]); // cola
717 put(0, 1, -2, [70, 168, 72]);
718 put(0, 0, 0, [120, 96, 72]); // pata
719 }
720 Bicho::Tigre => {
721 // Cuerpo naranja con rayas negras, patas, cabeza, cola.
722 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
723 put(lx, 0, lz, NARANJA);
724 }
725 for dx in -1i32..=0 {
726 for dz in -1i32..=1 {
727 // Rayas: bandas negras alternas a lo largo.
728 let raya = dz.rem_euclid(2) == 0;
729 put(dx, 1, dz, if raya { NEGRO } else { NARANJA });
730 }
731 }
732 put(0, 2, 2, NARANJA); // cabeza
733 put(-1, 2, 2, NARANJA);
734 put(0, 1, 2, [232, 200, 150]); // hocico
735 put(0, 2, -2, NARANJA); // cola
736 put(0, 2, -3, NEGRO);
737 }
738 Bicho::Elefante => {
739 // Grande: cuerpo gris (2×3×2), patas, trompa, orejas, colmillos.
740 for dx in -1i32..=0 {
741 for dz in -1..=1 {
742 for dy in 2..=3 {
743 put(dx, dy, dz, GRIS);
744 }
745 }
746 }
747 for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] {
748 put(lx, 0, lz, [110, 112, 118]);
749 put(lx, 1, lz, GRIS);
750 }
751 put(0, 3, 2, GRIS); // cabeza
752 put(-1, 3, 2, GRIS);
753 put(0, 2, 2, GRIS);
754 put(0, 1, 3, GRIS); // trompa
755 put(0, 0, 3, [110, 112, 118]);
756 put(1, 3, 1, [150, 150, 156]); // oreja
757 put(-2, 3, 1, [150, 150, 156]);
758 put(0, 1, 2, [232, 228, 214]); // colmillo
759 }
760 Bicho::Cocodrilo => {
761 // Cuerpo verde largo y bajo, dorso escamado, fauces, cola.
762 for dz in -3..=2 {
763 put(0, 1, dz, VERDE_OSC);
764 put(-1, 1, dz, VERDE_OSC);
765 }
766 // Dorso escamado (crestas alternas).
767 for dz in -3i32..=1 {
768 if dz.rem_euclid(2) == 0 {
769 put(0, 2, dz, [72, 128, 60]);
770 }
771 }
772 put(0, 1, 3, VERDE); // hocico
773 put(-1, 1, 3, VERDE);
774 put(0, 2, 3, NEGRO); // ojo/fauces
775 put(0, 1, -4, VERDE_OSC); // cola
776 put(0, 1, -5, [80, 130, 66]);
777 put(1, 0, 0, VERDE_OSC); // patas
778 put(-2, 0, 0, VERDE_OSC);
779 }
780 }
781}
782
783#[cfg(test)]
784mod tests {
785 use super::*;
786
787 fn solids(g: &VoxelGrid, dim: [u32; 3]) -> usize {
788 let mut n = 0;
789 for x in 0..dim[0] {
790 for y in 0..dim[1] {
791 for z in 0..dim[2] {
792 if g.is_solid(x as i32, y as i32, z as i32) {
793 n += 1;
794 }
795 }
796 }
797 }
798 n
799 }
800
801 #[test]
802 fn cada_bicho_estampa_voxels() {
803 let dim = [20u32, 16, 20];
804 for bicho in Bicho::ALL {
805 let mut g = VoxelGrid::new(dim);
806 stamp_bicho(&mut g, bicho, 10, 1, 10, 0);
807 assert!(solids(&g, dim) >= 5, "{} estampó muy poco", bicho.label());
808 }
809 }
810
811 #[test]
812 fn facing_rota_la_silueta() {
813 let dim = [24u32, 16, 24];
814 let mut a = VoxelGrid::new(dim);
815 let mut b = VoxelGrid::new(dim);
816 stamp_bicho(&mut a, Bicho::Ciervo, 12, 1, 12, 0);
817 stamp_bicho(&mut b, Bicho::Ciervo, 12, 1, 12, 1);
818 // Al rotar, la ocupación cambia (el hocico apunta a otro eje).
819 let mut diff = 0;
820 for x in 0..dim[0] {
821 for y in 0..dim[1] {
822 for z in 0..dim[2] {
823 if a.is_solid(x as i32, y as i32, z as i32) != b.is_solid(x as i32, y as i32, z as i32) {
824 diff += 1;
825 }
826 }
827 }
828 }
829 assert!(diff > 0, "facing no rotó la silueta");
830 }
831}