1use super::*;
2
3#[derive(Debug, Clone, Copy)]
5pub enum TileType {
6 Ortho {
8 width: u32,
10 height: u32,
12 render_order: RenderOrder,
14 },
15 Isometric {
17 width: u32,
19 height: u32,
21 stagger: bool,
23 stagger_odd: bool,
25 stagger_y: bool,
27 render_order: RenderOrder,
29 },
30 Hexagonal {
32 width: u32,
34 height: u32,
36 stagger_odd: bool,
38 stagger_y: bool,
40 side_length: u32,
42 render_order: RenderOrder,
44 },
45}
46
47impl TileType {
48 pub fn coord_to_pos(&self, layer_height: i32, x: i32, y: i32) -> (i32, i32) {
54 match *self {
55 TileType::Ortho { width, height, .. } => (x * width as i32, y * height as i32),
56
57 TileType::Isometric {
58 width,
59 height,
60 stagger,
61 stagger_odd,
62 stagger_y,
63 ..
64 } => {
65 if stagger {
66 if stagger_y {
67 let rx = if (y % 2 == 1) == stagger_odd {
68 x * width as i32 + width as i32 / 2
69 } else {
70 x * width as i32
71 };
72 let ry = (height as i32 * y) / 2;
73 (rx, ry)
74 } else {
75 let rx = (width as i32 * x) / 2;
76 let ry = if (x % 2 == 1) == stagger_odd {
77 y * height as i32 + height as i32 / 2
78 } else {
79 y * height as i32
80 };
81 (rx, ry)
82 }
83 } else {
84 let rx = (width as i32 * x + width as i32 * (layer_height - 1 - y)) / 2;
85 let ry = (height as i32 * x + height as i32 * y) / 2;
86 (rx, ry)
87 }
88 }
89
90 TileType::Hexagonal {
91 width,
92 height,
93 stagger_odd,
94 stagger_y,
95 side_length,
96 ..
97 } => {
98 if stagger_y {
99 let rx = if (y % 2 == 1) == stagger_odd {
100 x * width as i32 + width as i32 / 2
101 } else {
102 x * width as i32
103 };
104 let ry = ((height + side_length) / 2 - 1) as i32 * y;
105 (rx, ry)
106 } else {
107 let rx = ((width + side_length) / 2 - 1) as i32 * x;
108 let ry = if (x % 2 == 1) == stagger_odd {
109 y * height as i32 + height as i32 / 2
110 } else {
111 y * height as i32
112 };
113 (rx, ry)
114 }
115 }
116 }
117 }
118
119 pub fn pos_to_coord(&self, layer_height: i32, x: i32, y: i32) -> (i32, i32) {
125 match *self {
126 TileType::Ortho { width, height, .. } => (x / width as i32, y / height as i32),
127
128 TileType::Isometric {
129 width,
130 height,
131 stagger,
132 stagger_odd,
133 stagger_y,
134 ..
135 } => {
136 if stagger {
137 let half_w = width as i32 / 2;
138 let half_h = height as i32 / 2;
139
140 let (x, y, off_x, off_y) = match (stagger_odd, stagger_y) {
141 (true, _) => (x, y, 0, 0),
142 (false, false) => (x - half_w, y, 1, 0),
143 (false, true) => (x, y - half_h, 0, 1),
144 };
145
146 let ref_x = div2(x, width as i32);
147 let ref_y = div2(y, height as i32);
148 let rel_x = x - ref_x * width as i32;
149 let rel_y = y - ref_y * height as i32;
150
151 let offset = if rel_y < half_h {
152 (half_h - rel_y % half_h) * half_w / half_h
153 } else {
154 (rel_y % half_h) * half_w / half_h
155 };
156
157 let top = rel_y < half_h;
158 let left = rel_x < offset;
159 let right = rel_x > width as i32 - offset;
160
161 let (x, y) = match (stagger_y, top, left, right) {
162 (true, true, true, false) => (ref_x - 1, ref_y * 2 - 1),
163 (true, true, false, true) => (ref_x, ref_y * 2 - 1),
164 (true, false, true, false) => (ref_x - 1, ref_y * 2 + 1),
165 (true, false, false, true) => (ref_x, ref_y * 2 + 1),
166 (true, _, _, _) => (ref_x, ref_y * 2),
167
168 (false, true, true, false) => (ref_x * 2 - 1, ref_y - 1),
169 (false, true, false, true) => (ref_x * 2 + 1, ref_y - 1),
170 (false, false, true, false) => (ref_x * 2 - 1, ref_y),
171 (false, false, false, true) => (ref_x * 2 + 1, ref_y),
172 (false, _, _, _) => (ref_x * 2, ref_y),
173 };
174
175 (x + off_x, y + off_y)
176 } else {
177 let origin = (width as i32 * layer_height) / 2;
178 let x = x - origin;
179 let rx = y / (height as i32) + x / (width as i32);
180 let ry = y / (height as i32) - x / (width as i32);
181 (rx, ry)
182 }
183 }
184
185 TileType::Hexagonal {
186 width,
187 height,
188 stagger_odd,
189 stagger_y,
190 side_length,
191 ..
192 } => {
193 if stagger_y {
194 let col_w = width as i32;
195 let row_h = (height as i32 - side_length as i32) / 2 + side_length as i32 - 1;
196 let half_w = width as i32 / 2;
197 let half_h = height as i32 / 2;
198
199 let ref_x = div2(x, col_w);
200 let ref_y = div2(y, row_h);
201 let rel_x = x - ref_x * col_w;
202 let rel_y = y - ref_y * row_h;
203
204 let centers = if (mod2(ref_y, 2) == 1) == stagger_odd {
205 [
206 (half_w, -row_h + half_h, ref_x, ref_y - 1),
207 (0, half_h, ref_x - 1, ref_y),
208 (col_w, half_h, ref_x, ref_y),
209 ]
210 } else {
211 [
212 (half_w, half_h, ref_x, ref_y),
213 (0, -row_h + half_h, ref_x - 1, ref_y - 1),
214 (col_w, -row_h + half_h, ref_x, ref_y - 1),
215 ]
216 };
217
218 centers
220 .iter()
221 .min_by_key(|&(x, y, _, _)| {
222 (x - rel_x) * (x - rel_x) + (y - rel_y) * (y - rel_y)
223 })
224 .map(|&(_, _, x, y)| (x, y))
225 .unwrap()
226 } else {
227 let col_w = (width as i32 - side_length as i32) / 2 + side_length as i32 - 1;
228 let row_h = height as i32;
229 let half_w = width as i32 / 2;
230 let half_h = height as i32 / 2;
231
232 let ref_x = div2(x, col_w);
233 let ref_y = div2(y, row_h);
234 let rel_x = x - ref_x * col_w;
235 let rel_y = y - ref_y * row_h;
236
237 let centers = if (mod2(ref_x, 2) == 1) == stagger_odd {
238 [
239 (-col_w + half_w, half_h, ref_x - 1, ref_y),
240 (half_w, 0, ref_x, ref_y - 1),
241 (half_w, row_h, ref_x, ref_y),
242 ]
243 } else {
244 [
245 (half_w, half_h, ref_x, ref_y),
246 (-col_w + half_w, 0, ref_x - 1, ref_y - 1),
247 (-col_w + half_w, row_h, ref_x - 1, ref_y),
248 ]
249 };
250
251 centers
253 .iter()
254 .min_by_key(|&(x, y, _, _)| {
255 (x - rel_x) * (x - rel_x) + (y - rel_y) * (y - rel_y)
256 })
257 .map(|&(_, _, x, y)| (x, y))
258 .unwrap()
259 }
260 }
261 }
262 }
263
264 pub fn tile_width(&self) -> u32 {
266 match *self {
267 TileType::Ortho { width, .. } => width,
268 TileType::Isometric { width, .. } => width,
269 TileType::Hexagonal { width, .. } => width,
270 }
271 }
272
273 pub fn tile_height(&self) -> u32 {
275 match *self {
276 TileType::Ortho { height, .. } => height,
277 TileType::Isometric { height, .. } => height,
278 TileType::Hexagonal { height, .. } => height,
279 }
280 }
281}
282
283fn mod2(x: i32, m: i32) -> i32 {
284 let y = x % m;
285 if y >= 0 {
286 y
287 } else {
288 m + y
289 }
290}
291
292fn div2(x: i32, d: i32) -> i32 {
293 if x >= 0 {
294 x / d
295 } else {
296 x / d - 1
297 }
298}