Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * Portions of this file are copyright Rebirth contributors and licensed as |
||
3 | * described in COPYING.txt. |
||
4 | * Portions of this file are copyright Parallax Software and licensed |
||
5 | * according to the Parallax license below. |
||
6 | * See COPYING.txt for license details. |
||
7 | |||
8 | THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX |
||
9 | SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO |
||
10 | END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A |
||
11 | ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS |
||
12 | IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS |
||
13 | SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE |
||
14 | FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE |
||
15 | CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS |
||
16 | AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. |
||
17 | COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. |
||
18 | */ |
||
19 | |||
20 | /* |
||
21 | * |
||
22 | * Created from version 1.11 of main\wall.c |
||
23 | * |
||
24 | */ |
||
25 | |||
26 | #include <stdio.h> |
||
27 | #include <stdlib.h> |
||
28 | #include <math.h> |
||
29 | #include <string.h> |
||
30 | #include "wall.h" |
||
31 | #include "editor/medwall.h" |
||
32 | #include "inferno.h" |
||
33 | #include "editor/editor.h" |
||
34 | #include "editor/esegment.h" |
||
35 | #include "segment.h" |
||
36 | #include "dxxerror.h" |
||
37 | #include "event.h" |
||
38 | #include "game.h" |
||
39 | #include "gameseg.h" |
||
40 | #include "textures.h" |
||
41 | #include "screens.h" |
||
42 | #include "switch.h" |
||
43 | #include "editor/eswitch.h" |
||
44 | #include "texmerge.h" |
||
45 | #include "medrobot.h" |
||
46 | #include "timer.h" |
||
47 | #include "cntrlcen.h" |
||
48 | #include "key.h" |
||
49 | #include "ehostage.h" |
||
50 | #include "centers.h" |
||
51 | #include "piggy.h" |
||
52 | #include "kdefs.h" |
||
53 | #include "u_mem.h" |
||
54 | #include "d_enumerate.h" |
||
55 | |||
56 | #include "compiler-range_for.h" |
||
57 | #include "d_range.h" |
||
58 | #include "partial_range.h" |
||
59 | #include "d_zip.h" |
||
60 | #include <memory> |
||
61 | #include <utility> |
||
62 | |||
63 | static int wall_add_to_side(fvcvertptr &vcvertptr, wall_array &Walls, const vmsegptridx_t segp, unsigned side, unsigned type); |
||
64 | |||
65 | //------------------------------------------------------------------------- |
||
66 | // Variables for this module... |
||
67 | //------------------------------------------------------------------------- |
||
68 | static UI_DIALOG *MainWindow = NULL; |
||
69 | |||
70 | namespace { |
||
71 | |||
72 | struct wall_dialog |
||
73 | { |
||
74 | std::unique_ptr<UI_GADGET_USERBOX> wallViewBox; |
||
75 | std::unique_ptr<UI_GADGET_BUTTON> quitButton, prev_wall, next_wall, blastable, door, illusory, closed_wall, goto_prev_wall, goto_next_wall, remove, bind_trigger, bind_control; |
||
76 | std::array<std::unique_ptr<UI_GADGET_CHECKBOX>, 3> doorFlag; |
||
77 | std::array<std::unique_ptr<UI_GADGET_RADIO>, 4> keyFlag; |
||
78 | int old_wall_num; |
||
79 | fix64 time; |
||
80 | int framenum; |
||
81 | }; |
||
82 | |||
83 | static int Current_door_type=1; |
||
84 | |||
85 | struct count_wall |
||
86 | { |
||
87 | wallnum_t wallnum; |
||
88 | segnum_t segnum; |
||
89 | short sidenum; |
||
90 | }; |
||
91 | |||
92 | static unsigned predicate_find_nonblastable_wall(const wclip &w) |
||
93 | { |
||
94 | if (w.num_frames == wclip_frames_none) |
||
95 | return 0; |
||
96 | return !(w.flags & WCF_BLASTABLE); |
||
97 | } |
||
98 | |||
99 | static unsigned predicate_find_blastable_wall(const wclip &w) |
||
100 | { |
||
101 | if (w.num_frames == wclip_frames_none) |
||
102 | return 0; |
||
103 | return w.flags & WCF_BLASTABLE; |
||
104 | } |
||
105 | |||
106 | } |
||
107 | |||
108 | static window_event_result wall_dialog_handler(UI_DIALOG *dlg,const d_event &event, wall_dialog *wd); |
||
109 | |||
110 | //--------------------------------------------------------------------- |
||
111 | // Add a wall (removable 2 sided) |
||
112 | static int add_wall(fvcvertptr &vcvertptr, wall_array &Walls, const vmsegptridx_t seg, const unsigned side) |
||
113 | { |
||
114 | if (Walls.get_count() < MAX_WALLS-2) |
||
115 | if (IS_CHILD(seg->children[side])) { |
||
116 | shared_segment &sseg = seg; |
||
117 | auto &side0 = sseg.sides[side]; |
||
118 | if (side0.wall_num == wall_none) { |
||
119 | side0.wall_num = Walls.get_count(); |
||
120 | Walls.set_count(Walls.get_count() + 1); |
||
121 | } |
||
122 | |||
123 | const auto &&csegp = seg.absolute_sibling(seg->children[side]); |
||
124 | auto Connectside = find_connect_side(seg, csegp); |
||
125 | |||
126 | shared_segment &scseg = csegp; |
||
127 | auto &side1 = scseg.sides[Connectside]; |
||
128 | if (side1.wall_num == wall_none) { |
||
129 | side1.wall_num = Walls.get_count(); |
||
130 | Walls.set_count(Walls.get_count() + 1); |
||
131 | } |
||
132 | |||
133 | create_removable_wall(vcvertptr, seg, side, CurrentTexture); |
||
134 | create_removable_wall(vcvertptr, csegp, Connectside, CurrentTexture); |
||
135 | |||
136 | return 1; |
||
137 | } |
||
138 | |||
139 | return 0; |
||
140 | } |
||
141 | |||
142 | static int wall_assign_door(int door_type) |
||
143 | { |
||
144 | shared_segment &sseg = Cursegp; |
||
145 | unique_segment &useg = Cursegp; |
||
146 | if (sseg.sides[Curside].wall_num == wall_none) { |
||
147 | editor_status("Cannot assign door. No wall at Curside."); |
||
148 | return 0; |
||
149 | } |
||
150 | |||
151 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
152 | auto &vmwallptr = Walls.vmptr; |
||
153 | auto &wall0 = *vmwallptr(sseg.sides[Curside].wall_num); |
||
154 | if (wall0.type != WALL_DOOR && wall0.type != WALL_BLASTABLE) |
||
155 | { |
||
156 | editor_status("Cannot assign door. No door at Curside."); |
||
157 | return 0; |
||
158 | } |
||
159 | |||
160 | Current_door_type = door_type; |
||
161 | |||
162 | auto &csegp = *vmsegptr(Cursegp->children[Curside]); |
||
163 | auto Connectside = find_connect_side(Cursegp, csegp); |
||
164 | |||
165 | wall0.clip_num = door_type; |
||
166 | shared_segment &scseg = csegp; |
||
167 | unique_segment &ucseg = csegp; |
||
168 | vmwallptr(scseg.sides[Connectside].wall_num)->clip_num = door_type; |
||
169 | |||
170 | auto &wa = GameSharedState.WallAnims[door_type]; |
||
171 | if (wa.flags & WCF_TMAP1) { |
||
172 | useg.sides[Curside].tmap_num = wa.frames[0]; |
||
173 | ucseg.sides[Connectside].tmap_num = wa.frames[0]; |
||
174 | useg.sides[Curside].tmap_num2 = 0; |
||
175 | ucseg.sides[Connectside].tmap_num2 = 0; |
||
176 | } |
||
177 | else { |
||
178 | useg.sides[Curside].tmap_num2 = wa.frames[0]; |
||
179 | ucseg.sides[Connectside].tmap_num2 = wa.frames[0]; |
||
180 | } |
||
181 | |||
182 | Update_flags |= UF_WORLD_CHANGED; |
||
183 | return 1; |
||
184 | } |
||
185 | |||
186 | int wall_add_blastable() |
||
187 | { |
||
188 | auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); |
||
189 | auto &Vertices = LevelSharedVertexState.get_vertices(); |
||
190 | auto &vcvertptr = Vertices.vcptr; |
||
191 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
192 | return wall_add_to_side(vcvertptr, Walls, Cursegp, Curside, WALL_BLASTABLE); |
||
193 | } |
||
194 | |||
195 | int wall_add_door() |
||
196 | { |
||
197 | auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); |
||
198 | auto &Vertices = LevelSharedVertexState.get_vertices(); |
||
199 | auto &vcvertptr = Vertices.vcptr; |
||
200 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
201 | return wall_add_to_side(vcvertptr, Walls, Cursegp, Curside, WALL_DOOR); |
||
202 | } |
||
203 | |||
204 | int wall_add_closed_wall() |
||
205 | { |
||
206 | auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); |
||
207 | auto &Vertices = LevelSharedVertexState.get_vertices(); |
||
208 | auto &vcvertptr = Vertices.vcptr; |
||
209 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
210 | return wall_add_to_side(vcvertptr, Walls, Cursegp, Curside, WALL_CLOSED); |
||
211 | } |
||
212 | |||
213 | int wall_add_external_wall() |
||
214 | { |
||
215 | if (Cursegp->children[Curside] == segment_exit) |
||
216 | { |
||
217 | editor_status( "Wall is already external!" ); |
||
218 | return 1; |
||
219 | } |
||
220 | |||
221 | if (IS_CHILD(Cursegp->children[Curside])) { |
||
222 | editor_status( "Cannot add external wall here - seg has children" ); |
||
223 | return 0; |
||
224 | } |
||
225 | |||
226 | Cursegp->children[Curside] = -2; |
||
227 | |||
228 | return 1; |
||
229 | } |
||
230 | |||
231 | int wall_add_illusion() |
||
232 | { |
||
233 | auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state(); |
||
234 | auto &Vertices = LevelSharedVertexState.get_vertices(); |
||
235 | auto &vcvertptr = Vertices.vcptr; |
||
236 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
237 | return wall_add_to_side(vcvertptr, Walls, Cursegp, Curside, WALL_ILLUSION); |
||
238 | } |
||
239 | |||
240 | static int GotoPrevWall() { |
||
241 | wallnum_t current_wall; |
||
242 | |||
243 | shared_segment &sseg = Cursegp; |
||
244 | auto &side = sseg.sides[Curside]; |
||
245 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
246 | auto &vcwallptr = Walls.vcptr; |
||
247 | if (side.wall_num == wall_none) |
||
248 | current_wall = Walls.get_count(); |
||
249 | else |
||
250 | current_wall = side.wall_num; |
||
251 | |||
252 | current_wall--; |
||
253 | if (current_wall >= Walls.get_count()) current_wall = Walls.get_count()-1; |
||
254 | |||
255 | auto &w = *vcwallptr(current_wall); |
||
256 | if (w.segnum == segment_none) |
||
257 | { |
||
258 | return 0; |
||
259 | } |
||
260 | |||
261 | if (w.sidenum == side_none) |
||
262 | { |
||
263 | return 0; |
||
264 | } |
||
265 | |||
266 | Cursegp = imsegptridx(w.segnum); |
||
267 | Curside = w.sidenum; |
||
268 | |||
269 | return 1; |
||
270 | } |
||
271 | |||
272 | |||
273 | static int GotoNextWall() { |
||
274 | shared_segment &sseg = Cursegp; |
||
275 | auto &side = sseg.sides[Curside]; |
||
276 | auto current_wall = side.wall_num; // It's ok to be -1 because it will immediately become 0 |
||
277 | |||
278 | current_wall++; |
||
279 | |||
280 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
281 | auto &vcwallptr = Walls.vcptr; |
||
282 | if (current_wall >= Walls.get_count()) current_wall = 0; |
||
283 | |||
284 | auto &w = *vcwallptr(current_wall); |
||
285 | if (w.segnum == segment_none) |
||
286 | { |
||
287 | return 0; |
||
288 | } |
||
289 | |||
290 | if (w.sidenum == side_none) |
||
291 | { |
||
292 | return 0; |
||
293 | } |
||
294 | |||
295 | Cursegp = imsegptridx(w.segnum); |
||
296 | Curside = w.sidenum; |
||
297 | |||
298 | return 1; |
||
299 | } |
||
300 | |||
301 | template <typename I, typename P> |
||
302 | I wraparound_find_if(const I begin, const I start, const I end, P &&predicate) |
||
303 | { |
||
304 | for (I iter = start;;) |
||
305 | { |
||
306 | ++ iter; |
||
307 | if (iter == end) |
||
308 | iter = begin; |
||
309 | if (iter == start) |
||
310 | return iter; |
||
311 | if (predicate(*iter)) |
||
312 | return iter; |
||
313 | } |
||
314 | } |
||
315 | |||
316 | /* |
||
317 | * Given a range defined by [`begin`, `end`), a starting point `start` |
||
318 | * that is within that range, and a predicate `predicate`, examine each |
||
319 | * element in the range (`start`, `begin`]. If `predicate(*iter)` |
||
320 | * returns true, return `iter`. Otherwise, perform the same search on |
||
321 | * the range (`end`, `start`). If traversal reaches `start` without |
||
322 | * finding such an element, return `start` without calling |
||
323 | * `predicate(*start)`. |
||
324 | */ |
||
325 | template <typename I, typename P> |
||
326 | I wraparound_backward_find_if(const I begin, const I start, const I end, P &&predicate) |
||
327 | { |
||
328 | for (I iter = start;;) |
||
329 | { |
||
330 | if (iter == begin) |
||
331 | iter = end; |
||
332 | -- iter; |
||
333 | if (iter == start) |
||
334 | return iter; |
||
335 | if (predicate(*iter)) |
||
336 | return iter; |
||
337 | } |
||
338 | } |
||
339 | |||
340 | static int PrevWall() { |
||
341 | int wall_type; |
||
342 | const auto cur_wall_num = Cursegp->shared_segment::sides[Curside].wall_num; |
||
343 | if (cur_wall_num == wall_none) |
||
344 | { |
||
345 | editor_status("Cannot assign new wall. No wall on curside."); |
||
346 | return 0; |
||
347 | } |
||
348 | |||
349 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
350 | auto &vcwallptr = Walls.vcptr; |
||
351 | auto &w = *vcwallptr(cur_wall_num); |
||
352 | wall_type = w.clip_num; |
||
353 | auto &WallAnims = GameSharedState.WallAnims; |
||
354 | |||
355 | const auto b = WallAnims.begin(); |
||
356 | const auto s = std::next(b, wall_type); |
||
357 | const auto e = std::next(b, Num_wall_anims); |
||
358 | if (w.type == WALL_DOOR) |
||
359 | { |
||
360 | auto iter = wraparound_backward_find_if(b, s, e, predicate_find_nonblastable_wall); |
||
361 | if (iter == s) |
||
362 | throw std::runtime_error("Cannot find clip for door."); |
||
363 | wall_type = std::distance(b, iter); |
||
364 | } |
||
365 | else if (w.type == WALL_BLASTABLE) |
||
366 | { |
||
367 | auto iter = wraparound_backward_find_if(b, s, e, predicate_find_blastable_wall); |
||
368 | if (iter == s) |
||
369 | throw std::runtime_error("Cannot find clip for blastable wall."); |
||
370 | wall_type = std::distance(b, iter); |
||
371 | } |
||
372 | |||
373 | wall_assign_door(wall_type); |
||
374 | |||
375 | Update_flags |= UF_WORLD_CHANGED; |
||
376 | return 1; |
||
377 | } |
||
378 | |||
379 | static int NextWall() { |
||
380 | int wall_type; |
||
381 | const auto cur_wall_num = Cursegp->shared_segment::sides[Curside].wall_num; |
||
382 | if (cur_wall_num == wall_none) |
||
383 | { |
||
384 | editor_status("Cannot assign new wall. No wall on curside."); |
||
385 | return 0; |
||
386 | } |
||
387 | |||
388 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
389 | auto &WallAnims = GameSharedState.WallAnims; |
||
390 | auto &vcwallptr = Walls.vcptr; |
||
391 | auto &w = *vcwallptr(cur_wall_num); |
||
392 | wall_type = w.clip_num; |
||
393 | |||
394 | const auto b = WallAnims.begin(); |
||
395 | const auto s = std::next(b, wall_type); |
||
396 | const auto e = std::next(b, Num_wall_anims); |
||
397 | if (w.type == WALL_DOOR) |
||
398 | { |
||
399 | auto iter = wraparound_find_if(b, s, e, predicate_find_nonblastable_wall); |
||
400 | if (iter == s) |
||
401 | throw std::runtime_error("Cannot find clip for door."); |
||
402 | wall_type = std::distance(b, iter); |
||
403 | } |
||
404 | else if (w.type == WALL_BLASTABLE) |
||
405 | { |
||
406 | auto iter = wraparound_find_if(b, s, e, predicate_find_blastable_wall); |
||
407 | if (iter == s) |
||
408 | throw std::runtime_error("Cannot find clip for blastable wall."); |
||
409 | wall_type = std::distance(b, iter); |
||
410 | } |
||
411 | |||
412 | wall_assign_door(wall_type); |
||
413 | |||
414 | Update_flags |= UF_WORLD_CHANGED; |
||
415 | return 1; |
||
416 | |||
417 | } |
||
418 | |||
419 | //------------------------------------------------------------------------- |
||
420 | // Called from the editor... does one instance of the wall dialog box |
||
421 | //------------------------------------------------------------------------- |
||
422 | int do_wall_dialog() |
||
423 | { |
||
424 | // Only open 1 instance of this window... |
||
425 | if ( MainWindow != NULL ) return 0; |
||
426 | |||
427 | auto wd = std::make_unique<wall_dialog>(); |
||
428 | wd->framenum = 0; |
||
429 | |||
430 | // Close other windows. |
||
431 | close_all_windows(); |
||
432 | |||
433 | // Open a window with a quit button |
||
434 | MainWindow = ui_create_dialog(TMAPBOX_X+20, TMAPBOX_Y+20, 765-TMAPBOX_X, 545-TMAPBOX_Y, DF_DIALOG, wall_dialog_handler, std::move(wd)); |
||
435 | return 1; |
||
436 | } |
||
437 | |||
438 | static window_event_result wall_dialog_created(UI_DIALOG *const w, wall_dialog *const wd) |
||
439 | { |
||
440 | wd->quitButton = ui_add_gadget_button(w, 20, 252, 48, 40, "Done", NULL); |
||
441 | // These are the checkboxes for each door flag. |
||
442 | int i = 80; |
||
443 | wd->doorFlag[0] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Locked"); i += 24; |
||
444 | wd->doorFlag[1] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Auto"); i += 24; |
||
445 | wd->doorFlag[2] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Illusion OFF"); i += 24; |
||
446 | wd->keyFlag[0] = ui_add_gadget_radio(w, 22, i, 16, 16, 0, "NONE"); i += 24; |
||
447 | wd->keyFlag[1] = ui_add_gadget_radio(w, 22, i, 16, 16, 0, "Blue"); i += 24; |
||
448 | wd->keyFlag[2] = ui_add_gadget_radio(w, 22, i, 16, 16, 0, "Red"); i += 24; |
||
449 | wd->keyFlag[3] = ui_add_gadget_radio(w, 22, i, 16, 16, 0, "Yellow"); i += 24; |
||
450 | // The little box the wall will appear in. |
||
451 | wd->wallViewBox = ui_add_gadget_userbox(w, 155, 5, 64, 64); |
||
452 | // A bunch of buttons... |
||
453 | i = 80; |
||
454 | wd->prev_wall = ui_add_gadget_button(w, 155, i, 70, 22, "<< Clip", PrevWall); |
||
455 | wd->next_wall = ui_add_gadget_button(w, 155+70, i, 70, 22, "Clip >>", NextWall);i += 25; |
||
456 | wd->blastable = ui_add_gadget_button(w, 155, i, 140, 22, "Add Blastable", wall_add_blastable); i += 25; |
||
457 | wd->door = ui_add_gadget_button(w, 155, i, 140, 22, "Add Door", wall_add_door ); i += 25; |
||
458 | wd->illusory = ui_add_gadget_button(w, 155, i, 140, 22, "Add Illusory", wall_add_illusion); i += 25; |
||
459 | wd->closed_wall = ui_add_gadget_button(w, 155, i, 140, 22, "Add Closed Wall", wall_add_closed_wall); i+=25; |
||
460 | wd->goto_prev_wall = ui_add_gadget_button(w, 155, i, 70, 22, "<< Prev", GotoPrevWall); |
||
461 | wd->goto_next_wall = ui_add_gadget_button(w, 155+70, i, 70, 22, "Next >>", GotoNextWall);i += 25; |
||
462 | wd->remove = ui_add_gadget_button(w, 155, i, 140, 22, "Remove Wall", wall_remove); i += 25; |
||
463 | wd->bind_trigger = ui_add_gadget_button(w, 155, i, 140, 22, "Bind to Trigger", bind_wall_to_trigger); i += 25; |
||
464 | wd->bind_control = ui_add_gadget_button(w, 155, i, 140, 22, "Bind to Control", bind_wall_to_control_center); i+=25; |
||
465 | wd->old_wall_num = -2; // Set to some dummy value so everything works ok on the first frame. |
||
466 | |||
467 | return window_event_result::handled; |
||
468 | } |
||
469 | |||
470 | void close_wall_window() |
||
471 | { |
||
472 | if (MainWindow) |
||
473 | ui_close_dialog(std::exchange(MainWindow, nullptr)); |
||
474 | } |
||
475 | |||
476 | window_event_result wall_dialog_handler(UI_DIALOG *dlg,const d_event &event, wall_dialog *wd) |
||
477 | { |
||
478 | switch(event.type) |
||
479 | { |
||
480 | case EVENT_WINDOW_CREATED: |
||
481 | return wall_dialog_created(dlg, wd); |
||
482 | case EVENT_WINDOW_CLOSE: |
||
483 | std::default_delete<wall_dialog>()(wd); |
||
484 | MainWindow = nullptr; |
||
485 | return window_event_result::ignored; |
||
486 | default: |
||
487 | break; |
||
488 | } |
||
489 | sbyte type; |
||
490 | fix DeltaTime; |
||
491 | fix64 Temp; |
||
492 | int keypress = 0; |
||
493 | window_event_result rval = window_event_result::ignored; |
||
494 | |||
495 | if (event.type == EVENT_KEY_COMMAND) |
||
496 | keypress = event_key_get(event); |
||
497 | |||
498 | Assert(MainWindow != NULL); |
||
499 | |||
500 | //------------------------------------------------------------ |
||
501 | // Call the ui code.. |
||
502 | //------------------------------------------------------------ |
||
503 | ui_button_any_drawn = 0; |
||
504 | |||
505 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
506 | auto &WallAnims = GameSharedState.WallAnims; |
||
507 | auto &imwallptridx = Walls.imptridx; |
||
508 | const auto &&w = imwallptridx(Cursegp->shared_segment::sides[Curside].wall_num); |
||
509 | //------------------------------------------------------------ |
||
510 | // If we change walls, we need to reset the ui code for all |
||
511 | // of the checkboxes that control the wall flags. |
||
512 | //------------------------------------------------------------ |
||
513 | if (wd->old_wall_num != w) |
||
514 | { |
||
515 | if (w) |
||
516 | { |
||
517 | ui_checkbox_check(wd->doorFlag[0].get(), w->flags & WALL_DOOR_LOCKED); |
||
518 | ui_checkbox_check(wd->doorFlag[1].get(), w->flags & WALL_DOOR_AUTO); |
||
519 | ui_checkbox_check(wd->doorFlag[2].get(), w->flags & WALL_ILLUSION_OFF); |
||
520 | |||
521 | ui_radio_set_value(wd->keyFlag[0].get(), w->keys & KEY_NONE); |
||
522 | ui_radio_set_value(wd->keyFlag[1].get(), w->keys & KEY_BLUE); |
||
523 | ui_radio_set_value(wd->keyFlag[2].get(), w->keys & KEY_RED); |
||
524 | ui_radio_set_value(wd->keyFlag[3].get(), w->keys & KEY_GOLD); |
||
525 | } |
||
526 | } |
||
527 | |||
528 | //------------------------------------------------------------ |
||
529 | // If any of the checkboxes that control the wallflags are set, then |
||
530 | // update the corresponding wall flag. |
||
531 | //------------------------------------------------------------ |
||
532 | |||
533 | if (w && w->type == WALL_DOOR) |
||
534 | { |
||
535 | if (GADGET_PRESSED(wd->doorFlag[0].get())) |
||
536 | { |
||
537 | if ( wd->doorFlag[0]->flag == 1 ) |
||
538 | w->flags |= WALL_DOOR_LOCKED; |
||
539 | else |
||
540 | w->flags &= ~WALL_DOOR_LOCKED; |
||
541 | rval = window_event_result::handled; |
||
542 | } |
||
543 | else if (GADGET_PRESSED(wd->doorFlag[1].get())) |
||
544 | { |
||
545 | if ( wd->doorFlag[1]->flag == 1 ) |
||
546 | w->flags |= WALL_DOOR_AUTO; |
||
547 | else |
||
548 | w->flags &= ~WALL_DOOR_AUTO; |
||
549 | rval = window_event_result::handled; |
||
550 | } |
||
551 | |||
552 | //------------------------------------------------------------ |
||
553 | // If any of the radio buttons that control the mode are set, then |
||
554 | // update the corresponding key. |
||
555 | //------------------------------------------------------------ |
||
556 | range_for (const int i, xrange(4u)) { |
||
557 | if (GADGET_PRESSED(wd->keyFlag[i].get())) |
||
558 | { |
||
559 | w->keys = 1<<i; // Set the ai_state to the cooresponding radio button |
||
560 | rval = window_event_result::handled; |
||
561 | } |
||
562 | } |
||
563 | } else { |
||
564 | range_for (auto &i, partial_const_range(wd->doorFlag, 2u)) |
||
565 | ui_checkbox_check(i.get(), 0); |
||
566 | range_for (auto &i, wd->keyFlag) |
||
567 | ui_radio_set_value(i.get(), 0); |
||
568 | } |
||
569 | |||
570 | if (w && w->type == WALL_ILLUSION) { |
||
571 | if (GADGET_PRESSED(wd->doorFlag[2].get())) |
||
572 | { |
||
573 | if ( wd->doorFlag[2]->flag == 1 ) |
||
574 | w->flags |= WALL_ILLUSION_OFF; |
||
575 | else |
||
576 | w->flags &= ~WALL_ILLUSION_OFF; |
||
577 | rval = window_event_result::handled; |
||
578 | } |
||
579 | } else |
||
580 | for ( int i=2; i < 3; i++ ) |
||
581 | if (wd->doorFlag[i]->flag == 1) { |
||
582 | wd->doorFlag[i]->flag = 0; // Tells ui that this button isn't checked |
||
583 | wd->doorFlag[i]->status = 1; // Tells ui to redraw button |
||
584 | } |
||
585 | |||
586 | //------------------------------------------------------------ |
||
587 | // Draw the wall in the little 64x64 box |
||
588 | //------------------------------------------------------------ |
||
589 | if (event.type == EVENT_UI_DIALOG_DRAW) |
||
590 | { |
||
591 | // A simple frame time counter for animating the walls... |
||
592 | Temp = timer_query(); |
||
593 | DeltaTime = Temp - wd->time; |
||
594 | |||
595 | gr_set_current_canvas( wd->wallViewBox->canvas ); |
||
596 | if (w) { |
||
597 | type = w->type; |
||
598 | if ((type == WALL_DOOR) || (type == WALL_BLASTABLE)) { |
||
599 | if (DeltaTime > ((F1_0*200)/1000)) { |
||
600 | wd->framenum++; |
||
601 | wd->time = Temp; |
||
602 | } |
||
603 | auto &wa = WallAnims[w->clip_num]; |
||
604 | if (wd->framenum >= wa.num_frames) |
||
605 | wd->framenum=0; |
||
606 | const auto frame = wa.frames[wd->framenum]; |
||
607 | auto &texture = Textures[frame]; |
||
608 | PIGGY_PAGE_IN(texture); |
||
609 | gr_ubitmap(*grd_curcanv, GameBitmaps[texture.index]); |
||
610 | } else { |
||
611 | if (type == WALL_OPEN) |
||
612 | gr_clear_canvas(*grd_curcanv, CBLACK); |
||
613 | else { |
||
614 | auto &curside = Cursegp->unique_segment::sides[Curside]; |
||
615 | const auto tmap_num = curside.tmap_num; |
||
616 | if (curside.tmap_num2 > 0) |
||
617 | gr_ubitmap(*grd_curcanv, texmerge_get_cached_bitmap(tmap_num, curside.tmap_num2)); |
||
618 | else { |
||
619 | PIGGY_PAGE_IN(Textures[tmap_num]); |
||
620 | gr_ubitmap(*grd_curcanv, GameBitmaps[Textures[tmap_num].index]); |
||
621 | } |
||
622 | } |
||
623 | } |
||
624 | } else |
||
625 | gr_clear_canvas(*grd_curcanv, CGREY); |
||
626 | } |
||
627 | |||
628 | //------------------------------------------------------------ |
||
629 | // If anything changes in the ui system, redraw all the text that |
||
630 | // identifies this wall. |
||
631 | //------------------------------------------------------------ |
||
632 | if (event.type == EVENT_UI_DIALOG_DRAW) |
||
633 | { |
||
634 | if (w) { |
||
635 | ui_dprintf_at( MainWindow, 12, 6, "Wall: %hi ", static_cast<int16_t>(w)); |
||
636 | switch (w->type) { |
||
637 | case WALL_NORMAL: |
||
638 | ui_dprintf_at( MainWindow, 12, 23, " Type: Normal " ); |
||
639 | break; |
||
640 | case WALL_BLASTABLE: |
||
641 | ui_dprintf_at( MainWindow, 12, 23, " Type: Blastable" ); |
||
642 | break; |
||
643 | case WALL_DOOR: |
||
644 | ui_dprintf_at( MainWindow, 12, 23, " Type: Door " ); |
||
645 | ui_dputs_at( MainWindow, 223, 6, &WallAnims[w->clip_num].filename[0]); |
||
646 | break; |
||
647 | case WALL_ILLUSION: |
||
648 | ui_dprintf_at( MainWindow, 12, 23, " Type: Illusion " ); |
||
649 | break; |
||
650 | case WALL_OPEN: |
||
651 | ui_dprintf_at( MainWindow, 12, 23, " Type: Open " ); |
||
652 | break; |
||
653 | case WALL_CLOSED: |
||
654 | ui_dprintf_at( MainWindow, 12, 23, " Type: Closed " ); |
||
655 | break; |
||
656 | default: |
||
657 | ui_dprintf_at( MainWindow, 12, 23, " Type: Unknown " ); |
||
658 | break; |
||
659 | } |
||
660 | if (w->type != WALL_DOOR) |
||
661 | ui_dprintf_at( MainWindow, 223, 6, " " ); |
||
662 | |||
663 | ui_dprintf_at( MainWindow, 12, 40, " Clip: %d ", w->clip_num ); |
||
664 | ui_dprintf_at( MainWindow, 12, 57, " Trigger: %d ", w->trigger ); |
||
665 | } else { |
||
666 | ui_dprintf_at( MainWindow, 12, 6, "Wall: none "); |
||
667 | ui_dprintf_at( MainWindow, 12, 23, " Type: none "); |
||
668 | ui_dprintf_at( MainWindow, 12, 40, " Clip: none "); |
||
669 | ui_dprintf_at( MainWindow, 12, 57, " Trigger: none "); |
||
670 | } |
||
671 | } |
||
672 | |||
673 | if (ui_button_any_drawn || (wd->old_wall_num != w) ) |
||
674 | Update_flags |= UF_WORLD_CHANGED; |
||
675 | if (GADGET_PRESSED(wd->quitButton.get()) || keypress == KEY_ESC) |
||
676 | { |
||
677 | return window_event_result::close; |
||
678 | } |
||
679 | |||
680 | wd->old_wall_num = w; |
||
681 | |||
682 | return rval; |
||
683 | } |
||
684 | |||
685 | |||
686 | //--------------------------------------------------------------------- |
||
687 | |||
688 | // Restore all walls to original status (closed doors, repaired walls) |
||
689 | int wall_restore_all() |
||
690 | { |
||
691 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
692 | auto &WallAnims = GameSharedState.WallAnims; |
||
693 | auto &vcwallptr = Walls.vcptr; |
||
694 | auto &vmwallptr = Walls.vmptr; |
||
695 | range_for (const auto &&wp, vmwallptr) |
||
696 | { |
||
697 | auto &w = *wp; |
||
698 | if (w.flags & WALL_BLASTED) { |
||
699 | w.hps = WALL_HPS; |
||
700 | } |
||
701 | w.flags &= ~(WALL_BLASTED | WALL_DOOR_OPENED | WALL_DOOR_OPENING | WALL_EXPLODING); |
||
702 | } |
||
703 | |||
704 | auto &ActiveDoors = LevelUniqueWallSubsystemState.ActiveDoors; |
||
705 | range_for (auto &&i, ActiveDoors.vmptr) |
||
706 | wall_close_door_ref(Segments.vmptridx, Walls, WallAnims, i); |
||
707 | |||
708 | range_for (auto &&i, vmsegptr) |
||
709 | for (auto &&[us, ss] : zip(i->unique_segment::sides, i->shared_segment::sides)) |
||
710 | { |
||
711 | const auto wall_num = ss.wall_num; |
||
712 | if (wall_num != wall_none) |
||
713 | { |
||
714 | auto &w = *vcwallptr(wall_num); |
||
715 | if (w.type == WALL_BLASTABLE || w.type == WALL_DOOR) |
||
716 | us.tmap_num2 = WallAnims[w.clip_num].frames[0]; |
||
717 | } |
||
718 | } |
||
719 | |||
720 | #if defined(DXX_BUILD_DESCENT_II) |
||
721 | auto &Triggers = LevelUniqueWallSubsystemState.Triggers; |
||
722 | auto &vmtrgptr = Triggers.vmptr; |
||
723 | range_for (const auto i, vmtrgptr) |
||
724 | i->flags &= ~trigger_behavior_flags::disabled; |
||
725 | #endif |
||
726 | Update_flags |= UF_GAME_VIEW_CHANGED; |
||
727 | |||
728 | return 1; |
||
729 | } |
||
730 | |||
731 | //--------------------------------------------------------------------- |
||
732 | // Remove a specific side. |
||
733 | int wall_remove_side(const vmsegptridx_t seg, short side) |
||
734 | { |
||
735 | if (IS_CHILD(seg->children[side]) && seg->shared_segment::sides[side].wall_num != wall_none) |
||
736 | { |
||
737 | shared_segment &csegp = *vmsegptr(seg->children[side]); |
||
738 | const auto Connectside = find_connect_side(seg, csegp); |
||
739 | |||
740 | remove_trigger(seg, side); |
||
741 | remove_trigger(csegp, Connectside); |
||
742 | |||
743 | // Remove walls 'wall_num' and connecting side 'wall_num' |
||
744 | // from Walls array. |
||
745 | const auto wall0 = seg->shared_segment::sides[side].wall_num; |
||
746 | const auto wall1 = csegp.sides[Connectside].wall_num; |
||
747 | const auto lower_wallnum = (wall0 < wall1) ? wall0 : wall1; |
||
748 | |||
749 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
750 | auto &vcwallptr = Walls.vcptr; |
||
751 | auto &vmwallptr = Walls.vmptr; |
||
752 | { |
||
753 | const auto linked_wall = vcwallptr(lower_wallnum)->linked_wall; |
||
754 | if (linked_wall != wall_none) |
||
755 | vmwallptr(linked_wall)->linked_wall = wall_none; |
||
756 | } |
||
757 | { |
||
758 | const wallnum_t upper_wallnum = lower_wallnum + 1; |
||
759 | const auto linked_wall = vcwallptr(upper_wallnum)->linked_wall; |
||
760 | if (linked_wall != wall_none) |
||
761 | vmwallptr(linked_wall)->linked_wall = wall_none; |
||
762 | } |
||
763 | |||
764 | { |
||
765 | const auto num_walls = Walls.get_count(); |
||
766 | auto &&sr = partial_const_range(Walls, static_cast<wallnum_t>(lower_wallnum + 2), num_walls); |
||
767 | std::move(sr.begin(), sr.end(), partial_range(Walls, lower_wallnum, num_walls - 2).begin()); |
||
768 | Walls.set_count(num_walls - 2); |
||
769 | } |
||
770 | |||
771 | range_for (const auto &&segp, vmsegptr) |
||
772 | { |
||
773 | if (segp->segnum != segment_none) |
||
774 | range_for (auto &w, segp->shared_segment::sides) |
||
775 | if (w.wall_num != wall_none && w.wall_num > lower_wallnum+1) |
||
776 | w.wall_num -= 2; |
||
777 | } |
||
778 | |||
779 | // Destroy any links to the deleted wall. |
||
780 | auto &Triggers = LevelUniqueWallSubsystemState.Triggers; |
||
781 | auto &vmtrgptr = Triggers.vmptr; |
||
782 | range_for (const auto vt, vmtrgptr) |
||
783 | { |
||
784 | auto &t = *vt; |
||
785 | for (int l=0;l < t.num_links;l++) |
||
786 | if (t.seg[l] == seg && t.side[l] == side) { |
||
787 | for (int t1=0;t1 < t.num_links-1;t1++) { |
||
788 | t.seg[t1] = t.seg[t1+1]; |
||
789 | t.side[t1] = t.side[t1+1]; |
||
790 | } |
||
791 | t.num_links--; |
||
792 | } |
||
793 | } |
||
794 | |||
795 | // Destroy control center links as well. |
||
796 | for (int l=0;l<ControlCenterTriggers.num_links;l++) |
||
797 | if (ControlCenterTriggers.seg[l] == seg && ControlCenterTriggers.side[l] == side) { |
||
798 | for (int t1=0;t1<ControlCenterTriggers.num_links-1;t1++) { |
||
799 | ControlCenterTriggers.seg[t1] = ControlCenterTriggers.seg[t1+1]; |
||
800 | ControlCenterTriggers.side[t1] = ControlCenterTriggers.side[t1+1]; |
||
801 | } |
||
802 | ControlCenterTriggers.num_links--; |
||
803 | } |
||
804 | |||
805 | seg->shared_segment::sides[side].wall_num = wall_none; |
||
806 | csegp.sides[Connectside].wall_num = wall_none; |
||
807 | |||
808 | Update_flags |= UF_WORLD_CHANGED; |
||
809 | return 1; |
||
810 | } |
||
811 | |||
812 | editor_status( "Can't remove wall. No wall present."); |
||
813 | return 0; |
||
814 | } |
||
815 | |||
816 | //--------------------------------------------------------------------- |
||
817 | // Remove a special wall. |
||
818 | int wall_remove() |
||
819 | { |
||
820 | return wall_remove_side(Cursegp, Curside); |
||
821 | } |
||
822 | |||
823 | //--------------------------------------------------------------------- |
||
824 | // Add a wall to curside |
||
825 | static int wall_add_to_side(fvcvertptr &vcvertptr, wall_array &Walls, const vmsegptridx_t segp, const unsigned side, const unsigned type) |
||
826 | { |
||
827 | if (add_wall(vcvertptr, Walls, segp, side)) { |
||
828 | const auto &&csegp = segp.absolute_sibling(segp->children[side]); |
||
829 | auto connectside = find_connect_side(segp, csegp); |
||
830 | |||
831 | auto &vmwallptr = Walls.vmptr; |
||
832 | auto &w0 = *vmwallptr(segp->shared_segment::sides[side].wall_num); |
||
833 | auto &w1 = *vmwallptr(csegp->shared_segment::sides[connectside].wall_num); |
||
834 | w0.segnum = segp; |
||
835 | w1.segnum = csegp; |
||
836 | |||
837 | w0.sidenum = side; |
||
838 | w1.sidenum = connectside; |
||
839 | |||
840 | w0.flags = 0; |
||
841 | w1.flags = 0; |
||
842 | |||
843 | w0.type = type; |
||
844 | w1.type = type; |
||
845 | |||
846 | w0.clip_num = -1; |
||
847 | w1.clip_num = -1; |
||
848 | |||
849 | w0.keys = KEY_NONE; |
||
850 | w1.keys = KEY_NONE; |
||
851 | |||
852 | if (type == WALL_BLASTABLE) { |
||
853 | w0.hps = WALL_HPS; |
||
854 | w1.hps = WALL_HPS; |
||
855 | } |
||
856 | |||
857 | if (type != WALL_DOOR) { |
||
858 | segp->unique_segment::sides[side].tmap_num2 = 0; |
||
859 | csegp->unique_segment::sides[connectside].tmap_num2 = 0; |
||
860 | } |
||
861 | |||
862 | if (type == WALL_DOOR) { |
||
863 | w0.flags |= WALL_DOOR_AUTO; |
||
864 | w1.flags |= WALL_DOOR_AUTO; |
||
865 | |||
866 | w0.clip_num = Current_door_type; |
||
867 | w1.clip_num = Current_door_type; |
||
868 | } |
||
869 | |||
870 | //Update_flags |= UF_WORLD_CHANGED; |
||
871 | //return 1; |
||
872 | |||
873 | // return NextWall(); //assign a clip num |
||
874 | return wall_assign_door(Current_door_type); |
||
875 | |||
876 | } else { |
||
877 | editor_status( "Cannot add wall here, no children" ); |
||
878 | return 0; |
||
879 | } |
||
880 | } |
||
881 | |||
882 | |||
883 | //--------------------------------------------------------------------- |
||
884 | // Add a wall to markedside |
||
885 | int wall_add_to_markedside(fvcvertptr &vcvertptr, wall_array &Walls, const int8_t type) |
||
886 | { |
||
887 | if (add_wall(vcvertptr, Walls, Markedsegp, Markedside)) { |
||
888 | const auto &&csegp = vmsegptridx(Markedsegp->children[Markedside]); |
||
889 | auto Connectside = find_connect_side(Markedsegp, csegp); |
||
890 | |||
891 | const auto wall_num = Markedsegp->shared_segment::sides[Markedside].wall_num; |
||
892 | const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num; |
||
893 | auto &vmwallptr = Walls.vmptr; |
||
894 | auto &w0 = *vmwallptr(wall_num); |
||
895 | auto &w1 = *vmwallptr(cwall_num); |
||
896 | |||
897 | w0.segnum = Markedsegp; |
||
898 | w1.segnum = csegp; |
||
899 | |||
900 | w0.sidenum = Markedside; |
||
901 | w1.sidenum = Connectside; |
||
902 | |||
903 | w0.flags = 0; |
||
904 | w1.flags = 0; |
||
905 | |||
906 | w0.type = type; |
||
907 | w1.type = type; |
||
908 | |||
909 | w0.trigger = trigger_none; |
||
910 | w1.trigger = trigger_none; |
||
911 | |||
912 | w0.clip_num = -1; |
||
913 | w1.clip_num = -1; |
||
914 | |||
915 | w0.keys = KEY_NONE; |
||
916 | w1.keys = KEY_NONE; |
||
917 | |||
918 | if (type == WALL_BLASTABLE) { |
||
919 | w0.hps = WALL_HPS; |
||
920 | w1.hps = WALL_HPS; |
||
921 | |||
922 | w0.clip_num = 0; |
||
923 | w1.clip_num = 0; |
||
924 | } |
||
925 | |||
926 | if (type != WALL_DOOR) { |
||
927 | Markedsegp->unique_segment::sides[Markedside].tmap_num2 = 0; |
||
928 | csegp->unique_segment::sides[Connectside].tmap_num2 = 0; |
||
929 | } |
||
930 | |||
931 | Update_flags |= UF_WORLD_CHANGED; |
||
932 | return 1; |
||
933 | } else { |
||
934 | editor_status( "Cannot add wall here, no children" ); |
||
935 | return 0; |
||
936 | } |
||
937 | } |
||
938 | |||
939 | int bind_wall_to_control_center() { |
||
940 | |||
941 | int link_num; |
||
942 | if (Cursegp->shared_segment::sides[Curside].wall_num == wall_none) { |
||
943 | editor_status("No wall at Curside."); |
||
944 | return 0; |
||
945 | } |
||
946 | |||
947 | link_num = ControlCenterTriggers.num_links; |
||
948 | for (int i=0;i<link_num;i++) |
||
949 | if (Cursegp == ControlCenterTriggers.seg[i] && Curside == ControlCenterTriggers.side[i]) |
||
950 | { |
||
951 | editor_status("Curside already bound to Control Center."); |
||
952 | return 0; |
||
953 | } |
||
954 | |||
955 | // Error checking completed, actual binding begins |
||
956 | ControlCenterTriggers.seg[link_num] = Cursegp; |
||
957 | ControlCenterTriggers.side[link_num] = Curside; |
||
958 | ControlCenterTriggers.num_links++; |
||
959 | |||
960 | editor_status("Wall linked to control center"); |
||
961 | |||
962 | return 1; |
||
963 | } |
||
964 | |||
965 | //link two doors, curseg/curside and markedseg/markedside |
||
966 | int wall_link_doors() |
||
967 | { |
||
968 | const auto cwall_num = Cursegp->shared_segment::sides[Curside].wall_num; |
||
969 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
970 | auto &imwallptr = Walls.imptr; |
||
971 | const auto &&w1 = imwallptr(cwall_num); |
||
972 | |||
973 | if (!w1 || w1->type != WALL_DOOR) { |
||
974 | editor_status("Curseg/curside is not a door"); |
||
975 | return 0; |
||
976 | } |
||
977 | |||
978 | if (!Markedsegp) { |
||
979 | editor_status("No marked side."); |
||
980 | return 0; |
||
981 | } |
||
982 | |||
983 | const auto mwall_num = Markedsegp->shared_segment::sides[Markedside].wall_num; |
||
984 | const auto &&w2 = imwallptr(mwall_num); |
||
985 | |||
986 | if (!w2 || w2->type != WALL_DOOR) { |
||
987 | editor_status("Markedseg/markedside is not a door"); |
||
988 | return 0; |
||
989 | } |
||
990 | |||
991 | if (w1->linked_wall != wall_none) |
||
992 | editor_status("Curseg/curside is already linked"); |
||
993 | |||
994 | if (w2->linked_wall != wall_none) |
||
995 | editor_status("Markedseg/markedside is already linked"); |
||
996 | |||
997 | w1->linked_wall = Markedsegp->shared_segment::sides[Markedside].wall_num; |
||
998 | w2->linked_wall = Cursegp->shared_segment::sides[Curside].wall_num; |
||
999 | |||
1000 | return 1; |
||
1001 | } |
||
1002 | |||
1003 | int wall_unlink_door() |
||
1004 | { |
||
1005 | const auto cwall_num = Cursegp->shared_segment::sides[Curside].wall_num; |
||
1006 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
1007 | auto &imwallptr = Walls.imptr; |
||
1008 | const auto &&w1 = imwallptr(cwall_num); |
||
1009 | |||
1010 | if (!w1 || w1->type != WALL_DOOR) { |
||
1011 | editor_status("Curseg/curside is not a door"); |
||
1012 | return 0; |
||
1013 | } |
||
1014 | |||
1015 | if (w1->linked_wall == wall_none) |
||
1016 | { |
||
1017 | editor_status("Curseg/curside is not linked"); |
||
1018 | return 0; |
||
1019 | } |
||
1020 | |||
1021 | auto &vmwallptr = Walls.vmptr; |
||
1022 | auto &w2 = *vmwallptr(w1->linked_wall); |
||
1023 | Assert(w2.linked_wall == cwall_num); |
||
1024 | |||
1025 | w2.linked_wall = wall_none; |
||
1026 | w1->linked_wall = wall_none; |
||
1027 | |||
1028 | return 1; |
||
1029 | |||
1030 | } |
||
1031 | |||
1032 | int check_walls() |
||
1033 | { |
||
1034 | auto &RobotCenters = LevelSharedRobotcenterState.RobotCenters; |
||
1035 | std::array<count_wall, MAX_WALLS> CountedWalls; |
||
1036 | int matcen_num; |
||
1037 | |||
1038 | unsigned wall_count = 0; |
||
1039 | range_for (const auto &&segp, vmsegptridx) |
||
1040 | { |
||
1041 | if (segp->segnum != segment_none) { |
||
1042 | // Check fuelcenters |
||
1043 | matcen_num = segp->matcen_num; |
||
1044 | if (matcen_num == 0) |
||
1045 | if (RobotCenters[0].segnum != segp) { |
||
1046 | segp->matcen_num = -1; |
||
1047 | } |
||
1048 | |||
1049 | if (matcen_num > -1) |
||
1050 | RobotCenters[matcen_num].segnum = segp; |
||
1051 | |||
1052 | range_for (auto &&e, enumerate(segp->shared_segment::sides)) |
||
1053 | { |
||
1054 | auto &s = e.value; |
||
1055 | if (s.wall_num != wall_none) { |
||
1056 | CountedWalls[wall_count].wallnum = s.wall_num; |
||
1057 | CountedWalls[wall_count].segnum = segp; |
||
1058 | CountedWalls[wall_count].sidenum = e.idx; |
||
1059 | wall_count++; |
||
1060 | } |
||
1061 | } |
||
1062 | } |
||
1063 | } |
||
1064 | |||
1065 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
1066 | if (wall_count != Walls.get_count()) { |
||
1067 | if (ui_messagebox(-2, -2, 2, "Num_walls is bogus\nDo you wish to correct it?\n", "Yes", "No") == 1) |
||
1068 | { |
||
1069 | Walls.set_count(wall_count); |
||
1070 | editor_status_fmt("Num_walls set to %d\n", Walls.get_count()); |
||
1071 | } |
||
1072 | } |
||
1073 | |||
1074 | // Check validity of Walls array. |
||
1075 | auto &vmwallptr = Walls.vmptr; |
||
1076 | range_for (auto &cw, partial_const_range(CountedWalls, Walls.get_count())) |
||
1077 | { |
||
1078 | auto &w = *vmwallptr(cw.wallnum); |
||
1079 | if (w.segnum != cw.segnum || w.sidenum != cw.sidenum) |
||
1080 | { |
||
1081 | if (ui_messagebox( -2, -2, 2, "Unmatched wall detected\nDo you wish to correct it?\n", "Yes", "No") == 1) |
||
1082 | { |
||
1083 | w.segnum = cw.segnum; |
||
1084 | w.sidenum = cw.sidenum; |
||
1085 | } |
||
1086 | } |
||
1087 | } |
||
1088 | |||
1089 | const auto &&used_walls = partial_const_range(Walls, wall_count); |
||
1090 | const auto predicate = [](const wall &w) { |
||
1091 | return w.trigger != trigger_none; |
||
1092 | }; |
||
1093 | unsigned trigger_count = std::count_if(used_walls.begin(), used_walls.end(), predicate); |
||
1094 | |||
1095 | auto &Triggers = LevelUniqueWallSubsystemState.Triggers; |
||
1096 | if (trigger_count != Triggers.get_count()) { |
||
1097 | if (ui_messagebox(-2, -2, 2, "Num_triggers is bogus\nDo you wish to correct it?\n", "Yes", "No") == 1) |
||
1098 | { |
||
1099 | Triggers.set_count(trigger_count); |
||
1100 | editor_status_fmt("Num_triggers set to %d\n", Triggers.get_count()); |
||
1101 | } |
||
1102 | } |
||
1103 | |||
1104 | return 1; |
||
1105 | |||
1106 | } |
||
1107 | |||
1108 | |||
1109 | int delete_all_walls() |
||
1110 | { |
||
1111 | if (ui_messagebox(-2, -2, 2, "Are you sure that walls are hosed so\n badly that you want them ALL GONE!?\n", "YES!", "No") == 1) |
||
1112 | { |
||
1113 | range_for (shared_segment &segp, vmsegptr) |
||
1114 | { |
||
1115 | range_for (auto &side, segp.sides) |
||
1116 | side.wall_num = wall_none; |
||
1117 | } |
||
1118 | auto &Triggers = LevelUniqueWallSubsystemState.Triggers; |
||
1119 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
1120 | Walls.set_count(0); |
||
1121 | Triggers.set_count(0); |
||
1122 | |||
1123 | return 1; |
||
1124 | } |
||
1125 | |||
1126 | return 0; |
||
1127 | } |
||
1128 | |||
1129 | // ------------------------------------------------------------------------------------------------ |
||
1130 | static void copy_old_wall_data_to_new(wallnum_t owall, wallnum_t nwall) |
||
1131 | { |
||
1132 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
1133 | auto &o = *Walls.vcptr(owall); |
||
1134 | auto &n = *Walls.vmptr(nwall); |
||
1135 | n.flags = o.flags; |
||
1136 | n.type = o.type; |
||
1137 | n.clip_num = o.clip_num; |
||
1138 | n.keys = o.keys; |
||
1139 | n.hps = o.hps; |
||
1140 | n.state = o.state; |
||
1141 | n.linked_wall = wall_none; |
||
1142 | |||
1143 | n.trigger = trigger_none; |
||
1144 | if (o.trigger != trigger_none) |
||
1145 | { |
||
1146 | editor_status("Warning: Trigger not copied in group copy."); |
||
1147 | } |
||
1148 | } |
||
1149 | |||
1150 | // ------------------------------------------------------------------------------------------------ |
||
1151 | void copy_group_walls(int old_group, int new_group) |
||
1152 | { |
||
1153 | group::segment_array_type_t::const_iterator bn = GroupList[new_group].segments.begin(); |
||
1154 | |||
1155 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
1156 | auto &vmwallptr = Walls.vmptr; |
||
1157 | range_for (const auto old_seg, GroupList[old_group].segments) |
||
1158 | { |
||
1159 | const auto new_seg = *bn++; |
||
1160 | auto &os = vcsegptr(old_seg)->shared_segment::sides; |
||
1161 | auto &ns = vmsegptr(new_seg)->shared_segment::sides; |
||
1162 | for (int j=0; j<MAX_SIDES_PER_SEGMENT; j++) { |
||
1163 | if (os[j].wall_num != wall_none) { |
||
1164 | ns[j].wall_num = Walls.get_count(); |
||
1165 | copy_old_wall_data_to_new(os[j].wall_num, Walls.get_count()); |
||
1166 | auto &w = *vmwallptr(static_cast<wallnum_t>(Walls.get_count())); |
||
1167 | w.segnum = new_seg; |
||
1168 | w.sidenum = j; |
||
1169 | Walls.set_count(Walls.get_count() + 1); |
||
1170 | Assert(Walls.get_count() < MAX_WALLS); |
||
1171 | } |
||
1172 | } |
||
1173 | } |
||
1174 | } |
||
1175 | |||
1176 | static int Validate_walls=1; |
||
1177 | |||
1178 | // -------------------------------------------------------------------------------------------------------- |
||
1179 | // This function should be in medwall.c. |
||
1180 | // Make sure all wall/segment connections are valid. |
||
1181 | void check_wall_validity(void) |
||
1182 | { |
||
1183 | int sidenum; |
||
1184 | |||
1185 | if (!Validate_walls) |
||
1186 | return; |
||
1187 | |||
1188 | auto &Walls = LevelUniqueWallSubsystemState.Walls; |
||
1189 | auto &vcwallptr = Walls.vcptr; |
||
1190 | range_for (const auto &&w, vcwallptr) |
||
1191 | { |
||
1192 | segnum_t segnum; |
||
1193 | segnum = w->segnum; |
||
1194 | sidenum = w->sidenum; |
||
1195 | |||
1196 | if (vcwallptr(vcsegptr(segnum)->shared_segment::sides[sidenum].wall_num) != w) { |
||
1197 | if (!Validate_walls) |
||
1198 | return; |
||
1199 | Int3(); // Error! Your mine has been invalidated! |
||
1200 | // Do not continue! Do not save! |
||
1201 | // Remember your last action and Contact Mike! |
||
1202 | // To continue, set the variable Validate_walls to 1 by doing: |
||
1203 | // /Validate_walls = 1 |
||
1204 | // Then do the usual /eip++;g |
||
1205 | |||
1206 | } |
||
1207 | } |
||
1208 | |||
1209 | std::array<bool, MAX_WALLS> wall_flags{}; |
||
1210 | |||
1211 | range_for (const auto &&segp, vmsegptridx) |
||
1212 | { |
||
1213 | if (segp->segnum != segment_none) |
||
1214 | for (int j=0; j<MAX_SIDES_PER_SEGMENT; j++) { |
||
1215 | // Check walls |
||
1216 | auto wall_num = segp->shared_segment::sides[j].wall_num; |
||
1217 | if (wall_num != wall_none) { |
||
1218 | if (wall_flags[wall_num] != 0) { |
||
1219 | if (!Validate_walls) |
||
1220 | return; |
||
1221 | Int3(); // Error! Your mine has been invalidated! |
||
1222 | // Do not continue! Do not save! |
||
1223 | // Remember your last action and Contact Mike! |
||
1224 | // To continue, set the variable Validate_walls to 1 by doing: |
||
1225 | // /Validate_walls = 1 |
||
1226 | // Then do the usual /eip++;g |
||
1227 | } |
||
1228 | |||
1229 | auto &w = *vcwallptr(wall_num); |
||
1230 | if (w.segnum != segp || w.sidenum != j) |
||
1231 | { |
||
1232 | if (!Validate_walls) |
||
1233 | return; |
||
1234 | Int3(); // Error! Your mine has been invalidated! |
||
1235 | // Do not continue! Do not save! |
||
1236 | // Remember your last action and Contact Mike! |
||
1237 | // To continue, set the variable Validate_walls to 1 by doing: |
||
1238 | // /Validate_walls = 1 |
||
1239 | // Then do the usual /eip++;g |
||
1240 | } |
||
1241 | |||
1242 | wall_flags[wall_num] = 1; |
||
1243 | } |
||
1244 | } |
||
1245 | |||
1246 | } |
||
1247 | } |
||
1248 |