Skip to main content

dotzuki_engine/overworld/
map_transitions.rs

1use crate::map::MapTrait;
2use crate::overworld::types::{Direction, MapData};
3use crate::tileset::TilesetTrait;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct ConnectionTransition<M: MapTrait> {
7    pub new_map: M,
8    pub new_x: u16,
9    pub new_y: u16,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct WarpTransition<M: MapTrait> {
14    pub new_map: M,
15    pub dest_warp_id: u8,
16    pub is_last_map: bool,
17}
18
19/// Provider trait for map data needed by transition calculations.
20///
21/// This trait allows the transition functions to resolve map names
22/// to typed IDs and query map dimensions without depending on any
23/// specific game's data loading mechanism.
24pub trait MapTransitionProvider<M: MapTrait> {
25    /// Resolve a map name string to a typed map ID.
26    fn resolve_map_id(&self, name: &str) -> Option<M>;
27
28    /// Get the dimensions `(width, height)` of a map in blocks.
29    fn get_map_dimensions(&self, map: M) -> (u8, u8);
30}
31
32/// Calculate the transition when the player walks across a map boundary
33/// (connection). Returns the new map and the player's position within it.
34pub fn calculate_connection_transition<P, M, T, Mus>(
35    map_data: &MapData<M, T, Mus>,
36    provider: &P,
37    px: u16,
38    py: u16,
39    direction: Direction,
40) -> Option<ConnectionTransition<M>>
41where
42    M: MapTrait,
43    T: TilesetTrait,
44    P: MapTransitionProvider<M>,
45{
46    let conns = &map_data.connections;
47    let current_w = map_data.width as u16 * 2;
48    let current_h = map_data.height as u16 * 2;
49
50    match direction {
51        Direction::Up => {
52            if py != 0 {
53                return None;
54            }
55            let conn = conns.north.as_ref()?;
56            let (_, dest_h) = provider.get_map_dimensions(conn.target_map);
57            let new_y = (dest_h as u16) * 2 - 1;
58            let new_x = apply_offset(px, conn.offset);
59            Some(ConnectionTransition {
60                new_map: conn.target_map,
61                new_x,
62                new_y,
63            })
64        }
65        Direction::Down => {
66            if py != current_h - 1 {
67                return None;
68            }
69            let conn = conns.south.as_ref()?;
70            let new_y = 0;
71            let new_x = apply_offset(px, conn.offset);
72            Some(ConnectionTransition {
73                new_map: conn.target_map,
74                new_x,
75                new_y,
76            })
77        }
78        Direction::Left => {
79            if px != 0 {
80                return None;
81            }
82            let conn = conns.west.as_ref()?;
83            let (dest_w, _) = provider.get_map_dimensions(conn.target_map);
84            let new_x = (dest_w as u16) * 2 - 1;
85            let new_y = apply_offset(py, conn.offset);
86            Some(ConnectionTransition {
87                new_map: conn.target_map,
88                new_x,
89                new_y,
90            })
91        }
92        Direction::Right => {
93            if px != current_w - 1 {
94                return None;
95            }
96            let conn = conns.east.as_ref()?;
97            let new_x = 0;
98            let new_y = apply_offset(py, conn.offset);
99            Some(ConnectionTransition {
100                new_map: conn.target_map,
101                new_x,
102                new_y,
103            })
104        }
105    }
106}
107
108fn apply_offset(coord: u16, offset: i8) -> u16 {
109    let adjusted = coord as i32 - (offset as i32 * 2);
110    adjusted.max(0) as u16
111}
112
113/// Check if the player is standing on a warp point and return the
114/// transition info (target map, warp ID, and whether it's a last-map warp).
115pub fn check_warp_at<M: MapTrait, T: TilesetTrait, Mus>(
116    map_data: &MapData<M, T, Mus>,
117    px: u8,
118    py: u8,
119) -> Option<WarpTransition<M>> {
120    for warp in &map_data.warps {
121        if px == warp.x && py == warp.y {
122            return Some(WarpTransition {
123                new_map: warp.target_map,
124                dest_warp_id: warp.target_warp_id,
125                is_last_map: warp.is_last_map,
126            });
127        }
128    }
129    None
130}