//! **Seres del bioma (fauna procedural)**: un catálogo de modelitos voxel de //! animales (vaca, oveja, ciervo, zorro, oso, caballo…) que **pueblan el mundo //! por semilla**, como la flora y las casas. A diferencia de un [`Mueble`](crate::Mueble) //! (prop de interior, colocado a mano), un [`Bicho`] lo **esparce el bioma**: el //! campo `fauna` del [`Bioma`](crate::Bioma) dice qué animales y con qué densidad, //! y [`generate_window`](crate::Bioma::generate_window) los siembra parados sobre //! el terreno, orientados al azar. //! //! Los animales usan **color RGB directo** (no [`Material`](crate::worldgen::Material)): //! el rosa del cerdo o el naranja del zorro no viven en la paleta de terreno. Son //! mates (sin brillo). Se orientan con `facing` (0=+Z, 1=+X, 2=−Z, 3=−X). use serde::{Deserialize, Serialize}; use llimphi_3d::VoxelGrid; /// Catálogo de seres/animales que pueblan un bioma. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Bicho { /// Vaca: cuerpo blanco con manchas negras, cabeza y cuernos. Vaca, /// Oveja: cuerpo lanudo blanco, cara oscura. Oveja, /// Cerdo: cuerpo rosado, hocico y colita. Cerdo, /// Gallina: cuerpo blanco chico, cresta roja y pico. Gallina, /// Ciervo: cuerpo marrón, patas largas y astas. Ciervo, /// Conejo: cuerpo chico con orejas largas. Conejo, /// Zorro: cuerpo naranja, pecho blanco y cola tupida. Zorro, /// Lobo: cuerpo gris alargado, orejas y cola. Lobo, /// Oso: cuerpo marrón voluminoso, cabeza redonda. Oso, /// Caballo: cuerpo marrón alto, cuello, crin y cola. Caballo, /// Pájaro: cuerpo diminuto con alas y pico (posado). Pajaro, /// Pez: cuerpo con aleta caudal (para el agua/orilla). Pez, /// Tortuga: caparazón verde abovedado, cabeza y patas. Tortuga, /// Rana: cuerpo verde agazapado, ojos saltones. Rana, /// Serpiente: cuerpo largo y bajo en zigzag, con cabeza. Serpiente, /// Mariposa: cuerpecito con cuatro alas coloridas (revolotea bajo). Mariposa, // --- Granja / pradera --- /// Pato: cuerpo blanco chico, pico naranja (orilla/pradera). Pato, /// Cabra: cuerpo claro con cuernitos (montaña/granja). Cabra, /// Burro: cuerpo gris con orejas largas (granja). Burro, // --- Bosque --- /// Ardilla: cuerpito con cola tupida enroscada (bosque). Ardilla, /// Búho: cuerpo compacto con ojos grandes, posado (bosque). Buho, /// Erizo: lomo de púas oscuras, cara clara (bosque). Erizo, // --- Desierto --- /// Camello: cuerpo arena con joroba y cuello (desierto). Camello, /// Escorpión: cuerpo bajo con pinzas y cola en alto (desierto). Escorpion, /// Lagarto: cuerpo verde alargado y bajo (desierto/selva). Lagarto, // --- Nieve / agua fría --- /// Pingüino: cuerpo negro con panza blanca, pico naranja (nieve). Pinguino, /// Foca: cuerpo gris tendido con aletas (nieve/agua). Foca, // --- Selva / sabana --- /// Mono: cuerpo marrón con cola larga (selva). Mono, /// Loro: cuerpo verde con pecho rojo y pico (selva). Loro, /// Tigre: cuerpo naranja con rayas negras (selva). Tigre, /// Elefante: cuerpo gris grande con trompa y orejas (sabana). Elefante, /// Cocodrilo: cuerpo verde largo y bajo con fauces (selva/agua). Cocodrilo, // --- El bestiario SDF (2026-09-06) ------------------------------------- // Variantes SIEMPRE al final: `BichoUso` viaja en postcard, que numera por // posición — una al medio corre a todas las de abajo y un mundo guardado // cambia de fauna sin decir nada. // // Estas nacieron para la segunda técnica de graficación (`bicho_sdf`), que // las arma de primitivas analíticas. En VOXEL no tienen estampa propia: // `silueta_voxel` las manda a la de un pariente, que es el LOD lejano. /// Perro: compañía, orejas caídas y cola tupida. Perro, /// Gato: lomo arqueado, orejas en punta y cola larga. Gato, /// Toro: cuello macizo, morrillo y cuernos en lira. Toro, /// Alce: cruz alta, hocico largo y astas palmeadas. Alce, /// Mapache: antifaz oscuro y cola anillada. Mapache, /// Ratón: orejas redondas grandes y cola pelada. Raton, /// Panda: blanco y negro, cara chata, orejas redondas. Panda, /// Koala: orejas mullidas enormes, sin cola. Koala, /// Canguro: bípedo, cola gruesa de contrapeso. Canguro, /// Llama: cuello largo lanudo (andes). Llama, /// Jirafa: cuello altísimo y manchas. Jirafa, /// León: melena alrededor del cuello y cola con penacho. Leon, /// Cebra: caballo a rayas. Cebra, /// Jaguar: felino manchado de la selva. Jaguar, /// Hipopótamo: cuerpo bajo y bocaza. Hipopotamo, /// Rinoceronte: cuerno nasal y piel de placas. Rinoceronte, /// Capibara: roedor grande y tranquilo de ribera. Capibara, /// Perezoso: brazos largos, cara serena, sin cola. Perezoso, /// Gorrión: pajarito pardo de ciudad. Gorrion, /// Cardenal: rojo con copete. Cardenal, /// Cuervo: negro entero, pico recto. Cuervo, /// Colibrí: pico aguja y alas en batido. Colibri, /// Guacamayo: rojo/azul/amarillo con cola larguísima. Guacamayo, /// Tucán: pico enorme naranja. Tucan, /// Quetzal: verde metálico con cola de dos plumas largas. Quetzal, /// Flamenco: zancudo rosado de cuello en S. Flamenco, /// Garza: zancuda blanca de pico largo. Garza, /// Cisne: cuello en S y pico naranja. Cisne, /// Ganso: cuello mediano, pico pala. Ganso, /// Pelícano: pico con bolsa. Pelicano, /// Águila: pico ganchudo y alas abiertas. Aguila, /// Cóndor: planeador enorme de collar blanco. Condor, /// Pavo real: rueda de ocelos y copete. PavoReal, /// Avestruz: patas largas, cuello pelado. Avestruz, /// Martín pescador: azul con pico largo y copete. Martin, /// Gallo: cresta roja y cola en abanico. Gallo, /// Boa: serpiente gruesa con rombos. Boa, /// Cobra: capucha desplegada y tercio delantero alzado. Cobra, /// Iguana: cresta dorsal de púas. Iguana, /// Camaleón: cuerpo alto y comprimido, ojos saltones. Camaleon, /// Salamandra: negra con manchas amarillas. Salamandra, /// Delfín: hocico y cola horizontal. Delfin, /// Ballena: cuerpo enorme y cola horizontal. Ballena, /// Tiburón: aleta dorsal alta y boca abajo. Tiburon, /// Pulpo: manto y ocho tentáculos. Pulpo, /// Abeja: rayas y alas de membrana. Abeja, /// Libélula: abdomen largo y cuatro alas. Libelula, /// Escarabajo: élitros duros con lunares. Escarabajo, /// Hormiga: tres segmentos y seis patas. Hormiga, /// Araña: ocho patas arqueadas. Arana, /// Cangrejo: caparazón plano, pinzas y ojos en pedúnculo. Cangrejo, /// Caracol: concha en espiral y cuernitos. Caracol, /// Luciérnaga: élitros oscuros y abdomen luminoso. Luciernaga, } impl Bicho { pub const ALL: [Bicho; 85] = [ Bicho::Vaca, Bicho::Oveja, Bicho::Cerdo, Bicho::Gallina, Bicho::Ciervo, Bicho::Conejo, Bicho::Zorro, Bicho::Lobo, Bicho::Oso, Bicho::Caballo, Bicho::Pajaro, Bicho::Pez, Bicho::Tortuga, Bicho::Rana, Bicho::Serpiente, Bicho::Mariposa, Bicho::Pato, Bicho::Cabra, Bicho::Burro, Bicho::Ardilla, Bicho::Buho, Bicho::Erizo, Bicho::Camello, Bicho::Escorpion, Bicho::Lagarto, Bicho::Pinguino, Bicho::Foca, Bicho::Mono, Bicho::Loro, Bicho::Tigre, Bicho::Elefante, Bicho::Cocodrilo, Bicho::Perro, Bicho::Gato, Bicho::Toro, Bicho::Alce, Bicho::Mapache, Bicho::Raton, Bicho::Panda, Bicho::Koala, Bicho::Canguro, Bicho::Llama, Bicho::Jirafa, Bicho::Leon, Bicho::Cebra, Bicho::Jaguar, Bicho::Hipopotamo, Bicho::Rinoceronte, Bicho::Capibara, Bicho::Perezoso, Bicho::Gorrion, Bicho::Cardenal, Bicho::Cuervo, Bicho::Colibri, Bicho::Guacamayo, Bicho::Tucan, Bicho::Quetzal, Bicho::Flamenco, Bicho::Garza, Bicho::Cisne, Bicho::Ganso, Bicho::Pelicano, Bicho::Aguila, Bicho::Condor, Bicho::PavoReal, Bicho::Avestruz, Bicho::Martin, Bicho::Gallo, Bicho::Boa, Bicho::Cobra, Bicho::Iguana, Bicho::Camaleon, Bicho::Salamandra, Bicho::Delfin, Bicho::Ballena, Bicho::Tiburon, Bicho::Pulpo, Bicho::Abeja, Bicho::Libelula, Bicho::Escarabajo, Bicho::Hormiga, Bicho::Arana, Bicho::Cangrejo, Bicho::Caracol, Bicho::Luciernaga, ]; pub fn label(self) -> &'static str { match self { Bicho::Vaca => "vaca", Bicho::Oveja => "oveja", Bicho::Cerdo => "cerdo", Bicho::Gallina => "gallina", Bicho::Ciervo => "ciervo", Bicho::Conejo => "conejo", Bicho::Zorro => "zorro", Bicho::Lobo => "lobo", Bicho::Oso => "oso", Bicho::Caballo => "caballo", Bicho::Pajaro => "pájaro", Bicho::Pez => "pez", Bicho::Tortuga => "tortuga", Bicho::Rana => "rana", Bicho::Serpiente => "serpiente", Bicho::Mariposa => "mariposa", Bicho::Pato => "pato", Bicho::Cabra => "cabra", Bicho::Burro => "burro", Bicho::Ardilla => "ardilla", Bicho::Buho => "búho", Bicho::Erizo => "erizo", Bicho::Camello => "camello", Bicho::Escorpion => "escorpión", Bicho::Lagarto => "lagarto", Bicho::Pinguino => "pingüino", Bicho::Foca => "foca", Bicho::Mono => "mono", Bicho::Loro => "loro", Bicho::Tigre => "tigre", Bicho::Elefante => "elefante", Bicho::Cocodrilo => "cocodrilo", Bicho::Perro => "perro", Bicho::Gato => "gato", Bicho::Toro => "toro", Bicho::Alce => "alce", Bicho::Mapache => "mapache", Bicho::Raton => "ratón", Bicho::Panda => "panda", Bicho::Koala => "koala", Bicho::Canguro => "canguro", Bicho::Llama => "llama", Bicho::Jirafa => "jirafa", Bicho::Leon => "león", Bicho::Cebra => "cebra", Bicho::Jaguar => "jaguar", Bicho::Hipopotamo => "hipopótamo", Bicho::Rinoceronte => "rinoceronte", Bicho::Capibara => "capibara", Bicho::Perezoso => "perezoso", Bicho::Gorrion => "gorrión", Bicho::Cardenal => "cardenal", Bicho::Cuervo => "cuervo", Bicho::Colibri => "colibrí", Bicho::Guacamayo => "guacamayo", Bicho::Tucan => "tucán", Bicho::Quetzal => "quetzal", Bicho::Flamenco => "flamenco", Bicho::Garza => "garza", Bicho::Cisne => "cisne", Bicho::Ganso => "ganso", Bicho::Pelicano => "pelícano", Bicho::Aguila => "águila", Bicho::Condor => "cóndor", Bicho::PavoReal => "pavo real", Bicho::Avestruz => "avestruz", Bicho::Martin => "martín pescador", Bicho::Gallo => "gallo", Bicho::Boa => "boa", Bicho::Cobra => "cobra", Bicho::Iguana => "iguana", Bicho::Camaleon => "camaleón", Bicho::Salamandra => "salamandra", Bicho::Delfin => "delfín", Bicho::Ballena => "ballena", Bicho::Tiburon => "tiburón", Bicho::Pulpo => "pulpo", Bicho::Abeja => "abeja", Bicho::Libelula => "libélula", Bicho::Escarabajo => "escarabajo", Bicho::Hormiga => "hormiga", Bicho::Arana => "araña", Bicho::Cangrejo => "cangrejo", Bicho::Caracol => "caracol", Bicho::Luciernaga => "luciérnaga", } } /// **La estampa VOXEL que le toca** a este bicho. Las 32 primeras tienen la /// suya; las del bestiario SDF (2026-09-06) reusan la de un pariente de silueta /// parecida — a la distancia en que se usa el voxel (LOD lejano, fauna esparcida /// por el bioma) un jaguar y un tigre son el mismo puñado de voxels, y una /// estampa a medias sería peor que la del pariente. pub fn silueta_voxel(self) -> Bicho { match self { Bicho::Perro => Bicho::Zorro, Bicho::Gato => Bicho::Zorro, Bicho::Toro => Bicho::Vaca, Bicho::Alce => Bicho::Ciervo, Bicho::Mapache => Bicho::Zorro, Bicho::Raton => Bicho::Conejo, Bicho::Panda => Bicho::Oso, Bicho::Koala => Bicho::Oso, Bicho::Canguro => Bicho::Ciervo, Bicho::Llama => Bicho::Oveja, Bicho::Jirafa => Bicho::Caballo, Bicho::Leon => Bicho::Oso, Bicho::Cebra => Bicho::Caballo, Bicho::Jaguar => Bicho::Tigre, Bicho::Hipopotamo => Bicho::Oso, Bicho::Rinoceronte => Bicho::Oso, Bicho::Capibara => Bicho::Cerdo, Bicho::Perezoso => Bicho::Mono, Bicho::Gorrion => Bicho::Pajaro, Bicho::Cardenal => Bicho::Pajaro, Bicho::Cuervo => Bicho::Pajaro, Bicho::Colibri => Bicho::Pajaro, Bicho::Guacamayo => Bicho::Loro, Bicho::Tucan => Bicho::Loro, Bicho::Quetzal => Bicho::Loro, Bicho::Flamenco => Bicho::Pato, Bicho::Garza => Bicho::Pato, Bicho::Cisne => Bicho::Pato, Bicho::Ganso => Bicho::Pato, Bicho::Pelicano => Bicho::Pato, Bicho::Aguila => Bicho::Buho, Bicho::Condor => Bicho::Buho, Bicho::PavoReal => Bicho::Gallina, Bicho::Avestruz => Bicho::Gallina, Bicho::Martin => Bicho::Pajaro, Bicho::Gallo => Bicho::Gallina, Bicho::Boa => Bicho::Serpiente, Bicho::Cobra => Bicho::Serpiente, Bicho::Iguana => Bicho::Lagarto, Bicho::Camaleon => Bicho::Lagarto, Bicho::Salamandra => Bicho::Lagarto, Bicho::Delfin => Bicho::Pez, Bicho::Ballena => Bicho::Pez, Bicho::Tiburon => Bicho::Pez, Bicho::Pulpo => Bicho::Pez, Bicho::Abeja => Bicho::Mariposa, Bicho::Libelula => Bicho::Mariposa, Bicho::Escarabajo => Bicho::Escorpion, Bicho::Hormiga => Bicho::Escorpion, Bicho::Arana => Bicho::Escorpion, Bicho::Cangrejo => Bicho::Escorpion, Bicho::Caracol => Bicho::Escorpion, Bicho::Luciernaga => Bicho::Mariposa, otro => otro, } } pub fn next(self) -> Bicho { let i = Bicho::ALL.iter().position(|&b| b == self).unwrap_or(0); Bicho::ALL[(i + 1) % Bicho::ALL.len()] } } /// **Uso de un ser en un bioma**: qué animal y con qué densidad `[0,1]` aparece /// (una candidata por parcela de la grilla de fauna; el bioma lo esparce por /// semilla → una fauna procedural, como la flora y las casas). #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct BichoUso { pub bicho: Bicho, pub densidad: f32, } // Paleta cruda de fauna (colores que la paleta de terreno no tiene). const MARRON: [u8; 3] = [120, 84, 50]; const MARRON_OSC: [u8; 3] = [86, 58, 34]; const BLANCO: [u8; 3] = [230, 228, 222]; const NEGRO: [u8; 3] = [42, 42, 46]; const ROSA: [u8; 3] = [224, 152, 152]; const NARANJA: [u8; 3] = [206, 108, 46]; const GRIS: [u8; 3] = [132, 132, 138]; const VERDE: [u8; 3] = [96, 156, 74]; const VERDE_OSC: [u8; 3] = [64, 116, 56]; const ROJO: [u8; 3] = [206, 72, 60]; const AMARILLO: [u8; 3] = [224, 192, 92]; const AZUL: [u8; 3] = [72, 116, 186]; const TAN: [u8; 3] = [204, 172, 120]; const MARRON_CLR: [u8; 3] = [158, 118, 76]; const GRIS_OSC: [u8; 3] = [92, 94, 100]; /// Hash barato de la posición → variedad de morfo (color/patrón) por dónde cae el /// animal, sin usar la semilla del mundo. Igual que la carrocería de un vehículo o /// el tinte del follaje: un rebaño estampado sale VARIADO, determinista por posición. #[inline] fn morph(x: u32, z: u32) -> u32 { let mut h = x.wrapping_mul(0x9E37_79B9).wrapping_add(z.wrapping_mul(0x85EB_CA77)); h ^= h >> 15; h = h.wrapping_mul(0x2C1B_3C6D); (h ^ (h >> 13)) & 0xFFFF } /// Rota un desplazamiento `(dx, dz)` según `facing` (giros de 90°). #[inline] fn rot(dx: i32, dz: i32, facing: u8) -> (i32, i32) { match facing & 3 { 0 => (dx, dz), 1 => (-dz, dx), 2 => (-dx, -dz), _ => (dz, -dx), } } /// **Detalle sub-voxel de un ser vivo** `(rugosidad, bisel)` — la gramática /// cuantizada que rompe la caja rectangular de los animales voxel. Rugosidad suave /// (grano de piel/pelaje, escala `VoxelGrid::DETALLE_RUG_MAX`) + bisel alto (redondeo /// orgánico de la silueta de sombreado): un ser vivo no es un ladrillo. Es la MISMA /// gramática de bisel/campo que el terreno (no una fidelidad distinta → sin valle), /// sólo con más redondeo por ser un cuerpo. `generate_window` lo aplica como pincel /// de detalle alrededor de cada `stamp_bicho`. El bisel sólo luce si el bisel GLOBAL /// de la escena está prendido (activa la isosuperficie); la rugosidad luce siempre. pub const DETALLE_SER_VIVO: (f32, f32) = (0.4, 0.85); /// **Estampa un ser** del `bicho` parado con las patas en `(x, y, z)` (base local /// `dy=0` = a ras del suelo), orientado por `facing`. `dz` crece hacia el frente /// (hocico), `dx` a lo ancho, `dy` hacia arriba. pub fn stamp_bicho(g: &mut VoxelGrid, bicho: Bicho, x: u32, y: u32, z: u32, facing: u8) { // Las especies del bestiario SDF caen en la estampa de su pariente voxel. let bicho = bicho.silueta_voxel(); let (bx, by, bz) = (x as i32, y as i32, z as i32); let dim = g.dim(); let vh = morph(x, z); // morfo de color/patrón por posición (variedad de rebaño) let mut put = |dx: i32, dy: i32, dz: i32, c: [u8; 3]| { let (rx, rz) = rot(dx, dz, facing); let (px, py, pz) = (bx + rx, by + dy, bz + rz); if px >= 0 && py >= 0 && pz >= 0 && (px as u32) < dim[0] && (py as u32) < dim[1] && (pz as u32) < dim[2] { g.set(px as u32, py as u32, pz as u32, c); } }; match bicho { Bicho::Vaca => { // Morfo de pelaje: holstein / marrón-blanca / negra. let (base, mancha_c) = match vh % 3 { 0 => (BLANCO, NEGRO), 1 => (BLANCO, MARRON_CLR), _ => (MARRON_CLR, NEGRO), }; // Cuerpo (2 de ancho × 3 de largo × 2 de alto) sobre 4 patas. for dx in -1i32..=0 { for dz in -1i32..=1 { for dy in 2..=3 { // Manchas en algunas celdas, base en el resto. let mancha = (dx + dz).rem_euclid(2) == 0 && dz != 1; put(dx, dy, dz, if mancha { mancha_c } else { base }); } } } for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] { put(lx, 0, lz, MARRON_OSC); put(lx, 1, lz, MARRON_OSC); } // Cabeza al frente + cuernos. put(0, 3, 2, ROSA); put(-1, 3, 2, ROSA); put(0, 4, 2, base); put(-1, 4, 2, base); put(1, 4, 2, [232, 228, 214]); // cuerno put(-2, 4, 2, [232, 228, 214]); // cuerno } Bicho::Oveja => { // Morfo de lana: blanca / beige / gris. let lana = match vh % 3 { 0 => BLANCO, 1 => [214, 202, 176], _ => [186, 186, 190], }; // Cuerpo lanudo y mullido sobre patas oscuras. for dx in -1..=0 { for dz in -1..=1 { for dy in 2..=3 { put(dx, dy, dz, lana); } } } put(0, 4, 0, lana); put(-1, 4, 0, lana); for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] { put(lx, 0, lz, NEGRO); put(lx, 1, lz, NEGRO); } // Cara oscura al frente. put(0, 2, 2, NEGRO); put(-1, 2, 2, NEGRO); put(0, 3, 2, lana); } Bicho::Cerdo => { // Cuerpo rosado rechoncho, patas cortas, hocico y colita. for dx in -1..=0 { for dz in -1..=1 { put(dx, 1, dz, ROSA); put(dx, 2, dz, ROSA); } } for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] { put(lx, 0, lz, ROSA); } put(0, 1, 2, ROSA); // hocico put(-1, 1, 2, ROSA); put(0, 2, 2, ROSA); put(-1, 2, 2, ROSA); put(0, 2, 3, [232, 170, 170]); // trompa put(-1, 1, -2, ROSA); // colita } Bicho::Gallina => { // Morfo de plumaje: blanca / marrón / negra (con gallo rojizo). let plum = match vh % 3 { 0 => BLANCO, 1 => MARRON_CLR, _ => [70, 68, 74], }; put(0, 1, 0, plum); put(0, 1, -1, plum); put(0, 2, 0, plum); put(0, 2, 1, plum); // pecho/cabeza put(0, 3, 1, ROJO); // cresta put(0, 2, 2, AMARILLO); // pico put(0, 0, 0, AMARILLO); // patas put(0, 0, -1, AMARILLO); put(0, 1, -2, plum); // cola } Bicho::Ciervo => { // Patas largas, cuerpo marrón, cuello y cabeza al frente, astas. for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] { put(lx, 0, lz, MARRON_OSC); put(lx, 1, lz, MARRON_OSC); } for dx in -1..=0 { for dz in -1..=1 { put(dx, 2, dz, MARRON); put(dx, 3, dz, MARRON); } } // Cuello + cabeza. put(0, 3, 2, MARRON); put(0, 4, 2, MARRON); put(0, 5, 2, MARRON); put(0, 5, 3, MARRON); // hocico // Astas. put(0, 6, 2, MARRON_OSC); put(1, 6, 2, MARRON_OSC); put(-1, 6, 2, MARRON_OSC); put(0, 6, 3, MARRON_OSC); } Bicho::Conejo => { // Morfo de pelaje: blanco / marrón / gris. let pelo = match vh % 3 { 0 => BLANCO, 1 => MARRON_CLR, _ => GRIS, }; // Cuerpo chico, orejas largas, cola de algodón. put(0, 1, 0, pelo); put(0, 1, -1, pelo); put(0, 2, 0, pelo); put(0, 2, 1, pelo); // cabeza al frente put(0, 3, 1, pelo); // oreja put(0, 4, 1, pelo); put(0, 3, 0, pelo); // oreja 2 put(0, 4, 0, pelo); put(0, 0, 0, pelo); put(0, 0, -1, pelo); put(0, 2, -1, [240, 240, 236]); // colita } Bicho::Zorro => { // Cuerpo naranja, pecho/cola blancos, orejas puntiagudas. for dz in -1..=1 { put(0, 1, dz, NARANJA); put(0, 2, dz, NARANJA); } put(0, 0, -1, MARRON_OSC); // patas put(0, 0, 1, MARRON_OSC); put(0, 2, 2, NARANJA); // cabeza put(0, 3, 2, NARANJA); // oreja put(0, 2, 3, BLANCO); // hocico put(0, 1, 2, BLANCO); // pecho put(0, 2, -2, BLANCO); // cola put(0, 1, -2, NARANJA); } Bicho::Lobo => { // Como el zorro pero gris y más largo/robusto. for dz in -2..=1 { put(0, 1, dz, GRIS); put(0, 2, dz, GRIS); } for &lz in &[-1, 1] { put(0, 0, lz, [96, 96, 102]); } put(0, 2, 2, GRIS); // cabeza put(0, 3, 2, GRIS); // oreja put(0, 2, 3, [110, 110, 116]); // hocico put(0, 2, -3, GRIS); // cola put(0, 3, -3, GRIS); } Bicho::Oso => { // Cuerpo voluminoso (2×3), cabeza redonda con orejas. for dx in -1..=0 { for dz in -1..=1 { for dy in 1..=3 { put(dx, dy, dz, MARRON_OSC); } } } for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] { put(lx, 0, lz, MARRON_OSC); } put(0, 3, 2, MARRON); // cabeza put(-1, 3, 2, MARRON); put(0, 4, 2, MARRON); put(-1, 4, 2, MARRON); put(1, 4, 2, MARRON_OSC); // orejas put(-2, 4, 2, MARRON_OSC); } Bicho::Caballo => { // Morfo de capa: alazán / negro / bayo, con crin de contraste. let (capa, crin) = match vh % 3 { 0 => (MARRON, MARRON_OSC), 1 => ([58, 54, 56], NEGRO), _ => (TAN, MARRON_OSC), }; // Alto: patas largas, lomo, cuello erguido, crin y cola. for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] { put(lx, 0, lz, crin); put(lx, 1, lz, capa); put(lx, 2, lz, capa); } for dx in -1..=0 { for dz in -1..=1 { put(dx, 3, dz, capa); } } // Cuello + cabeza. put(0, 4, 1, capa); put(0, 5, 1, capa); put(0, 5, 2, capa); // testa put(0, 4, 2, capa); put(0, 6, 1, crin); // crin put(0, 5, 0, crin); put(-1, 4, -1, crin); // cola put(-1, 3, -2, crin); } Bicho::Pajaro => { // Diminuto: cuerpo azul, alas, pico, patitas. put(0, 1, 0, AZUL); put(0, 2, 0, AZUL); // cabeza put(0, 2, 1, AMARILLO); // pico put(1, 1, 0, [96, 140, 210]); // ala put(-1, 1, 0, [96, 140, 210]); put(0, 1, -1, AZUL); // cola put(0, 0, 0, AMARILLO); // pata } Bicho::Pez => { // Cuerpo lateral + aleta caudal (se coloca en/junto al agua). put(0, 1, 0, AZUL); put(0, 1, 1, AZUL); put(0, 2, 0, [110, 150, 210]); put(0, 1, 2, [110, 150, 210]); // morro put(0, 1, -1, [150, 180, 220]); // cola put(0, 2, -1, [150, 180, 220]); put(0, 0, -1, [150, 180, 220]); } Bicho::Tortuga => { // Caparazón verde abovedado + cabeza + 4 patas. for dx in -1..=1 { for dz in -1..=1 { put(dx, 1, dz, VERDE_OSC); } } put(0, 2, 0, VERDE); // cúpula del caparazón put(0, 1, 2, [180, 200, 120]); // cabeza for &(lx, lz) in &[(-1, -1), (1, -1), (-1, 1), (1, 1)] { put(lx, 0, lz, [180, 200, 120]); // patas } } Bicho::Rana => { // Cuerpo verde agazapado, ojos saltones, patas dobladas. put(0, 1, 0, VERDE); put(-1, 1, 0, VERDE); put(0, 1, 1, VERDE); // frente put(-1, 1, 1, VERDE); put(0, 2, 1, VERDE_OSC); // ojo put(-1, 2, 1, VERDE_OSC); put(0, 0, -1, VERDE); // pata trasera put(-1, 0, -1, VERDE); put(0, 0, 2, [200, 210, 140]); // pata delantera } Bicho::Serpiente => { // Cuerpo largo y bajo en zigzag + cabeza. let cuerpo = [(0, 0), (0, -1), (-1, -1), (-1, -2), (-2, -2), (-2, -3)]; for &(sx, sz) in &cuerpo { put(sx, 1, sz, VERDE_OSC); } put(0, 1, 1, VERDE); // cabeza put(0, 1, 2, VERDE); put(0, 2, 1, ROJO); // lengua/ojo } Bicho::Mariposa => { // Morfo de alas: naranja monarca / azul / amarilla. let (ala1, ala2) = match vh % 3 { 0 => (AMARILLO, NARANJA), 1 => (AZUL, [140, 180, 230]), _ => (AMARILLO, [230, 170, 60]), }; put(0, 3, 0, NEGRO); // cuerpo put(0, 2, 0, NEGRO); put(1, 3, 0, ala1); // alas put(1, 3, 1, ala2); put(-1, 3, 0, ala1); put(-1, 3, 1, ala2); put(1, 2, 0, ala2); put(-1, 2, 0, ala2); } // ---- Granja / pradera ---- Bicho::Pato => { // Cuerpo blanco chico, cuello, pico naranja, cola. put(0, 1, 0, BLANCO); put(0, 1, -1, BLANCO); put(-1, 1, 0, BLANCO); put(0, 2, 1, BLANCO); // cuello/cabeza put(0, 3, 1, BLANCO); put(0, 3, 2, NARANJA); // pico put(0, 2, -1, BLANCO); // cola put(0, 0, 0, NARANJA); // patas } Bicho::Cabra => { // Morfo: blanca / parda. Cuerpo, patas, cabeza barbada y cuernos. let capa = if vh % 2 == 0 { [222, 216, 202] } else { MARRON_CLR }; for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] { put(lx, 0, lz, MARRON_OSC); put(lx, 1, lz, capa); } for dx in -1..=0 { for dz in -1..=1 { put(dx, 2, dz, capa); } } put(0, 2, 2, capa); // cabeza put(0, 3, 2, capa); put(0, 2, 3, capa); // hocico put(0, 4, 2, [232, 228, 214]); // cuerno put(0, 1, 2, [220, 214, 200]); // barbita put(-1, 2, -2, capa); // cola corta } Bicho::Burro => { // Gris, patas, lomo, cuello y orejas largas. for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] { put(lx, 0, lz, GRIS_OSC); put(lx, 1, lz, GRIS); } for dx in -1..=0 { for dz in -1..=1 { put(dx, 2, dz, GRIS); } } put(0, 3, 1, GRIS); // cuello put(0, 3, 2, GRIS); // cabeza put(0, 4, 2, GRIS); // oreja larga put(0, 5, 2, GRIS_OSC); put(-1, 2, -2, GRIS_OSC); // cola } // ---- Bosque ---- Bicho::Ardilla => { // Cuerpito naranja con cola tupida enroscada hacia arriba. put(0, 1, 0, NARANJA); put(0, 2, 0, NARANJA); put(0, 2, 1, NARANJA); // cabeza put(0, 3, 1, [222, 150, 90]); // oreja put(0, 1, 1, [232, 190, 150]); // pecho claro put(0, 0, 0, MARRON_OSC); // pata put(0, 2, -1, NARANJA); // cola put(0, 3, -1, [222, 150, 90]); put(0, 4, -1, NARANJA); put(0, 4, 0, [222, 150, 90]); } Bicho::Buho => { // Compacto, posado, con ojos grandes y penachos. for dy in 1..=3 { put(0, dy, 0, MARRON); put(-1, dy, 0, MARRON); } put(0, 3, 1, AMARILLO); // ojo put(-1, 3, 1, AMARILLO); put(0, 4, 0, MARRON_OSC); // penacho put(-1, 4, 0, MARRON_OSC); put(0, 2, 1, [206, 180, 140]); // pecho put(-1, 2, 1, [206, 180, 140]); put(0, 0, 0, AMARILLO); // garras put(-1, 0, 0, AMARILLO); } Bicho::Erizo => { // Cuerpo bajo con lomo de púas oscuras y carita clara. for dx in -1..=0 { for dz in -1..=1 { put(dx, 1, dz, MARRON_OSC); put(dx, 2, dz, [64, 52, 40]); // púas } } put(0, 2, 2, [222, 198, 160]); // carita put(-1, 2, 2, [222, 198, 160]); put(0, 1, 3, NEGRO); // hocico for &(lx, lz) in &[(-1, -1), (0, 1)] { put(lx, 0, lz, [120, 96, 72]); } } // ---- Desierto ---- Bicho::Camello => { // Color arena, patas largas, joroba, cuello y cabeza. for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] { put(lx, 0, lz, [176, 148, 104]); put(lx, 1, lz, TAN); put(lx, 2, lz, TAN); } for dx in -1..=0 { for dz in -1..=1 { put(dx, 3, dz, TAN); } } put(0, 4, 0, TAN); // joroba put(-1, 4, 0, TAN); put(0, 4, 1, TAN); // cuello put(0, 5, 2, TAN); // cabeza put(0, 5, 3, [176, 148, 104]); // hocico put(-1, 3, -2, TAN); // cola } Bicho::Escorpion => { // Cuerpo bajo oscuro, pinzas al frente, cola curva con aguijón. put(0, 1, 0, [70, 58, 40]); put(0, 1, 1, [70, 58, 40]); put(0, 1, 2, [86, 70, 48]); // pinzas put(1, 1, 2, [86, 70, 48]); put(-1, 1, 2, [86, 70, 48]); put(0, 1, -1, [70, 58, 40]); // cola put(0, 2, -1, [70, 58, 40]); put(0, 2, 0, [70, 58, 40]); put(0, 3, 0, ROJO); // aguijón } Bicho::Lagarto => { // Cuerpo verde alargado y bajo, patitas, cabeza, cola larga. for dz in -2..=1 { put(0, 1, dz, VERDE_OSC); } put(0, 1, 2, VERDE); // cabeza put(0, 1, -3, VERDE_OSC); // cola put(0, 1, -4, [80, 130, 66]); put(1, 0, 0, VERDE_OSC); // patas put(-1, 0, 0, VERDE_OSC); put(1, 0, -1, VERDE_OSC); put(-1, 0, -1, VERDE_OSC); } // ---- Nieve / agua fría ---- Bicho::Pinguino => { // De pie: espalda negra, panza blanca, pico y patas naranjas. for dy in 1..=3 { put(0, dy, 0, NEGRO); // espalda } put(0, 1, 1, BLANCO); // panza put(0, 2, 1, BLANCO); put(0, 4, 0, NEGRO); // cabeza put(0, 4, 1, NARANJA); // pico put(1, 2, 0, NEGRO); // aleta put(-1, 2, 0, NEGRO); put(0, 0, 1, NARANJA); // patas } Bicho::Foca => { // Cuerpo gris tendido, cabeza levantada, aletas. for dz in -2..=1 { put(0, 1, dz, GRIS); put(-1, 1, dz, GRIS); } put(0, 2, 1, GRIS); // cabeza put(-1, 2, 1, GRIS); put(0, 2, 2, [110, 112, 118]); // hocico put(0, 1, -3, [110, 112, 118]); // cola/aleta put(-1, 1, -3, [110, 112, 118]); put(1, 1, 0, [150, 150, 156]); // aleta lateral } // ---- Selva / sabana ---- Bicho::Mono => { // Cuerpo marrón sentado, cabeza clara, cola larga curva. put(0, 1, 0, MARRON); put(0, 2, 0, MARRON); put(0, 2, 1, [176, 140, 100]); // cara clara put(0, 3, 1, MARRON); // frente put(0, 1, 1, MARRON); // rodillas put(1, 2, 0, MARRON); // brazo put(-1, 2, 0, MARRON); put(0, 1, -1, MARRON); // cola put(0, 2, -1, MARRON); put(0, 2, -2, MARRON); } Bicho::Loro => { // Cuerpo verde vivo, pecho rojo, pico y cola larga. put(0, 1, 0, [70, 168, 72]); put(0, 2, 0, [70, 168, 72]); put(0, 3, 0, [70, 168, 72]); // cabeza put(0, 3, 1, ROJO); // pico put(0, 1, 1, ROJO); // pecho put(1, 2, 0, [90, 190, 90]); // ala put(0, 1, -1, [220, 200, 60]); // cola put(0, 1, -2, [70, 168, 72]); put(0, 0, 0, [120, 96, 72]); // pata } Bicho::Tigre => { // Cuerpo naranja con rayas negras, patas, cabeza, cola. for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] { put(lx, 0, lz, NARANJA); } for dx in -1i32..=0 { for dz in -1i32..=1 { // Rayas: bandas negras alternas a lo largo. let raya = dz.rem_euclid(2) == 0; put(dx, 1, dz, if raya { NEGRO } else { NARANJA }); } } put(0, 2, 2, NARANJA); // cabeza put(-1, 2, 2, NARANJA); put(0, 1, 2, [232, 200, 150]); // hocico put(0, 2, -2, NARANJA); // cola put(0, 2, -3, NEGRO); } Bicho::Elefante => { // Grande: cuerpo gris (2×3×2), patas, trompa, orejas, colmillos. for dx in -1i32..=0 { for dz in -1..=1 { for dy in 2..=3 { put(dx, dy, dz, GRIS); } } } for &(lx, lz) in &[(-1, -1), (0, -1), (-1, 1), (0, 1)] { put(lx, 0, lz, [110, 112, 118]); put(lx, 1, lz, GRIS); } put(0, 3, 2, GRIS); // cabeza put(-1, 3, 2, GRIS); put(0, 2, 2, GRIS); put(0, 1, 3, GRIS); // trompa put(0, 0, 3, [110, 112, 118]); put(1, 3, 1, [150, 150, 156]); // oreja put(-2, 3, 1, [150, 150, 156]); put(0, 1, 2, [232, 228, 214]); // colmillo } Bicho::Cocodrilo => { // Cuerpo verde largo y bajo, dorso escamado, fauces, cola. for dz in -3..=2 { put(0, 1, dz, VERDE_OSC); put(-1, 1, dz, VERDE_OSC); } // Dorso escamado (crestas alternas). for dz in -3i32..=1 { if dz.rem_euclid(2) == 0 { put(0, 2, dz, [72, 128, 60]); } } put(0, 1, 3, VERDE); // hocico put(-1, 1, 3, VERDE); put(0, 2, 3, NEGRO); // ojo/fauces put(0, 1, -4, VERDE_OSC); // cola put(0, 1, -5, [80, 130, 66]); put(1, 0, 0, VERDE_OSC); // patas put(-2, 0, 0, VERDE_OSC); } // Inalcanzable por construcción: `silueta_voxel` (arriba, primera línea de // esta función) manda toda especie del bestiario SDF a la estampa de un // pariente, y las 32 originales tienen la suya. Lo fija el test // `cada_bicho_estampa_voxels`, que recorre `Bicho::ALL` y exige voxels: una // especie nueva sin remapeo cae acá, no estampa nada, y el test se pone // rojo — que es mejor que un `unreachable!` reventando un mundo entero. _ => {} } } #[cfg(test)] mod tests { use super::*; fn solids(g: &VoxelGrid, dim: [u32; 3]) -> usize { let mut n = 0; for x in 0..dim[0] { for y in 0..dim[1] { for z in 0..dim[2] { if g.is_solid(x as i32, y as i32, z as i32) { n += 1; } } } } n } #[test] fn cada_bicho_estampa_voxels() { let dim = [20u32, 16, 20]; for bicho in Bicho::ALL { let mut g = VoxelGrid::new(dim); stamp_bicho(&mut g, bicho, 10, 1, 10, 0); assert!(solids(&g, dim) >= 5, "{} estampó muy poco", bicho.label()); } } #[test] fn facing_rota_la_silueta() { let dim = [24u32, 16, 24]; let mut a = VoxelGrid::new(dim); let mut b = VoxelGrid::new(dim); stamp_bicho(&mut a, Bicho::Ciervo, 12, 1, 12, 0); stamp_bicho(&mut b, Bicho::Ciervo, 12, 1, 12, 1); // Al rotar, la ocupación cambia (el hocico apunta a otro eje). let mut diff = 0; for x in 0..dim[0] { for y in 0..dim[1] { for z in 0..dim[2] { 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) { diff += 1; } } } } assert!(diff > 0, "facing no rotó la silueta"); } }