1use crate::*;
2
3impl TileFlags {
4 pub fn flip_x(&mut self) {
9 match self.contains(TileFlags::ROTATE) {
10 false => self.toggle(TileFlags::FLIP_X),
11 true => self.toggle(TileFlags::FLIP_Y),
12 }
13 }
14
15 pub fn flip_y(&mut self) {
20 match self.contains(TileFlags::ROTATE) {
21 false => self.toggle(TileFlags::FLIP_Y),
22 true => self.toggle(TileFlags::FLIP_X),
23 }
24 }
25
26 pub fn rotate_cw(&mut self) {
30 if self.contains(TileFlags::ROTATE) {
31 self.toggle(TileFlags::FLIP_Y | TileFlags::FLIP_X);
32 }
33 self.toggle(TileFlags::ROTATE);
34 }
35
36 pub fn rotate_ccw(&mut self) {
40 if !self.contains(TileFlags::ROTATE) {
41 self.toggle(TileFlags::FLIP_Y | TileFlags::FLIP_X);
42 }
43 self.toggle(TileFlags::ROTATE);
44 }
45
46 pub fn flip_x_physics(&mut self) {
49 if self.contains(TileFlags::ROTATE) {
50 self.toggle(TileFlags::FLIP_Y | TileFlags::FLIP_X);
51 }
52 }
53
54 pub fn flip_y_physics(&mut self) {
57 if !self.contains(TileFlags::ROTATE) {
58 self.toggle(TileFlags::FLIP_Y | TileFlags::FLIP_X);
59 }
60 }
61}
62
63trait TilePrivate {
64 fn mirror_id(id: u8) -> u8 {
65 id
66 }
67 fn directional_physics_tile(_id: u8) -> bool {
68 false
69 }
70}
71
72impl TilePrivate for Tile {}
73
74fn mirror_lasers(mut id: u8) -> u8 {
75 if (203..=205).contains(&id) {
76 id += (206 - id) * 2;
77 } else if (207..=209).contains(&id) {
78 id -= (id - 206) * 2;
79 }
80 id
81}
82
83impl TilePrivate for GameTile {
84 fn mirror_id(id: u8) -> u8 {
85 mirror_lasers(id)
86 }
87
88 fn directional_physics_tile(id: u8) -> bool {
89 matches!(id, 60 | 61 | 64 | 65 | 67 | 224 | 225)
90 }
91}
92impl TilePrivate for Tele {}
93impl TilePrivate for Switch {
94 fn mirror_id(id: u8) -> u8 {
95 mirror_lasers(id)
96 }
97 fn directional_physics_tile(id: u8) -> bool {
98 matches!(id, 224 | 225)
99 }
100}
101impl TilePrivate for Tune {}
102
103pub trait TileFlips: AnyTile {
105 fn flip_x(&mut self);
107 fn flip_y(&mut self);
109 fn rotate_cw(&mut self);
111 fn rotate_ccw(&mut self);
113}
114
115impl<T: AnyTile + TilePrivate> TileFlips for T {
116 fn flip_x(&mut self) {
117 let id = self.id();
118 if let Some(flags) = self.flags_mut() {
119 if T::directional_physics_tile(id) {
120 flags.flip_x_physics();
121 } else {
122 flags.flip_x();
123 }
124 *self.id_mut() = T::mirror_id(*self.id_mut());
125 }
126 }
127
128 fn flip_y(&mut self) {
129 let id = self.id();
130 if let Some(flags) = self.flags_mut() {
131 if T::directional_physics_tile(id) {
132 flags.flip_y_physics();
133 } else {
134 flags.flip_y();
135 }
136 *self.id_mut() = T::mirror_id(*self.id_mut());
137 }
138 }
139
140 fn rotate_cw(&mut self) {
141 if let Some(flags) = self.flags_mut() {
142 flags.rotate_cw();
143 }
144 }
145
146 fn rotate_ccw(&mut self) {
147 if let Some(flags) = self.flags_mut() {
148 flags.rotate_ccw();
149 }
150 }
151}
152
153impl TileFlips for Speedup {
154 fn flip_x(&mut self) {
155 let mut angle = i16::from(self.angle);
156 angle = 180 - angle;
157 if angle < 0 {
158 angle += 360;
159 }
160 self.angle = angle.into();
161 }
162
163 fn flip_y(&mut self) {
164 let mut angle = i16::from(self.angle);
165 angle = 180 - angle;
166 angle *= -1;
167 angle = 180 - angle;
168 angle %= 360;
169 self.angle = angle.into();
170 }
171
172 fn rotate_cw(&mut self) {
173 let mut angle = i16::from(self.angle);
174 angle += 90;
175 angle %= 360;
176 self.angle = angle.into();
177 }
178
179 fn rotate_ccw(&mut self) {
180 let mut angle = i16::from(self.angle);
181 angle += 270;
182 angle %= 360;
183 self.angle = angle.into();
184 }
185}
186
187pub trait EditTile {
188 fn tile(_tile: &mut Tile) {}
189 fn game_tile(_tile: &mut GameTile) {}
190 fn tele(_tele: &mut Tele) {}
191 fn speedup(_speedup: &mut Speedup) {}
192 fn switch(_switch: &mut Switch) {}
193 fn tune(_tune: &mut Tune) {}
194}
195
196fn edit_tilemap<T: TilemapLayer, F: Fn(&mut T::TileType)>(layer: &mut T, f: F) {
197 layer.tiles_mut().unwrap_mut().iter_mut().for_each(f)
198}
199
200impl TwMap {
201 pub fn edit_tiles<T: EditTile>(&mut self) {
203 for group in &mut self.groups {
204 for layer in &mut group.layers {
205 match layer {
206 Layer::Game(l) => edit_tilemap(l, T::game_tile),
207 Layer::Tiles(l) => edit_tilemap(l, T::tile),
208 Layer::Front(l) => edit_tilemap(l, T::game_tile),
209 Layer::Tele(l) => edit_tilemap(l, T::tele),
210 Layer::Speedup(l) => edit_tilemap(l, T::speedup),
211 Layer::Switch(l) => edit_tilemap(l, T::switch),
212 Layer::Tune(l) => edit_tilemap(l, T::tune),
213 Layer::Quads(_) | Layer::Sounds(_) | Layer::Invalid(_) => {}
214 }
215 }
216 }
217 }
218}
219
220pub struct ZeroUnusedParts;
221
222impl TileFlags {
223 fn clear_unused(&mut self) -> bool {
224 let cleared = *self & TileFlags::all();
225 let changed = cleared != *self;
226 *self = cleared;
227 changed
228 }
229
230 fn clear_unused_and_opaque(&mut self) -> bool {
231 let cleared = *self & (TileFlags::all() - TileFlags::OPAQUE);
232 let changed = cleared != *self;
233 *self = cleared;
234 changed
235 }
236}
237
238impl EditTile for ZeroUnusedParts {
239 fn tile(tile: &mut Tile) {
240 tile.flags.clear_unused();
241 tile.unused = 0;
242 tile.skip = 0;
243 }
244 fn game_tile(tile: &mut GameTile) {
245 tile.flags.clear_unused_and_opaque();
246 tile.unused = 0;
247 tile.skip = 0;
248 }
249 fn speedup(speedup: &mut Speedup) {
250 speedup.unused_padding = 0;
251 }
252 fn switch(switch: &mut Switch) {
253 switch.flags.clear_unused_and_opaque();
254 }
255}
256
257fn zero_air_tile<T: AnyTile>(tile: &mut T) {
258 if tile.id() == 0 {
259 *tile = T::default()
260 }
261}
262
263pub struct ZeroAir;
265
266impl EditTile for ZeroAir {
267 fn tile(tile: &mut Tile) {
268 zero_air_tile(tile)
269 }
270 fn game_tile(tile: &mut GameTile) {
271 zero_air_tile(tile)
272 }
273 fn tele(tele: &mut Tele) {
274 zero_air_tile(tele)
275 }
276 fn speedup(speedup: &mut Speedup) {
277 zero_air_tile(speedup)
278 }
279 fn switch(switch: &mut Switch) {
280 zero_air_tile(switch)
281 }
282 fn tune(tune: &mut Tune) {
283 zero_air_tile(tune)
284 }
285}
286
287fn reset_unnecessary_rotation<T: TilePrivate + AnyTile>(tile: &mut T) {
288 if !T::directional_physics_tile(tile.id()) {
289 if let Some(flags) = tile.flags_mut() {
290 flags.remove(TileFlags::ROTATE | TileFlags::FLIP_X | TileFlags::FLIP_Y);
291 }
292 }
293}
294
295pub struct DDNetNormalizePhysicsRotation;
298
299impl EditTile for DDNetNormalizePhysicsRotation {
302 fn game_tile(tile: &mut GameTile) {
303 reset_unnecessary_rotation(tile);
304 }
305 fn tele(tele: &mut Tele) {
306 reset_unnecessary_rotation(tele);
307 }
308 fn switch(switch: &mut Switch) {
309 reset_unnecessary_rotation(switch);
310 }
311 fn tune(tune: &mut Tune) {
312 reset_unnecessary_rotation(tune);
313 }
314}
315
316fn fix_broken_physics_rotation<T: TilePrivate + AnyTile>(tile: &mut T) {
317 if T::directional_physics_tile(tile.id()) {
318 let Some(flags) = tile.flags_mut() else {
319 return;
320 };
321 if flags.contains(TileFlags::FLIP_X) ^ flags.contains(TileFlags::FLIP_Y) {
322 if flags.contains(TileFlags::ROTATE) {
323 flags.insert(TileFlags::FLIP_X | TileFlags::FLIP_Y);
324 } else {
325 flags.remove(TileFlags::FLIP_X | TileFlags::FLIP_Y);
326 }
327 }
328 }
329}
330
331pub struct DDNetFixPhysicsRotation;
333
334impl EditTile for DDNetFixPhysicsRotation {
335 fn game_tile(tile: &mut GameTile) {
336 fix_broken_physics_rotation(tile);
337 }
338 fn tele(tele: &mut Tele) {
339 fix_broken_physics_rotation(tele);
340 }
341 fn switch(switch: &mut Switch) {
342 fix_broken_physics_rotation(switch);
343 }
344 fn tune(tune: &mut Tune) {
345 fix_broken_physics_rotation(tune);
346 }
347}
348
349pub struct DDNetMigrateSpeedup;
352
353impl EditTile for DDNetMigrateSpeedup {
354 fn speedup(speedup: &mut Speedup) {
355 if speedup.id == 28 {
356 speedup.id = 29;
357 if (1..5).contains(&speedup.max_speed) {
358 speedup.max_speed = 5;
359 }
360 }
361 }
362}