Skip to main content
Verticopolis engineGitHub

verticopolis_engine/
brush.rs

1//! The floor and lobby brush: [`Simulation::paint_brush`] lays a run of
2//! floor or lobby tiles in one command.
3
4use crate::charges::INVALID_REQUEST;
5use crate::facilities::Kind;
6use crate::sim::Simulation;
7
8/// The most tiles one brush may carry.
9pub const BRUSH_TILE_LIMIT: usize = 32;
10
11/// What a brush laid.
12#[derive(Clone, Debug, PartialEq)]
13pub struct BrushResult {
14    /// How many tiles it laid.
15    pub placed: i64,
16    /// The last refusal one of its tiles got, if any did.
17    pub reason: Option<String>,
18}
19
20impl BrushResult {
21    #[doc(hidden)]
22    pub fn invalid() -> BrushResult {
23        BrushResult {
24            placed: 0,
25            reason: Some(INVALID_REQUEST.into()),
26        }
27    }
28}
29
30impl Simulation {
31    /// Lays `kind` (floor or lobby) on each tile column in `tiles` on
32    /// `floor`, charging as that many [`build`](Simulation::build) calls
33    /// would, in one command. It skips a tile that already carries `kind`,
34    /// and tries a refused tile again once a neighbor it needed is laid, so
35    /// the order of `tiles` does not matter.
36    ///
37    /// A lobby brush also turns plain floor into lobby (a sky lobby). Any
38    /// other kind, or more than [`BRUSH_TILE_LIMIT`] tiles, lays nothing and
39    /// answers `INVALID_REQUEST`.
40    ///
41    /// ```
42    /// use verticopolis_engine::clock::GameMode;
43    /// use verticopolis_engine::facilities::Kind;
44    /// use verticopolis_engine::sim::Simulation;
45    ///
46    /// let mut sim = Simulation::new_game(1, GameMode::Classic);
47    /// let tiles: Vec<i64> = (100..124).collect();
48    /// let laid = sim.paint_brush(Kind::Lobby, 1, &tiles);
49    /// assert_eq!(laid.placed, 24);
50    /// ```
51    pub fn paint_brush(&mut self, kind: Kind, floor: i64, tiles: &[i64]) -> BrushResult {
52        if !matches!(kind, Kind::Floor | Kind::Lobby) || tiles.len() > BRUSH_TILE_LIMIT {
53            return BrushResult::invalid();
54        }
55        self.with_lobby_upgrades(|sim| {
56            let mut placed = 0;
57            let mut reason = None;
58            let mut progress = true;
59            while progress {
60                progress = false;
61                for &tx in tiles {
62                    let existing = sim.tower.structure_kind_at(floor, tx);
63                    if existing == Some(kind) || (existing.is_some() && kind != Kind::Lobby) {
64                        continue;
65                    }
66                    let r = sim.build(kind, floor, tx);
67                    if r.ok {
68                        placed += 1;
69                        progress = true;
70                    } else {
71                        reason = r.reason;
72                    }
73                }
74            }
75            BrushResult { placed, reason }
76        })
77    }
78}