Skip to main content

pinch_points/app/
layout.rs

1//! Grid → world-space mapping. The board is centred on the origin; sim `y`
2//! grows downward (row 0 on top) while Bevy's world `y` grows upward.
3
4use crate::sim::{Board, Direction};
5use bevy::prelude::*;
6
7pub const TILE: f32 = 64.0;
8
9/// Z-layers, back to front.
10pub mod z {
11    pub const SAND: f32 = 0.0;
12    /// Damp fringe around the board, above sand.
13    pub const WET: f32 = 0.2;
14    /// Pool water sits under features and wading creatures.
15    pub const POOL: f32 = 0.5;
16    pub const TILE_FEATURE: f32 = 1.0;
17    pub const SIGNPOST: f32 = 2.0;
18    pub const CREATURE: f32 = 3.0;
19    pub const WALL: f32 = 4.0;
20    /// The tide water bars.
21    pub const WATER: f32 = 4.5;
22    /// The foam lip riding the water's inner edge.
23    pub const FOAM: f32 = 4.6;
24    pub const CURSOR: f32 = 5.0;
25    /// Confetti under the flash/pip layers.
26    pub const CONFETTI: f32 = 5.4;
27    /// Raid flash overlay.
28    pub const FLASH: f32 = 5.45;
29    /// Burst/glint particles.
30    pub const PARTICLE: f32 = 5.5;
31    /// Floating score text tops everything on the board.
32    pub const PIP: f32 = 5.7;
33}
34
35pub fn tile_center(board: &Board, x: u8, y: u8) -> Vec2 {
36    let w = f32::from(board.width());
37    let h = f32::from(board.height());
38    Vec2::new(
39        (f32::from(x) - (w - 1.0) / 2.0) * TILE,
40        ((h - 1.0) / 2.0 - f32::from(y)) * TILE,
41    )
42}
43
44/// The tile whose centre is closest to a world position (clamped in-board).
45pub fn nearest_tile(board: &Board, pos: Vec2) -> (u8, u8) {
46    let w = f32::from(board.width());
47    let h = f32::from(board.height());
48    let x = (pos.x / TILE + (w - 1.0) / 2.0).round().clamp(0.0, w - 1.0);
49    let y = ((h - 1.0) / 2.0 - pos.y / TILE).round().clamp(0.0, h - 1.0);
50    (x as u8, y as u8)
51}
52
53/// World position of a creature `progress` subunits past the centre of `tile`,
54/// heading in `dir`.
55pub fn creature_pos(board: &Board, tile: u16, dir: Direction, progress: u16) -> Vec2 {
56    let (x, y) = board.coords_u8(tile);
57    let (dx, dy) = dir.offset();
58    let t = f32::from(progress) / f32::from(crate::sim::SUBUNITS_PER_TILE) * TILE;
59    // Flip dy: sim y-down, world y-up.
60    tile_center(board, x, y) + Vec2::new(dx as f32 * t, -dy as f32 * t)
61}
62
63/// Rotation aligning a sprite authored facing +X with a sim direction.
64pub fn dir_rotation(dir: Direction) -> Quat {
65    let angle = match dir {
66        Direction::Right => 0.0,
67        Direction::Up => std::f32::consts::FRAC_PI_2,
68        Direction::Left => std::f32::consts::PI,
69        Direction::Down => -std::f32::consts::FRAC_PI_2,
70    };
71    Quat::from_rotation_z(angle)
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn tile_centers_round_trip_through_nearest_tile() {
80        let board = crate::sim::Board::new(12, 9, 1);
81        for y in 0..board.height() {
82            for x in 0..board.width() {
83                let pos = tile_center(&board, x, y);
84                assert_eq!(nearest_tile(&board, pos), (x, y));
85            }
86        }
87    }
88
89    #[test]
90    fn creature_pos_at_rest_is_the_tile_center() {
91        let board = crate::sim::Board::new(7, 5, 1);
92        for tile in 0..(7 * 5u16) {
93            let (x, y) = board.coords_u8(tile);
94            assert_eq!(
95                creature_pos(&board, tile, crate::sim::Direction::Right, 0),
96                tile_center(&board, x, y)
97            );
98        }
99    }
100
101    use crate::sim::Board;
102
103    #[test]
104    fn tile_centers_are_symmetric_about_the_board_middle() {
105        let board = Board::new(5, 3, 0);
106        let a = tile_center(&board, 0, 0);
107        let b = tile_center(&board, 4, 2);
108        assert_eq!(a, -b, "opposite corners mirror through the origin");
109        assert_eq!(tile_center(&board, 2, 1), Vec2::ZERO, "centre tile is 0,0");
110    }
111}