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 |