Skip to main content

terrain_forge/compose/
layer.rs

1//! Layered generation with blend modes
2
3use crate::grid::Cell;
4use crate::{Algorithm, Grid};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
8pub enum BlendMode {
9    /// Replace existing tiles.
10    Replace,
11    /// Union (OR) of floor tiles.
12    Union,
13    /// Intersection (AND) of floor tiles.
14    Intersect,
15    /// Difference — floor only where first has floor and second has wall.
16    Difference,
17    /// Mask — keep first layer only where second is floor.
18    Mask,
19}
20
21/// Layered generator that blends multiple algorithms.
22///
23/// Generic over `C: Cell`, so it works with both [`Tile`](crate::Tile) and custom cell types.
24pub struct LayeredGenerator<C: Cell = crate::Tile> {
25    layers: Vec<(Box<dyn Algorithm<C> + Send + Sync>, BlendMode)>,
26}
27
28impl<C: Cell> LayeredGenerator<C> {
29    /// Creates an empty layered generator.
30    pub fn new() -> Self {
31        Self { layers: Vec::new() }
32    }
33
34    /// Sets the base layer (replaces).
35    pub fn base<A: Algorithm<C> + Send + Sync + 'static>(mut self, algo: A) -> Self {
36        self.layers.push((Box::new(algo), BlendMode::Replace));
37        self
38    }
39
40    /// Adds a union layer.
41    pub fn union<A: Algorithm<C> + Send + Sync + 'static>(mut self, algo: A) -> Self {
42        self.layers.push((Box::new(algo), BlendMode::Union));
43        self
44    }
45
46    /// Adds an intersection layer.
47    pub fn intersect<A: Algorithm<C> + Send + Sync + 'static>(mut self, algo: A) -> Self {
48        self.layers.push((Box::new(algo), BlendMode::Intersect));
49        self
50    }
51
52    /// Adds a difference layer.
53    pub fn difference<A: Algorithm<C> + Send + Sync + 'static>(mut self, algo: A) -> Self {
54        self.layers.push((Box::new(algo), BlendMode::Difference));
55        self
56    }
57
58    /// Adds a layer with the specified blend mode.
59    pub fn add<A: Algorithm<C> + Send + Sync + 'static>(
60        mut self,
61        algo: A,
62        mode: BlendMode,
63    ) -> Self {
64        self.layers.push((Box::new(algo), mode));
65        self
66    }
67}
68
69impl<C: Cell> Default for LayeredGenerator<C> {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl<C: Cell + 'static> Algorithm<C> for LayeredGenerator<C> {
76    fn generate(&self, grid: &mut Grid<C>, seed: u64) {
77        for (i, (algo, mode)) in self.layers.iter().enumerate() {
78            let layer_seed = seed.wrapping_add(i as u64 * 1000);
79
80            match mode {
81                BlendMode::Replace => {
82                    algo.generate(grid, layer_seed);
83                }
84                BlendMode::Union => {
85                    let mut layer = Grid::new(grid.width(), grid.height());
86                    algo.generate(&mut layer, layer_seed);
87                    for y in 0..grid.height() {
88                        for x in 0..grid.width() {
89                            if layer[(x, y)].is_passable() {
90                                grid[(x, y)].set_passable();
91                            }
92                        }
93                    }
94                }
95                BlendMode::Intersect => {
96                    let mut layer = Grid::new(grid.width(), grid.height());
97                    algo.generate(&mut layer, layer_seed);
98                    for y in 0..grid.height() {
99                        for x in 0..grid.width() {
100                            if !layer[(x, y)].is_passable() {
101                                grid.set(x as i32, y as i32, C::default());
102                            }
103                        }
104                    }
105                }
106                BlendMode::Difference => {
107                    let mut layer = Grid::new(grid.width(), grid.height());
108                    algo.generate(&mut layer, layer_seed);
109                    for y in 0..grid.height() {
110                        for x in 0..grid.width() {
111                            if layer[(x, y)].is_passable() {
112                                grid.set(x as i32, y as i32, C::default());
113                            }
114                        }
115                    }
116                }
117                BlendMode::Mask => {
118                    let mut mask = Grid::new(grid.width(), grid.height());
119                    algo.generate(&mut mask, layer_seed);
120                    for y in 0..grid.height() {
121                        for x in 0..grid.width() {
122                            if !mask[(x, y)].is_passable() {
123                                grid.set(x as i32, y as i32, C::default());
124                            }
125                        }
126                    }
127                }
128            }
129        }
130    }
131
132    fn name(&self) -> &'static str {
133        "LayeredGenerator"
134    }
135}