new layer model

This commit is contained in:
2026-07-10 23:16:28 -05:00
parent 325d2c27dd
commit ad95a9cd8d
36 changed files with 1040 additions and 897 deletions
+67 -1
View File
@@ -12,7 +12,63 @@
use crate::glyph::Glyph;
use color::Rgba8;
use tinyrand::{Probability, Rand, StdRand};
use tinyrand::{Probability, Rand, Seeded, StdRand};
/// A board's floor: the cosmetic backdrop drawn beneath everything, replacing the
/// old dedicated floor *layer*. A board has exactly one [`Floor`] (see
/// [`Board::floor`](crate::board::Board)), given in the map file's `[map]` header
/// as an optional `floor = { … }` attribute.
///
/// Three forms: [`Blank`](Floor::Blank) (the canonical empty/black cell shows
/// through), [`Fixed`](Floor::Fixed) (one glyph tiled across the whole board), or
/// [`Biome`](Floor::Biome) (a procedural [`FloorGenerator`] texture). A biome keeps
/// its generator (so save re-emits the generator name) alongside a per-cell glyph
/// buffer pre-rolled once at load from [`FLOOR_SEED`] — deterministic, and the
/// direct replacement for the old per-cell floor-layer rolling.
#[derive(Clone)]
pub enum Floor {
/// No floor: the canonical black empty cell shows.
Blank,
/// A single fixed glyph tiled across the whole board.
Fixed(Glyph),
/// A procedural biome floor: the `generator` (retained for save) plus a
/// `glyphs` buffer holding one pre-rolled glyph per cell (row-major).
Biome {
/// The generator this biome was built from; re-emitted on save.
generator: FloorGenerator,
/// One pre-rolled glyph per cell (`width * height`, row-major).
glyphs: Vec<Glyph>,
},
}
impl Default for Floor {
/// A board with no declared floor is [`Floor::Blank`].
fn default() -> Self {
Floor::Blank
}
}
impl Floor {
/// Builds a [`Floor::Biome`] for a `width × height` board, pre-rolling one glyph
/// per cell from a [`FLOOR_SEED`]-seeded PRNG (so the result is deterministic and
/// depends only on the board dimensions + generator).
pub(crate) fn biome(generator: FloorGenerator, width: usize, height: usize) -> Floor {
let mut rng = StdRand::seed(FLOOR_SEED);
let glyphs = (0..width * height).map(|_| generator.generate(&mut rng)).collect();
Floor::Biome { generator, glyphs }
}
/// The floor glyph to draw at `(x, y)`, or `None` for [`Floor::Blank`].
///
/// `width` is the board width, needed to index a biome's row-major buffer.
pub(crate) fn glyph_at(&self, x: usize, y: usize, width: usize) -> Option<Glyph> {
match self {
Floor::Blank => None,
Floor::Fixed(g) => Some(*g),
Floor::Biome { glyphs, .. } => glyphs.get(y * width + x).copied(),
}
}
}
/// Fixed seed for the floor PRNG, so a board's generated floor is deterministic
/// for a given map (stable across reloads within a run, and testable). The layer
@@ -46,6 +102,16 @@ impl FloorGenerator {
}
}
/// The generator's map-file name (inverse of [`from_name`](FloorGenerator::from_name)),
/// re-emitted on save so a biome floor round-trips.
pub fn name(&self) -> &'static str {
match self {
FloorGenerator::Grass => "grass",
FloorGenerator::Dirt => "dirt",
FloorGenerator::Stone => "stone",
}
}
/// Generates one random floor [`Glyph`] for this generator.
///
/// Picks a background ground color within the generator's scheme, then with