Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  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.  
  1249.