flatland_presentation/
directional.rs1pub const PLAYER_DIRECTIONAL_BASE_MODES: &[&str] = &["idle", "walk", "run"];
5
6pub fn cardinal_facing_label(yaw: f32) -> &'static str {
10 eight_way_facing_label(yaw)
11}
12
13pub fn eight_way_facing_label(yaw: f32) -> &'static str {
15 let deg = yaw.to_degrees().rem_euclid(360.0);
16 if (22.5..67.5).contains(°) {
18 "northeast"
19 } else if (67.5..112.5).contains(°) {
20 "east"
21 } else if (112.5..157.5).contains(°) {
22 "southeast"
23 } else if (157.5..202.5).contains(°) {
24 "south"
25 } else if (202.5..247.5).contains(°) {
26 "southwest"
27 } else if (247.5..292.5).contains(°) {
28 "west"
29 } else if (292.5..337.5).contains(°) {
30 "northwest"
31 } else {
32 "north"
33 }
34}
35
36pub fn four_way_facing_label(yaw: f32) -> &'static str {
38 let deg = yaw.to_degrees().rem_euclid(360.0);
39 if (45.0..135.0).contains(°) {
40 "east"
41 } else if (135.0..225.0).contains(°) {
42 "south"
43 } else if (225.0..315.0).contains(°) {
44 "west"
45 } else {
46 "north"
47 }
48}
49
50pub fn base_sprite_mode(mode: &str) -> &str {
53 for suffix in [
54 "_northeast",
55 "_northwest",
56 "_southeast",
57 "_southwest",
58 "_north",
59 "_east",
60 "_south",
61 "_west",
62 ] {
63 if let Some(base) = mode.strip_suffix(suffix) {
64 return base;
65 }
66 }
67 mode
68}
69
70pub fn sheet_has_mode(sheet_modes: &[String], mode: &str) -> bool {
72 if sheet_modes.iter().any(|m| m == mode) {
73 return true;
74 }
75 let prefix = format!("{mode}_");
76 sheet_modes.iter().any(|m| m.starts_with(&prefix))
77}
78
79pub fn expand_directional_draw_mode(
82 sheet_modes: &[String],
83 base_mode: &str,
84 facing_yaw: f32,
85) -> String {
86 let base = base_sprite_mode(base_mode);
87 let eight = eight_way_facing_label(facing_yaw);
88 let four = four_way_facing_label(facing_yaw);
89 for label in [eight, four] {
90 let directional = format!("{base}_{label}");
91 if sheet_has_mode(sheet_modes, &directional) {
92 return directional;
93 }
94 }
95 if sheet_has_mode(sheet_modes, base) {
96 return base.to_string();
97 }
98 for label in [eight, four] {
100 let idle_dir = format!("idle_{label}");
101 if sheet_has_mode(sheet_modes, &idle_dir) {
102 return idle_dir;
103 }
104 }
105 if sheet_has_mode(sheet_modes, "idle") {
106 return "idle".to_string();
107 }
108 base.to_string()
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 fn modes(ids: &[&str]) -> Vec<String> {
116 ids.iter().map(|s| s.to_string()).collect()
117 }
118
119 #[test]
120 fn eight_way_facing_label_bins() {
121 assert_eq!(eight_way_facing_label(0.0), "north");
122 assert_eq!(
123 eight_way_facing_label(std::f32::consts::FRAC_PI_4),
124 "northeast"
125 );
126 assert_eq!(
127 eight_way_facing_label(std::f32::consts::FRAC_PI_2),
128 "east"
129 );
130 assert_eq!(eight_way_facing_label(std::f32::consts::PI), "south");
131 assert_eq!(
132 eight_way_facing_label(-std::f32::consts::FRAC_PI_4),
133 "northwest"
134 );
135 }
136
137 #[test]
138 fn cardinal_facing_label_bins() {
139 assert_eq!(cardinal_facing_label(0.0), "north");
140 assert_eq!(cardinal_facing_label(std::f32::consts::FRAC_PI_2), "east");
141 assert_eq!(cardinal_facing_label(std::f32::consts::PI), "south");
142 }
143
144 #[test]
145 fn base_sprite_mode_strips_suffix() {
146 assert_eq!(base_sprite_mode("walk_north"), "walk");
147 assert_eq!(base_sprite_mode("walk_northeast"), "walk");
148 assert_eq!(base_sprite_mode("idle"), "idle");
149 }
150
151 #[test]
152 fn expand_directional_when_sheet_has_variant() {
153 let sheet = modes(&[
154 "idle_north",
155 "walk_north",
156 "walk_northeast",
157 "walk_east",
158 "combat",
159 ]);
160 assert_eq!(
161 expand_directional_draw_mode(&sheet, "walk", 0.0),
162 "walk_north"
163 );
164 assert_eq!(
165 expand_directional_draw_mode(&sheet, "walk", std::f32::consts::FRAC_PI_4),
166 "walk_northeast"
167 );
168 assert_eq!(
169 expand_directional_draw_mode(&sheet, "walk", std::f32::consts::FRAC_PI_2),
170 "walk_east"
171 );
172 assert_eq!(expand_directional_draw_mode(&sheet, "combat", 0.0), "combat");
173 }
174
175 #[test]
176 fn expand_falls_back_to_four_way_when_eight_missing() {
177 let sheet = modes(&["walk_north", "walk_east", "walk_south", "walk_west"]);
178 assert_eq!(
180 expand_directional_draw_mode(&sheet, "walk", std::f32::consts::FRAC_PI_4),
181 "walk_east"
182 );
183 }
184
185 #[test]
186 fn expand_falls_back_to_base_without_directional() {
187 let sheet = modes(&["walk", "idle"]);
188 assert_eq!(expand_directional_draw_mode(&sheet, "walk", 0.0), "walk");
189 }
190}