game_toolkit_platformer/sim/spot.rs
1//! [`Spot`]: where to put the character, by its hitbox's top-left or by its feet.
2//!
3//! Every placement takes one ([`World::new`](super::World::new), the `Platformer`'s spawn,
4//! respawn, teleport and checkpoint), so feet and top-left can't be mixed up. Tiles are not
5//! a spot: a [`Level`](super::Level) turns its spawn tile into one with
6//! [`Level::start`](super::Level::start).
7//!
8//! A spot is in the terrain's space: units, or your pixels with a `Builder::scale`.
9//!
10//! ```
11//! use game_toolkit_platformer::sim::Spot;
12//!
13//! // A 6x12 body standing on the ground at y = 100, centered on x = 50.
14//! assert_eq!(Spot::Feet(50, 100).top_left((6, 12)), (47, 88));
15//! assert_eq!(Spot::TopLeft(47, 88).top_left((6, 12)), (47, 88));
16//! ```
17
18/// A place for the character's body.
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum Spot {
21 /// The hitbox's top-left corner at `x, y`.
22 TopLeft(i32, i32),
23 /// Standing on the ground at height `floor_y` (the ground's top edge), centered on `x`:
24 /// `Feet(x, floor_y)`. The way to put it on a terrain of your own.
25 Feet(i32, i32),
26}
27
28impl Spot {
29 /// The hitbox's top-left for a body of `(w, h)` at this spot.
30 pub fn top_left(self, (w, h): (i32, i32)) -> (i32, i32) {
31 match self {
32 Spot::TopLeft(x, y) => (x, y),
33 Spot::Feet(x, floor_y) => (x - (w + 1) / 2, floor_y - h),
34 }
35 }
36
37 /// This spot given in `k` pixels per unit, in units (rounding down).
38 pub fn in_units(self, k: i32) -> Spot {
39 let d = |v: i32| v.div_euclid(k);
40 match self {
41 Spot::TopLeft(x, y) => Spot::TopLeft(d(x), d(y)),
42 Spot::Feet(x, y) => Spot::Feet(d(x), d(y)),
43 }
44 }
45
46 /// This spot given in units, in `k` pixels per unit.
47 pub fn in_pixels(self, k: i32) -> Spot {
48 match self {
49 Spot::TopLeft(x, y) => Spot::TopLeft(x * k, y * k),
50 Spot::Feet(x, y) => Spot::Feet(x * k, y * k),
51 }
52 }
53}