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-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  18. */
  19.  
  20. #include "wall.h"
  21. #include "player.h"
  22. #include "text.h"
  23. #include "fireball.h"
  24. #include "textures.h"
  25. #include "newdemo.h"
  26. #include "multi.h"
  27. #include "gameseq.h"
  28. #include "physfs-serial.h"
  29. #include "gameseg.h"
  30. #include "hudmsg.h"
  31. #include "laser.h"              //      For seeing if a flare is stuck in a wall.
  32. #include "effects.h"
  33.  
  34. #include "d_enumerate.h"
  35. #include "d_range.h"
  36. #include "compiler-range_for.h"
  37. #include "segiter.h"
  38. #include "d_zip.h"
  39.  
  40. //      Special door on boss level which is locked if not in multiplayer...sorry for this awful solution --MK.
  41. #define BOSS_LOCKED_DOOR_LEVEL  7
  42. #define BOSS_LOCKED_DOOR_SEG            595
  43. #define BOSS_LOCKED_DOOR_SIDE   5
  44.  
  45. namespace dcx {
  46. unsigned Num_wall_anims;
  47. }
  48.  
  49. namespace dsx {
  50.  
  51. namespace {
  52.  
  53. struct ad_removal_predicate
  54. {
  55.         bool operator()(active_door &) const;
  56. };
  57.  
  58. struct find_active_door_predicate
  59. {
  60.         const wallnum_t wall_num;
  61.         explicit find_active_door_predicate(const wallnum_t i) :
  62.                 wall_num(i)
  63.         {
  64.         }
  65.         bool operator()(active_door &d) const {
  66.                 if (d.front_wallnum[0] == wall_num)
  67.                         return true;
  68.                 if (d.back_wallnum[0] == wall_num)
  69.                         return true;
  70.                 if (d.n_parts != 2)
  71.                         return false;
  72.                 if (d.front_wallnum[1] == wall_num)
  73.                         return true;
  74.                 if (d.back_wallnum[1] == wall_num)
  75.                         return true;
  76.                 return false;
  77.         }
  78. };
  79.  
  80. }
  81.  
  82. }
  83. #if defined(DXX_BUILD_DESCENT_II)
  84. #include "collide.h"
  85. namespace dsx {
  86. constexpr std::integral_constant<unsigned, F1_0> CLOAKING_WALL_TIME{};
  87.  
  88. namespace {
  89.  
  90. struct cwframe
  91. {
  92.         wall &w;
  93.         std::array<uvl, 4> &uvls;
  94.         cwframe(fvmsegptr &vmsegptr, wall &wr) :
  95.                 w(wr),
  96.                 uvls(vmsegptr(w.segnum)->unique_segment::sides[w.sidenum].uvls)
  97.         {
  98.         }
  99. };
  100.  
  101. struct cwresult
  102. {
  103.         bool remove;
  104.         bool record;
  105.         cwresult() = default;
  106.         explicit cwresult(bool r) :
  107.                 remove(false), record(r)
  108.         {
  109.         }
  110. };
  111.  
  112. struct cw_removal_predicate
  113. {
  114.         fvmsegptr &vmsegptr;
  115.         wall_array &Walls;
  116.         unsigned num_cloaking_walls = 0;
  117.         bool operator()(cloaking_wall &d);
  118.         cw_removal_predicate(fvmsegptr &v, wall_array &w) :
  119.                 vmsegptr(v), Walls(w)
  120.         {
  121.         }
  122. };
  123.  
  124. struct find_cloaked_wall_predicate
  125. {
  126.         const vmwallidx_t w;
  127.         find_cloaked_wall_predicate(const vmwallidx_t i) :
  128.                 w(i)
  129.         {
  130.         }
  131.         bool operator()(const cloaking_wall &cw) const
  132.         {
  133.                 return cw.front_wallnum == w || cw.back_wallnum == w;
  134.         }
  135. };
  136.  
  137. }
  138.  
  139. }
  140. #endif
  141.  
  142. static std::pair<uint_fast32_t, uint_fast32_t> get_transparency_check_values(const unique_side &side)
  143. {
  144.         if (const uint_fast32_t masked_tmap_num2 = side.tmap_num2 & 0x3FFF)
  145.                 return {masked_tmap_num2, BM_FLAG_SUPER_TRANSPARENT};
  146.         return {side.tmap_num, BM_FLAG_TRANSPARENT};
  147. }
  148.  
  149. // This function determines whether the current segment/side is transparent
  150. //              1 = YES
  151. //              0 = NO
  152. static uint_fast32_t check_transparency(const GameBitmaps_array &GameBitmaps, const Textures_array &Textures, const unique_side &side)
  153. {
  154.         const auto &&v = get_transparency_check_values(side);
  155.         return GameBitmaps[Textures[v.first].index].get_flag_mask(v.second);
  156. }
  157.  
  158. //-----------------------------------------------------------------
  159. // This function checks whether we can fly through the given side.
  160. //      In other words, whether or not we have a 'doorway'
  161. //       Flags:
  162. //              WID_FLY_FLAG                            1
  163. //              WID_RENDER_FLAG                 2
  164. //              WID_RENDPAST_FLAG                       4
  165. //       Return values:
  166. //              WID_WALL                                                2       // 0/1/0                wall   
  167. //              WID_TRANSPARENT_WALL            6       //      0/1/1           transparent wall
  168. //              WID_ILLUSORY_WALL                       3       //      1/1/0           illusory wall
  169. //              WID_TRANSILLUSORY_WALL  7       //      1/1/1           transparent illusory wall
  170. //              WID_NO_WALL                                     5       //      1/0/1           no wall, can fly through
  171. namespace dsx {
  172.  
  173. static WALL_IS_DOORWAY_result_t wall_is_doorway(const GameBitmaps_array &GameBitmaps, const Textures_array &Textures, fvcwallptr &vcwallptr, const shared_side &sside, const unique_side &uside)
  174. {
  175.         auto &w = *vcwallptr(sside.wall_num);
  176.         const auto type = w.type;
  177.         if (type == WALL_OPEN)
  178.                 return WID_NO_WALL;
  179.  
  180. #if defined(DXX_BUILD_DESCENT_II)
  181.         if (unlikely(type == WALL_CLOAKED))
  182.                 return WID_CLOAKED_WALL;
  183. #endif
  184.  
  185.         const auto flags = w.flags;
  186.         if (type == WALL_ILLUSION) {
  187.                 if (flags & WALL_ILLUSION_OFF)
  188.                         return WID_NO_WALL;
  189.                 else {
  190.                         if (check_transparency(GameBitmaps, Textures, uside))
  191.                                 return WID_TRANSILLUSORY_WALL;
  192.                         else
  193.                                 return WID_ILLUSORY_WALL;
  194.                 }
  195.         }
  196.  
  197.         if (type == WALL_BLASTABLE) {
  198.                 if (flags & WALL_BLASTED)
  199.                         return WID_TRANSILLUSORY_WALL;
  200.         }      
  201.         else
  202.         {
  203.         if (unlikely(flags & WALL_DOOR_OPENED))
  204.                 return WID_TRANSILLUSORY_WALL;
  205.         if (likely(type == WALL_DOOR) && unlikely(w.state == WALL_DOOR_OPENING))
  206.                 return WID_TRANSPARENT_WALL;
  207.         }
  208. // If none of the above flags are set, there is no doorway.
  209.         if (check_transparency(GameBitmaps, Textures, uside))
  210.                 return WID_TRANSPARENT_WALL;
  211.         else
  212.                 return WID_WALL; // There are children behind the door.
  213. }
  214.  
  215. WALL_IS_DOORWAY_result_t WALL_IS_DOORWAY(const GameBitmaps_array &GameBitmaps, const Textures_array &Textures, fvcwallptr &vcwallptr, const cscusegment seg, const uint_fast32_t side)
  216. {
  217.         const auto child = seg.s.children[side];
  218.         if (unlikely(child == segment_none))
  219.                 return WID_WALL;
  220.         if (unlikely(child == segment_exit))
  221.                 return WID_EXTERNAL;
  222.         auto &sside = seg.s.sides[side];
  223.         if (likely(sside.wall_num == wall_none))
  224.                 return WID_NO_WALL;
  225.         auto &uside = seg.u.sides[side];
  226.         return wall_is_doorway(GameBitmaps, Textures, vcwallptr, sside, uside);
  227. }
  228.  
  229. }
  230.  
  231. #if DXX_USE_EDITOR
  232. //-----------------------------------------------------------------
  233. // Initializes all the walls (in other words, no special walls)
  234. namespace dsx {
  235. void wall_init()
  236. {
  237.         init_exploding_walls();
  238.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  239.         Walls.set_count(0);
  240.         range_for (auto &w, Walls)
  241.         {
  242.                 w.segnum = segment_none;
  243.                 w.sidenum = -1;
  244.                 w.type = WALL_NORMAL;
  245.                 w.flags = 0;
  246.                 w.hps = 0;
  247.                 w.trigger = -1;
  248.                 w.clip_num = -1;
  249.                 w.linked_wall = -1;
  250.         }
  251.         auto &ActiveDoors = LevelUniqueWallSubsystemState.ActiveDoors;
  252.         ActiveDoors.set_count(0);
  253. #if defined(DXX_BUILD_DESCENT_II)
  254.         auto &CloakingWalls = LevelUniqueWallSubsystemState.CloakingWalls;
  255.         CloakingWalls.set_count(0);
  256. #endif
  257.  
  258. }
  259. }
  260. #endif
  261.  
  262. //set the tmap_num or tmap_num2 field for a wall/door
  263. void wall_set_tmap_num(const wclip &anim, const vmsegptridx_t seg, const unsigned side, const vmsegptridx_t csegp, const unsigned cside, const unsigned frame_num)
  264. {
  265.         const auto newdemo_state = Newdemo_state;
  266.         if (newdemo_state == ND_STATE_PLAYBACK)
  267.                 return;
  268.  
  269.         const auto tmap = anim.frames[frame_num];
  270.         auto &uside = seg->unique_segment::sides[side];
  271.         auto &cuside = csegp->unique_segment::sides[cside];
  272.         if (anim.flags & WCF_TMAP1)     {
  273.                 if (tmap != uside.tmap_num || tmap != cuside.tmap_num)
  274.                 {
  275.                         uside.tmap_num = cuside.tmap_num = tmap;
  276.                         if (newdemo_state == ND_STATE_RECORDING)
  277.                                 newdemo_record_wall_set_tmap_num1(seg,side,csegp,cside,tmap);
  278.                 }
  279.         } else  {
  280.                 assert(tmap != 0 && uside.tmap_num2 != 0);
  281.                 if (tmap != uside.tmap_num2 || tmap != cuside.tmap_num2)
  282.                 {
  283.                         uside.tmap_num2 = cuside.tmap_num2 = tmap;
  284.                         if (newdemo_state == ND_STATE_RECORDING)
  285.                                 newdemo_record_wall_set_tmap_num2(seg,side,csegp,cside,tmap);
  286.                 }
  287.         }
  288. }
  289.  
  290.  
  291. // -------------------------------------------------------------------------------
  292. //when the wall has used all its hitpoints, this will destroy it
  293. static void blast_blastable_wall(const vmsegptridx_t seg, const unsigned side, wall &w0)
  294. {
  295.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  296.         auto &Objects = LevelUniqueObjectState.Objects;
  297.         auto &Vertices = LevelSharedVertexState.get_vertices();
  298.         auto &vmobjptr = Objects.vmptr;
  299.         auto &sside = seg->shared_segment::sides[side];
  300.         const auto wall_num = sside.wall_num;
  301.         w0.hps = -1;    //say it's blasted
  302.  
  303.         const auto &&csegp = seg.absolute_sibling(seg->children[side]);
  304.         auto Connectside = find_connect_side(seg, csegp);
  305.         Assert(Connectside != side_none);
  306.         const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
  307.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  308.         auto &WallAnims = GameSharedState.WallAnims;
  309.         auto &imwallptr = Walls.imptr;
  310.         const auto &&w1 = imwallptr(cwall_num);
  311.         if (w1)
  312.                 LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, cwall_num);
  313.         LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, wall_num);
  314.         flush_fcd_cache();
  315.  
  316.         const auto a = w0.clip_num;
  317.         auto &wa = WallAnims[a];
  318.         //if this is an exploding wall, explode it
  319.         if (wa.flags & WCF_EXPLODES)
  320.         {
  321.                 auto &vcvertptr = Vertices.vcptr;
  322.                 explode_wall(vcvertptr, seg, side, w0);
  323.         }
  324.         else {
  325.                 //if not exploding, set final frame, and make door passable
  326.                 const auto n = wa.num_frames;
  327.                 w0.flags |= WALL_BLASTED;
  328.                 if (w1)
  329.                         w1->flags |= WALL_BLASTED;
  330.                 wall_set_tmap_num(wa, seg, side, csegp, Connectside, n - 1);
  331.         }
  332.  
  333. }
  334.  
  335.  
  336. //-----------------------------------------------------------------
  337. // Destroys a blastable wall.
  338. void wall_destroy(const vmsegptridx_t seg, const unsigned side)
  339. {
  340.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  341.         auto &vmwallptr = Walls.vmptr;
  342.         auto &w = *vmwallptr(seg->shared_segment::sides[side].wall_num);
  343.         if (w.type == WALL_BLASTABLE)
  344.                 blast_blastable_wall(seg, side, w);
  345.         else
  346.                 Error("Hey bub, you are trying to destroy an indestructable wall.");
  347. }
  348.  
  349. //-----------------------------------------------------------------
  350. // Deteriorate appearance of wall. (Changes bitmap (paste-ons))
  351. void wall_damage(const vmsegptridx_t seg, const unsigned side, fix damage)
  352. {
  353.         auto &WallAnims = GameSharedState.WallAnims;
  354.         int i;
  355.  
  356.         auto &sside = seg->shared_segment::sides[side];
  357.         const auto wall_num = sside.wall_num;
  358.         if (wall_num == wall_none) {
  359.                 return;
  360.         }
  361.  
  362.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  363.         auto &vmwallptr = Walls.vmptr;
  364.         auto &w0 = *vmwallptr(wall_num);
  365.         if (w0.type != WALL_BLASTABLE)
  366.                 return;
  367.        
  368.         if (!(w0.flags & WALL_BLASTED) && w0.hps >= 0)
  369.                 {
  370.                 const auto &&csegp = seg.absolute_sibling(seg->children[side]);
  371.                 auto Connectside = find_connect_side(seg, csegp);
  372.                 Assert(Connectside != side_none);
  373.                 const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
  374.                 auto &imwallptr = Walls.imptr;
  375.                 if (const auto &&w1p = imwallptr(cwall_num))
  376.                 {
  377.                         auto &w1 = *w1p;
  378.                         w1.hps -= damage;
  379.                 }
  380.                 w0.hps -= damage;
  381.  
  382.                 const auto a = w0.clip_num;
  383.                 const auto n = WallAnims[a].num_frames;
  384.                
  385.                 if (w0.hps < WALL_HPS*1/n) {
  386.                         blast_blastable_wall(seg, side, w0);
  387.                         if (Game_mode & GM_MULTI)
  388.                                 multi_send_door_open(seg, side, w0.flags);
  389.                 }
  390.                 else
  391.                         for (i=0;i<n;i++)
  392.                                 if (w0.hps < WALL_HPS*(n-i)/n)
  393.                                 {
  394.                                         wall_set_tmap_num(WallAnims[a], seg, side, csegp, Connectside, i);
  395.                                 }
  396.                 }
  397. }
  398.  
  399.  
  400. //-----------------------------------------------------------------
  401. // Opens a door
  402. namespace dsx {
  403. void wall_open_door(const vmsegptridx_t seg, const unsigned side)
  404. {
  405.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  406.         auto &Objects = LevelUniqueObjectState.Objects;
  407.         auto &Vertices = LevelSharedVertexState.get_vertices();
  408.         auto &WallAnims = GameSharedState.WallAnims;
  409.         auto &vmobjptr = Objects.vmptr;
  410.         active_door *d;
  411.  
  412.         auto &sside = seg->shared_segment::sides[side];
  413.         const auto wall_num = sside.wall_num;
  414.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  415.         auto &vmwallptr = Walls.vmptr;
  416.         wall *const w = vmwallptr(wall_num);
  417.         LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, wall_num);
  418.  
  419.         if ((w->state == WALL_DOOR_OPENING) ||          //already opening
  420.                  (w->state == WALL_DOOR_WAITING))               //open, waiting to close
  421.                 return;
  422. #if defined(DXX_BUILD_DESCENT_II)
  423.         if (w->state == WALL_DOOR_OPEN)                 //open, & staying open
  424.                 return;
  425. #endif
  426.  
  427.         auto &ActiveDoors = LevelUniqueWallSubsystemState.ActiveDoors;
  428.         auto &vmactdoorptr = ActiveDoors.vmptr;
  429.         if (w->state == WALL_DOOR_CLOSING) {            //closing, so reuse door
  430.                 const auto &&r = make_range(vmactdoorptr);
  431.                 const auto &&i = std::find_if(r.begin(), r.end(), find_active_door_predicate(wall_num));
  432.                 if (i == r.end())       // likely in demo playback or multiplayer
  433.                 {
  434.                         const auto c = ActiveDoors.get_count();
  435.                         ActiveDoors.set_count(c + 1);
  436.                         d = vmactdoorptr(static_cast<actdoornum_t>(c));
  437.                         d->time = 0;
  438.                 }
  439.                 else
  440.                 {
  441.                         d = *i;
  442.                         d->time = WallAnims[w->clip_num].play_time - d->time;
  443.                         if (d->time < 0)
  444.                                 d->time = 0;
  445.                 }
  446.         }
  447.         else {                                                                                  //create new door
  448.                 Assert(w->state == WALL_DOOR_CLOSED);
  449.                 const auto i = ActiveDoors.get_count();
  450.                 ActiveDoors.set_count(i + 1);
  451.                 d = vmactdoorptr(static_cast<actdoornum_t>(i));
  452.                 d->time = 0;
  453.         }
  454.  
  455.  
  456.         w->state = WALL_DOOR_OPENING;
  457.  
  458.         // So that door can't be shot while opening
  459.         const auto &&csegp = vcsegptr(seg->children[side]);
  460.         auto Connectside = find_connect_side(seg, csegp);
  461.         if (Connectside != side_none)
  462.         {
  463.                 const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
  464.                 auto &imwallptr = Walls.imptr;
  465.                 if (const auto &&w1 = imwallptr(cwall_num))
  466.                 {
  467.                         w1->state = WALL_DOOR_OPENING;
  468.                         d->back_wallnum[0] = cwall_num;
  469.                 }
  470.                 d->front_wallnum[0] = seg->shared_segment::sides[side].wall_num;
  471.         }
  472.         else
  473.                 con_printf(CON_URGENT, "Illegal Connectside %i in wall_open_door. Trying to hop over. Please check your level!", side);
  474.  
  475.         if (Newdemo_state == ND_STATE_RECORDING) {
  476.                 newdemo_record_door_opening(seg, side);
  477.         }
  478.  
  479.         if (w->linked_wall != wall_none)
  480.         {
  481.                 wall *const w2 = vmwallptr(w->linked_wall);
  482.  
  483.                 Assert(w2->linked_wall == seg->shared_segment::sides[side].wall_num);
  484.                 //Assert(!(w2->flags & WALL_DOOR_OPENING  ||  w2->flags & WALL_DOOR_OPENED));
  485.  
  486.                 w2->state = WALL_DOOR_OPENING;
  487.  
  488.                 const auto &&seg2 = vcsegptridx(w2->segnum);
  489.                 const auto &&csegp2 = vcsegptr(seg2->children[w2->sidenum]);
  490.                 Connectside = find_connect_side(seg2, csegp2);
  491.                 Assert(Connectside != side_none);
  492.                 const auto cwall_num = csegp2->shared_segment::sides[Connectside].wall_num;
  493.                 auto &imwallptr = Walls.imptr;
  494.                 if (const auto &&w3 = imwallptr(cwall_num))
  495.                         w3->state = WALL_DOOR_OPENING;
  496.  
  497.                 d->n_parts = 2;
  498.                 d->front_wallnum[1] = w->linked_wall;
  499.                 d->back_wallnum[1] = cwall_num;
  500.         }
  501.         else
  502.                 d->n_parts = 1;
  503.  
  504.  
  505.         if ( Newdemo_state != ND_STATE_PLAYBACK )
  506.         {
  507.                 // NOTE THE LINK TO ABOVE!!!!
  508.                 auto &vcvertptr = Vertices.vcptr;
  509.                 const auto &&cp = compute_center_point_on_side(vcvertptr, seg, side);
  510.                 const auto open_sound = WallAnims[w->clip_num].open_sound;
  511.                 if (open_sound > -1)
  512.                         digi_link_sound_to_pos(open_sound, seg, side, cp, 0, F1_0);
  513.  
  514.         }
  515. }
  516. }
  517.  
  518. #if defined(DXX_BUILD_DESCENT_II)
  519. namespace dsx {
  520.  
  521. //-----------------------------------------------------------------
  522. // start the transition from closed -> open wall
  523. void start_wall_cloak(const vmsegptridx_t seg, const unsigned side)
  524. {
  525.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  526.         auto &Vertices = LevelSharedVertexState.get_vertices();
  527.         cloaking_wall *d;
  528.  
  529.         if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
  530.  
  531.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  532.         const auto &&w = Walls.vmptridx(seg->shared_segment::sides[side].wall_num);
  533.  
  534.         if (w->type == WALL_OPEN || w->state == WALL_DOOR_CLOAKING)             //already open or cloaking
  535.                 return;
  536.  
  537.         const auto &&csegp = vcsegptr(seg->children[side]);
  538.         auto Connectside = find_connect_side(seg, csegp);
  539.         Assert(Connectside != side_none);
  540.         const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
  541.  
  542.         auto &CloakingWalls = LevelUniqueWallSubsystemState.CloakingWalls;
  543.         if (w->state == WALL_DOOR_DECLOAKING) { //decloaking, so reuse door
  544.                 const auto &&r = make_range(CloakingWalls.vmptr);
  545.                 const auto i = std::find_if(r.begin(), r.end(), find_cloaked_wall_predicate(w));
  546.                 if (i == r.end())
  547.                 {
  548.                         d_debugbreak();
  549.                         return;
  550.                 }
  551.                 d = *i;
  552.                 d->time = CLOAKING_WALL_TIME - d->time;
  553.         }
  554.         else if (w->state == WALL_DOOR_CLOSED) {        //create new door
  555.                 const clwallnum_t c = CloakingWalls.get_count();
  556.                 if (c >= CloakingWalls.size())
  557.                 {
  558.                         Int3();         //ran out of cloaking wall slots
  559.                         w->type = WALL_OPEN;
  560.                         if (const auto &&w1 = Walls.imptr(cwall_num))
  561.                                 w1->type = WALL_OPEN;
  562.                         return;
  563.                 }
  564.                 CloakingWalls.set_count(c + 1);
  565.                 d = CloakingWalls.vmptr(c);
  566.                 d->time = 0;
  567.         }
  568.         else {
  569.                 Int3();         //unexpected wall state
  570.                 return;
  571.         }
  572.  
  573.         w->state = WALL_DOOR_CLOAKING;
  574.         if (const auto &&w1 = Walls.imptr(cwall_num))
  575.                 w1->state = WALL_DOOR_CLOAKING;
  576.  
  577.         d->front_wallnum = seg->shared_segment::sides[side].wall_num;
  578.         d->back_wallnum = cwall_num;
  579.         Assert(w->linked_wall == wall_none);
  580.  
  581.         if ( Newdemo_state != ND_STATE_PLAYBACK ) {
  582.                 auto &vcvertptr = Vertices.vcptr;
  583.                 const auto &&cp = compute_center_point_on_side(vcvertptr, seg, side);
  584.                 digi_link_sound_to_pos( SOUND_WALL_CLOAK_ON, seg, side, cp, 0, F1_0 );
  585.         }
  586.  
  587.         for (auto &&[front_ls, back_ls, s0_uvls, s1_uvls] : zip(
  588.                         d->front_ls,
  589.                         d->back_ls,
  590.                         seg->unique_segment::sides[side].uvls,
  591.                         csegp->unique_segment::sides[Connectside].uvls
  592.         ))
  593.         {
  594.                 front_ls = s0_uvls.l;
  595.                 back_ls = s1_uvls.l;
  596.         }
  597. }
  598.  
  599. //-----------------------------------------------------------------
  600. // start the transition from open -> closed wall
  601. void start_wall_decloak(const vmsegptridx_t seg, const unsigned side)
  602. {
  603.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  604.         auto &Vertices = LevelSharedVertexState.get_vertices();
  605.         cloaking_wall *d;
  606.  
  607.         if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
  608.  
  609.         auto &sside = seg->shared_segment::sides[side];
  610.         assert(sside.wall_num != wall_none);    //Opening door on illegal wall
  611.  
  612.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  613.         const auto &&w = Walls.vmptridx(sside.wall_num);
  614.  
  615.         if (w->type == WALL_CLOSED || w->state == WALL_DOOR_DECLOAKING)         //already closed or decloaking
  616.                 return;
  617.  
  618.         auto &CloakingWalls = LevelUniqueWallSubsystemState.CloakingWalls;
  619.         if (w->state == WALL_DOOR_CLOAKING) {   //cloaking, so reuse door
  620.                 const auto &&r = make_range(CloakingWalls.vmptr);
  621.                 const auto i = std::find_if(r.begin(), r.end(), find_cloaked_wall_predicate(w));
  622.                 if (i == r.end())
  623.                 {
  624.                         d_debugbreak();
  625.                         return;
  626.                 }
  627.                 d = *i;
  628.                 d->time = CLOAKING_WALL_TIME - d->time;
  629.         }
  630.         else if (w->state == WALL_DOOR_CLOSED) {        //create new door
  631.                 const clwallnum_t c = CloakingWalls.get_count();
  632.                 if (c >= CloakingWalls.size())
  633.                 {
  634.                         Int3();         //ran out of cloaking wall slots
  635.                         /* what is this _doing_ here?
  636.                         w->type = WALL_CLOSED;
  637.                         Walls[csegp->sides[Connectside].wall_num].type = WALL_CLOSED;
  638.                         */
  639.                         return;
  640.                 }
  641.                 CloakingWalls.set_count(c + 1);
  642.                 d = CloakingWalls.vmptr(c);
  643.                 d->time = 0;
  644.         }
  645.         else {
  646.                 Int3();         //unexpected wall state
  647.                 return;
  648.         }
  649.  
  650.         w->state = WALL_DOOR_DECLOAKING;
  651.  
  652.         // So that door can't be shot while opening
  653.         const auto &&csegp = vcsegptr(seg->children[side]);
  654.         auto Connectside = find_connect_side(seg, csegp);
  655.         Assert(Connectside != side_none);
  656.         auto &csside = csegp->shared_segment::sides[Connectside];
  657.         const auto cwall_num = csside.wall_num;
  658.         if (const auto &&w1 = Walls.imptr(cwall_num))
  659.                 w1->state = WALL_DOOR_DECLOAKING;
  660.  
  661.         d->front_wallnum = seg->shared_segment::sides[side].wall_num;
  662.         d->back_wallnum = csside.wall_num;
  663.         Assert(w->linked_wall == wall_none);
  664.  
  665.         if ( Newdemo_state != ND_STATE_PLAYBACK ) {
  666.                 auto &vcvertptr = Vertices.vcptr;
  667.                 const auto &&cp = compute_center_point_on_side(vcvertptr, seg, side);
  668.                 digi_link_sound_to_pos( SOUND_WALL_CLOAK_OFF, seg, side, cp, 0, F1_0 );
  669.         }
  670.  
  671.         for (auto &&[front_ls, back_ls, s0_uvls, s1_uvls] : zip(
  672.                         d->front_ls,
  673.                         d->back_ls,
  674.                         seg->unique_segment::sides[side].uvls,
  675.                         csegp->unique_segment::sides[Connectside].uvls
  676.         ))
  677.         {
  678.                 front_ls = s0_uvls.l;
  679.                 back_ls = s1_uvls.l;
  680.         }
  681. }
  682.  
  683. }
  684. #endif
  685.  
  686. //-----------------------------------------------------------------
  687. // This function closes the specified door and restores the closed
  688. //  door texture.  This is called when the animation is done
  689. void wall_close_door_ref(fvmsegptridx &vmsegptridx, wall_array &Walls, const wall_animations_array &WallAnims, active_door &d)
  690. {
  691.         range_for (const auto p, partial_const_range(d.front_wallnum, d.n_parts))
  692.         {
  693.                 wall &w = *Walls.vmptr(p);
  694.  
  695.                 const auto &&seg = vmsegptridx(w.segnum);
  696.                 const auto side = w.sidenum;
  697.                 w.state = WALL_DOOR_CLOSED;
  698.  
  699.                 assert(seg->shared_segment::sides[side].wall_num != wall_none);         //Closing door on illegal wall
  700.  
  701.                 const auto &&csegp = seg.absolute_sibling(seg->children[side]);
  702.                 auto Connectside = find_connect_side(seg, csegp);
  703.                 Assert(Connectside != side_none);
  704.                 const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
  705.                 if (const auto &&w1 = Walls.imptr(cwall_num))
  706.                         w1->state = WALL_DOOR_CLOSED;
  707.  
  708.                 wall_set_tmap_num(WallAnims[w.clip_num], seg, side, csegp, Connectside, 0);
  709.         }
  710. }
  711.  
  712. static unsigned check_poke(fvcvertptr &vcvertptr, const object_base &obj, const shared_segment &seg, const unsigned side)
  713. {
  714.         //note: don't let objects with zero size block door
  715.         if (!obj.size)
  716.                 return 0;
  717.         return get_seg_masks(vcvertptr, obj.pos, seg, obj.size).sidemask & (1 << side);         //pokes through side!
  718. }
  719.  
  720. namespace dsx {
  721. static unsigned is_door_side_obstructed(fvcobjptridx &vcobjptridx, fvcsegptr &vcsegptr, const cscusegment seg, const unsigned side)
  722. {
  723.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  724.         auto &Vertices = LevelSharedVertexState.get_vertices();
  725.         auto &vcvertptr = Vertices.vcptr;
  726.         range_for (const object_base &obj, objects_in(seg, vcobjptridx, vcsegptr))
  727.         {
  728. #if defined(DXX_BUILD_DESCENT_II)
  729.                 if (obj.type == OBJ_WEAPON)
  730.                         continue;
  731.                 if (obj.type == OBJ_FIREBALL)
  732.                         continue;
  733. #endif
  734.                 if (const auto obstructed = check_poke(vcvertptr, obj, seg, side))
  735.                         return obstructed;
  736.         }
  737.         return 0;
  738. }
  739.  
  740. //returns true if door is obstructed (& thus cannot close)
  741. static unsigned is_door_obstructed(fvcobjptridx &vcobjptridx, fvcsegptr &vcsegptr, const vcsegptridx_t seg, const unsigned side)
  742. {
  743.         if (const auto obstructed = is_door_side_obstructed(vcobjptridx, vcsegptr, seg, side))
  744.                 return obstructed;
  745.         const auto &&csegp = vcsegptr(seg->children[side]);
  746.         const auto &&Connectside = find_connect_side(seg, csegp);
  747.         Assert(Connectside != side_none);
  748.         //go through each object in each of two segments, and see if
  749.         //it pokes into the connecting seg
  750.         return is_door_side_obstructed(vcobjptridx, vcsegptr, csegp, Connectside);
  751. }
  752.  
  753. #if defined(DXX_BUILD_DESCENT_II)
  754. //-----------------------------------------------------------------
  755. // Closes a door
  756. void wall_close_door(wall_array &Walls, const vmsegptridx_t seg, const unsigned side)
  757. {
  758.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  759.         auto &Objects = LevelUniqueObjectState.Objects;
  760.         auto &Vertices = LevelSharedVertexState.get_vertices();
  761.         auto &WallAnims = GameSharedState.WallAnims;
  762.         auto &vcobjptridx = Objects.vcptridx;
  763.         active_door *d;
  764.  
  765.         const auto wall_num = seg->shared_segment::sides[side].wall_num;
  766.         wall *const w = Walls.vmptr(wall_num);
  767.         if ((w->state == WALL_DOOR_CLOSING) ||          //already closing
  768.                  (w->state == WALL_DOOR_WAITING)        ||              //open, waiting to close
  769.                  (w->state == WALL_DOOR_CLOSED))                        //closed
  770.                 return;
  771.  
  772.         if (is_door_obstructed(vcobjptridx, vcsegptr, seg, side))
  773.                 return;
  774.  
  775.         auto &ActiveDoors = LevelUniqueWallSubsystemState.ActiveDoors;
  776.         auto &vmactdoorptr = ActiveDoors.vmptr;
  777.         if (w->state == WALL_DOOR_OPENING) {    //reuse door
  778.                 const auto &&r = make_range(vmactdoorptr);
  779.                 const auto &&i = std::find_if(r.begin(), r.end(), find_active_door_predicate(wall_num));
  780.                 if (i == r.end())
  781.                 {
  782.                         d_debugbreak();
  783.                         return;
  784.                 }
  785.                 d = *i;
  786.                 d->time = WallAnims[w->clip_num].play_time - d->time;
  787.  
  788.                 if (d->time < 0)
  789.                         d->time = 0;
  790.  
  791.         }
  792.         else {                                                                                  //create new door
  793.                 Assert(w->state == WALL_DOOR_OPEN);
  794.                 const auto i = ActiveDoors.get_count();
  795.                 ActiveDoors.set_count(i + 1);
  796.                 d = vmactdoorptr(static_cast<actdoornum_t>(i));
  797.                 d->time = 0;
  798.         }
  799.  
  800.         w->state = WALL_DOOR_CLOSING;
  801.  
  802.         // So that door can't be shot while opening
  803.         const auto &&csegp = vcsegptr(seg->children[side]);
  804.         const auto &&Connectside = find_connect_side(seg, csegp);
  805.         Assert(Connectside != side_none);
  806.         const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
  807.         if (const auto &&w1 = Walls.imptr(cwall_num))
  808.                 w1->state = WALL_DOOR_CLOSING;
  809.  
  810.         d->front_wallnum[0] = seg->shared_segment::sides[side].wall_num;
  811.         d->back_wallnum[0] = cwall_num;
  812.         if (Newdemo_state == ND_STATE_RECORDING) {
  813.                 newdemo_record_door_opening(seg, side);
  814.         }
  815.  
  816.         if (w->linked_wall != wall_none)
  817.         {
  818.                 Int3();         //don't think we ever used linked walls
  819.         }
  820.         else
  821.                 d->n_parts = 1;
  822.  
  823.  
  824.         if ( Newdemo_state != ND_STATE_PLAYBACK )
  825.         {
  826.                 // NOTE THE LINK TO ABOVE!!!!
  827.                 auto &vcvertptr = Vertices.vcptr;
  828.                 const auto &&cp = compute_center_point_on_side(vcvertptr, seg, side);
  829.                 const auto open_sound = WallAnims[w->clip_num].open_sound;
  830.                 if (open_sound > -1)
  831.                         digi_link_sound_to_pos(open_sound, seg, side, cp, 0, F1_0);
  832.  
  833.         }
  834. }
  835. #endif
  836.  
  837. //-----------------------------------------------------------------
  838. // Animates opening of a door.
  839. // Called in the game loop.
  840. static bool do_door_open(active_door &d)
  841. {
  842.         auto &Objects = LevelUniqueObjectState.Objects;
  843.         auto &WallAnims = GameSharedState.WallAnims;
  844.         auto &vmobjptr = Objects.vmptr;
  845.         bool remove = false;
  846.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  847.         auto &vmwallptr = Walls.vmptr;
  848.         for (unsigned p = 0; p < d.n_parts; ++p)
  849.         {
  850.                 int side;
  851.                 fix time_elapsed, time_total, one_frame;
  852.                 int i;
  853.  
  854.                 wall &w = *vmwallptr(d.front_wallnum[p]);
  855.                 LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, d.front_wallnum[p]);
  856.                 LevelUniqueStuckObjectState.kill_stuck_objects(vmobjptr, d.back_wallnum[p]);
  857.  
  858.                 const auto &&seg = vmsegptridx(w.segnum);
  859.                 side = w.sidenum;
  860.  
  861.                 if (seg->shared_segment::sides[side].wall_num == wall_none)
  862.                 {
  863.                         con_printf(CON_URGENT, "Trying to do_door_open on illegal wall %i. Please check your level!",side);
  864.                         continue;
  865.                 }
  866.  
  867.                 const auto &&csegp = seg.absolute_sibling(seg->children[side]);
  868.                 const auto &&Connectside = find_connect_side(seg, csegp);
  869.                 Assert(Connectside != side_none);
  870.  
  871.                 d.time += FrameTime;
  872.  
  873.                 time_elapsed = d.time;
  874.                 auto &wa = WallAnims[w.clip_num];
  875.                 const auto n = wa.num_frames;
  876.                 time_total = wa.play_time;
  877.  
  878.                 one_frame = time_total/n;      
  879.  
  880.                 i = time_elapsed/one_frame;
  881.  
  882.                 if (i < n)
  883.                         wall_set_tmap_num(wa, seg, side, csegp, Connectside, i);
  884.  
  885.                 const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
  886.                 auto &w1 = *vmwallptr(cwall_num);
  887.                 if (i> n/2) {
  888.                         w.flags |= WALL_DOOR_OPENED;
  889.                         w1.flags |= WALL_DOOR_OPENED;
  890.                 }
  891.  
  892.                 if (i >= n-1) {
  893.                         wall_set_tmap_num(wa, seg, side, csegp, Connectside, n - 1);
  894.  
  895.                         // If our door is not automatic just remove it from the list.
  896.                         if (!(w.flags & WALL_DOOR_AUTO)) {
  897.                                 remove = true;
  898. #if defined(DXX_BUILD_DESCENT_II)
  899.                                 w.state = WALL_DOOR_OPEN;
  900.                                 w1.state = WALL_DOOR_OPEN;
  901. #endif
  902.                         }
  903.                         else {
  904.                                 w.state = WALL_DOOR_WAITING;
  905.                                 w1.state = WALL_DOOR_WAITING;
  906.                         }
  907.                 }
  908.  
  909.         }
  910.         flush_fcd_cache();
  911.         return remove;
  912. }
  913.  
  914. //-----------------------------------------------------------------
  915. // Animates and processes the closing of a door.
  916. // Called from the game loop.
  917. static bool do_door_close(active_door &d)
  918. {
  919.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  920.         auto &Objects = LevelUniqueObjectState.Objects;
  921.         auto &Vertices = LevelSharedVertexState.get_vertices();
  922.         auto &vcobjptridx = Objects.vcptridx;
  923.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  924.         auto &WallAnims = GameSharedState.WallAnims;
  925.         auto &vmwallptr = Walls.vmptr;
  926.         auto &w0 = *vmwallptr(d.front_wallnum[0]);
  927.         const auto &&seg0 = vmsegptridx(w0.segnum);
  928.  
  929.         //check for objects in doorway before closing
  930.         if (w0.flags & WALL_DOOR_AUTO)
  931.                 if (is_door_obstructed(vcobjptridx, vcsegptr, seg0, w0.sidenum))
  932.                 {
  933. #if defined(DXX_BUILD_DESCENT_II)
  934.                         digi_kill_sound_linked_to_segment(w0.segnum, w0.sidenum, -1);
  935.                         wall_open_door(seg0, w0.sidenum);               //re-open door
  936. #endif
  937.                         return false;
  938.                 }
  939.  
  940.         bool played_sound = false;
  941.         bool remove = false;
  942.         range_for (const auto p, partial_const_range(d.front_wallnum, d.n_parts))
  943.         {
  944.                 int side;
  945.                 fix time_elapsed, time_total, one_frame;
  946.                 int i;
  947.  
  948.                 auto &wp = *vmwallptr(p);
  949.  
  950.                 const auto &&seg = vmsegptridx(wp.segnum);
  951.                 side = wp.sidenum;
  952.  
  953.                 if (seg->shared_segment::sides[side].wall_num == wall_none) {
  954.                         return false;
  955.                 }
  956.  
  957. #if defined(DXX_BUILD_DESCENT_I)
  958.                 //if here, must be auto door
  959. //don't assert here, because now we have triggers to close non-auto doors
  960.                 Assert(wp.flags & WALL_DOOR_AUTO);
  961. #endif
  962.  
  963.                 // Otherwise, close it.
  964.                 const auto &&csegp = seg.absolute_sibling(seg->children[side]);
  965.                 const auto &&Connectside = find_connect_side(seg, csegp);
  966.                 Assert(Connectside != side_none);
  967.  
  968.  
  969.                 auto &wa = WallAnims[wp.clip_num];
  970.                 if ( Newdemo_state != ND_STATE_PLAYBACK )
  971.                 {
  972.                         // NOTE THE LINK TO ABOVE!!
  973.                         if (!played_sound)      //only play one sound for linked doors
  974.                         {
  975.                                 played_sound = true;
  976.                                 if (d.time == 0)
  977.                                 {               //first time
  978.                                         const auto close_sound = wa.close_sound;
  979.                                         if (close_sound > -1)
  980.                                         {
  981.                                                 auto &vcvertptr = Vertices.vcptr;
  982.                                                 digi_link_sound_to_pos(close_sound, seg, side, compute_center_point_on_side(vcvertptr, seg, side), 0, F1_0);
  983.                                         }
  984.                                 }
  985.                         }
  986.                 }
  987.  
  988.                 d.time += FrameTime;
  989.  
  990.                 time_elapsed = d.time;
  991.                 const auto n = wa.num_frames;
  992.                 time_total = wa.play_time;
  993.  
  994.                 one_frame = time_total/n;      
  995.  
  996.                 i = n-time_elapsed/one_frame-1;
  997.  
  998.                 const auto cwall_num = csegp->shared_segment::sides[Connectside].wall_num;
  999.                 auto &w1 = *vmwallptr(cwall_num);
  1000.                 if (i < n/2) {
  1001.                         wp.flags &= ~WALL_DOOR_OPENED;
  1002.                         w1.flags &= ~WALL_DOOR_OPENED;
  1003.                 }
  1004.  
  1005.                 // Animate door.
  1006.                 if (i > 0) {
  1007.                         wall_set_tmap_num(wa, seg, side, csegp, Connectside, i);
  1008.  
  1009.                         wp.state = WALL_DOOR_CLOSING;
  1010.                         w1.state = WALL_DOOR_CLOSING;
  1011.                 } else
  1012.                         remove = true;
  1013.         }
  1014.  
  1015.         if (remove)
  1016.                 wall_close_door_ref(Segments.vmptridx, Walls, WallAnims, d);
  1017.         return remove;
  1018. }
  1019.  
  1020. static std::pair<wall *, wall *> wall_illusion_op(fvmwallptr &vmwallptr, const vcsegptridx_t seg, const unsigned side)
  1021. {
  1022.         const auto wall0 = seg->shared_segment::sides[side].wall_num;
  1023.         if (wall0 == wall_none)
  1024.                 return {nullptr, nullptr};
  1025.         const shared_segment &csegp = *seg.absolute_sibling(seg->children[side]);
  1026.         const auto &&cside = find_connect_side(seg, csegp);
  1027.         if (cside == side_none)
  1028.         {
  1029.                 assert(cside != side_none);
  1030.                 return {nullptr, nullptr};
  1031.         }
  1032.         const auto wall1 = csegp.sides[cside].wall_num;
  1033.         if (wall1 == wall_none)
  1034.                 return {nullptr, nullptr};
  1035.         return {vmwallptr(wall0), vmwallptr(wall1)};
  1036. }
  1037.  
  1038. template <typename F>
  1039. static void wall_illusion_op(fvmwallptr &vmwallptr, const vcsegptridx_t seg, const unsigned side, const F op)
  1040. {
  1041.         const auto &&r = wall_illusion_op(vmwallptr, seg, side);
  1042.         if (r.first)
  1043.         {
  1044.                 op(*r.first);
  1045.                 op(*r.second);
  1046.         }
  1047. }
  1048.  
  1049. //-----------------------------------------------------------------
  1050. // Turns off an illusionary wall (This will be used primarily for
  1051. //  wall switches or triggers that can turn on/off illusionary walls.)
  1052. void wall_illusion_off(fvmwallptr &vmwallptr, const vcsegptridx_t seg, const unsigned side)
  1053. {
  1054.         const auto &&op = [](wall &w) {
  1055.                 w.flags |= WALL_ILLUSION_OFF;
  1056.         };
  1057.         wall_illusion_op(vmwallptr, seg, side, op);
  1058. }
  1059.  
  1060. //-----------------------------------------------------------------
  1061. // Turns on an illusionary wall (This will be used primarily for
  1062. //  wall switches or triggers that can turn on/off illusionary walls.)
  1063. void wall_illusion_on(fvmwallptr &vmwallptr, const vcsegptridx_t seg, const unsigned side)
  1064. {
  1065.         const auto &&op = [](wall &w) {
  1066.                 w.flags &= ~WALL_ILLUSION_OFF;
  1067.         };
  1068.         wall_illusion_op(vmwallptr, seg, side, op);
  1069. }
  1070.  
  1071. }
  1072.  
  1073. //      -----------------------------------------------------------------------------
  1074. //      Allowed to open the normally locked special boss door if in multiplayer mode.
  1075. static int special_boss_opening_allowed(segnum_t segnum, int sidenum)
  1076. {
  1077.         if (Game_mode & GM_MULTI)
  1078.                 return (Current_level_num == BOSS_LOCKED_DOOR_LEVEL) && (segnum == BOSS_LOCKED_DOOR_SEG) && (sidenum == BOSS_LOCKED_DOOR_SIDE);
  1079.         else
  1080.                 return 0;
  1081. }
  1082.  
  1083. //-----------------------------------------------------------------
  1084. // Determines what happens when a wall is shot
  1085. //returns info about wall.  see wall.h for codes
  1086. //obj is the object that hit...either a weapon or the player himself
  1087. //playernum is the number the player who hit the wall or fired the weapon,
  1088. //or -1 if a robot fired the weapon
  1089. namespace dsx {
  1090. wall_hit_process_t wall_hit_process(const player_flags powerup_flags, const vmsegptridx_t seg, const unsigned side, const fix damage, const unsigned playernum, const object &obj)
  1091. {
  1092.         fix     show_message;
  1093.  
  1094.         // If it is not a "wall" then just return.
  1095.         const auto wall_num = seg->shared_segment::sides[side].wall_num;
  1096.         if (wall_num == wall_none)
  1097.                 return wall_hit_process_t::WHP_NOT_SPECIAL;
  1098.  
  1099.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1100.         auto &vmwallptr = Walls.vmptr;
  1101.         wall *const w = vmwallptr(wall_num);
  1102.  
  1103.         if ( Newdemo_state == ND_STATE_RECORDING )
  1104.                 newdemo_record_wall_hit_process( seg, side, damage, playernum );
  1105.  
  1106.         if (w->type == WALL_BLASTABLE) {
  1107. #if defined(DXX_BUILD_DESCENT_II)
  1108.                 if (obj.ctype.laser_info.parent_type == OBJ_PLAYER)
  1109. #endif
  1110.                         wall_damage(seg, side, damage);
  1111.                 return wall_hit_process_t::WHP_BLASTABLE;
  1112.         }
  1113.  
  1114.         if (playernum != Player_num)    //return if was robot fire
  1115.                 return wall_hit_process_t::WHP_NOT_SPECIAL;
  1116.  
  1117.         //      Determine whether player is moving forward.  If not, don't say negative
  1118.         //      messages because he probably didn't intentionally hit the door.
  1119.         if (obj.type == OBJ_PLAYER)
  1120.                 show_message = (vm_vec_dot(obj.orient.fvec, obj.mtype.phys_info.velocity) > 0);
  1121. #if defined(DXX_BUILD_DESCENT_II)
  1122.         else if (obj.type == OBJ_ROBOT)
  1123.                 show_message = 0;
  1124.         else if (obj.type == OBJ_WEAPON && obj.ctype.laser_info.parent_type == OBJ_ROBOT)
  1125.                 show_message = 0;
  1126. #endif
  1127.         else
  1128.                 show_message = 1;
  1129.  
  1130.         /* Set key_color only after the type matches, since TXT_* are macros
  1131.          * that trigger a load from memory.  Use operator,() to suppress the
  1132.          * truth test on the second branch since the compiler cannot prove
  1133.          * that the loaded value will always be non-null.
  1134.          */
  1135.         const char *key_color;
  1136.         if (
  1137.                 (w->keys == KEY_BLUE && (key_color = TXT_BLUE, true)) ||
  1138.                 (w->keys == KEY_GOLD && (key_color = TXT_YELLOW, true)) ||
  1139.                 (w->keys == KEY_RED && (key_color = TXT_RED, true))
  1140.         )
  1141.         {
  1142.                 if (!(powerup_flags & static_cast<PLAYER_FLAG>(w->keys)))
  1143.                 {
  1144.                         static_assert(KEY_BLUE == static_cast<unsigned>(PLAYER_FLAGS_BLUE_KEY), "BLUE key flag mismatch");
  1145.                         static_assert(KEY_GOLD == static_cast<unsigned>(PLAYER_FLAGS_GOLD_KEY), "GOLD key flag mismatch");
  1146.                         static_assert(KEY_RED == static_cast<unsigned>(PLAYER_FLAGS_RED_KEY), "RED key flag mismatch");
  1147.                                 if (show_message)
  1148.                                         HUD_init_message(HM_DEFAULT, "%s %s",key_color,TXT_ACCESS_DENIED);
  1149.                         return wall_hit_process_t::WHP_NO_KEY;
  1150.                 }
  1151.         }
  1152.  
  1153.         if (w->type == WALL_DOOR)
  1154.         {
  1155.                 if ((w->flags & WALL_DOOR_LOCKED ) && !(special_boss_opening_allowed(seg, side)) ) {
  1156.                                 if (show_message)
  1157.                                         HUD_init_message_literal(HM_DEFAULT, TXT_CANT_OPEN_DOOR);
  1158.                         return wall_hit_process_t::WHP_NO_KEY;
  1159.                 }
  1160.                 else {
  1161.                         if (w->state != WALL_DOOR_OPENING)
  1162.                         {
  1163.                                 wall_open_door(seg, side);
  1164.                                 if (Game_mode & GM_MULTI)
  1165.                                 {
  1166.                                         int flags;
  1167. #if defined(DXX_BUILD_DESCENT_I)
  1168.                                         flags = 0;
  1169. #elif defined(DXX_BUILD_DESCENT_II)
  1170.                                         flags = w->flags;
  1171. #endif
  1172.                                         multi_send_door_open(seg, side,flags);
  1173.                                 }
  1174.                         }
  1175.                         return wall_hit_process_t::WHP_DOOR;
  1176.                        
  1177.                 }
  1178.         }
  1179.         return wall_hit_process_t::WHP_NOT_SPECIAL;             //default is treat like normal wall
  1180. }
  1181. }
  1182.  
  1183. //-----------------------------------------------------------------
  1184. // Opens doors/destroys wall/shuts off triggers.
  1185. void wall_toggle(fvmwallptr &vmwallptr, const vmsegptridx_t segp, const unsigned side)
  1186. {
  1187.         if (side >= MAX_SIDES_PER_SEGMENT)
  1188.         {
  1189. #ifndef NDEBUG
  1190.                 Warning("Can't toggle side %u of segment %d (%u)!\n", side, static_cast<segnum_t>(segp), Highest_segment_index);
  1191. #endif
  1192.                 return;
  1193.         }
  1194.         const auto wall_num = segp->shared_segment::sides[side].wall_num;
  1195.         if (wall_num == wall_none)
  1196.         {
  1197.                 LevelError("Ignoring attempt to toggle wall in segment %hu, side %u: no wall exists there.", segp.get_unchecked_index(), side);
  1198.                 return;
  1199.         }
  1200.  
  1201.         if ( Newdemo_state == ND_STATE_RECORDING )
  1202.                 newdemo_record_wall_toggle(segp, side);
  1203.  
  1204.         wall *const w = vmwallptr(wall_num);
  1205.         if (w->type == WALL_BLASTABLE)
  1206.                 wall_destroy(segp, side);
  1207.  
  1208.         if (w->type == WALL_DOOR && w->state == WALL_DOOR_CLOSED)
  1209.                 wall_open_door(segp, side);
  1210. }
  1211.  
  1212. bool ad_removal_predicate::operator()(active_door &d) const
  1213. {
  1214. #if defined(DXX_BUILD_DESCENT_II)
  1215.         auto &Objects = LevelUniqueObjectState.Objects;
  1216.         auto &vcobjptridx = Objects.vcptridx;
  1217. #endif
  1218.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1219.         wall &w = *Walls.vmptr(d.front_wallnum[0]);
  1220.         if (w.state == WALL_DOOR_OPENING)
  1221.                 return do_door_open(d);
  1222.         else if (w.state == WALL_DOOR_CLOSING)
  1223.                 return do_door_close(d);
  1224.         else if (w.state == WALL_DOOR_WAITING) {
  1225.                 d.time += FrameTime;
  1226.                 // set flags to fix occasional netgame problem where door is waiting to close but open flag isn't set
  1227.                 w.flags |= WALL_DOOR_OPENED;
  1228.                 if (wall *const w1 = Walls.imptr(d.back_wallnum[0]))
  1229.                         w1->flags |= WALL_DOOR_OPENED;
  1230.                 if (d.time > DOOR_WAIT_TIME)
  1231. #if defined(DXX_BUILD_DESCENT_II)
  1232.                         if (!is_door_obstructed(vcobjptridx, vcsegptr, vcsegptridx(w.segnum), w.sidenum))
  1233. #endif
  1234.                         {
  1235.                                 w.state = WALL_DOOR_CLOSING;
  1236.                                 d.time = 0;
  1237.                         }
  1238.         }
  1239.         return false;
  1240. }
  1241.  
  1242. #if defined(DXX_BUILD_DESCENT_II)
  1243. static void copy_cloaking_wall_light_to_wall(std::array<uvl, 4> &back_uvls, std::array<uvl, 4> &front_uvls, const cloaking_wall &d)
  1244. {
  1245.         range_for (const uint_fast32_t i, xrange(4u))
  1246.         {
  1247.                 back_uvls[i].l = d.back_ls[i];
  1248.                 front_uvls[i].l = d.front_ls[i];
  1249.         }
  1250. }
  1251.  
  1252. static void scale_cloaking_wall_light_to_wall(std::array<uvl, 4> &back_uvls, std::array<uvl, 4> &front_uvls, const cloaking_wall &d, const fix light_scale)
  1253. {
  1254.         range_for (const uint_fast32_t i, xrange(4u))
  1255.         {
  1256.                 back_uvls[i].l = fixmul(d.back_ls[i], light_scale);
  1257.                 front_uvls[i].l = fixmul(d.front_ls[i], light_scale);
  1258.         }
  1259. }
  1260.  
  1261. static cwresult do_cloaking_wall_frame(const bool initial, cloaking_wall &d, const cwframe front, const cwframe back)
  1262. {
  1263.         cwresult r(initial);
  1264.         if (d.time > CLOAKING_WALL_TIME) {
  1265.                 front.w.type = back.w.type = WALL_OPEN;
  1266.                 front.w.state = back.w.state = WALL_DOOR_CLOSED;                //why closed? why not?
  1267.                 r.remove = true;
  1268.         }
  1269.         else if (d.time > CLOAKING_WALL_TIME/2) {
  1270.                 const int8_t cloak_value = ((d.time - CLOAKING_WALL_TIME / 2) * (GR_FADE_LEVELS - 2)) / (CLOAKING_WALL_TIME / 2);
  1271.                 if (front.w.cloak_value != cloak_value)
  1272.                 {
  1273.                         r.record = true;
  1274.                         front.w.cloak_value = back.w.cloak_value = cloak_value;
  1275.                 }
  1276.  
  1277.                 if (front.w.type != WALL_CLOAKED)
  1278.                 {               //just switched
  1279.                         front.w.type = back.w.type = WALL_CLOAKED;
  1280.                         copy_cloaking_wall_light_to_wall(back.uvls, front.uvls, d);
  1281.                 }
  1282.         }
  1283.         else {          //fading out
  1284.                 fix light_scale;
  1285.                 light_scale = fixdiv(CLOAKING_WALL_TIME / 2 - d.time, CLOAKING_WALL_TIME / 2);
  1286.                 scale_cloaking_wall_light_to_wall(back.uvls, front.uvls, d, light_scale);
  1287.         }
  1288.         return r;
  1289. }
  1290.  
  1291. static cwresult do_decloaking_wall_frame(const bool initial, cloaking_wall &d, const cwframe front, const cwframe back)
  1292. {
  1293.         cwresult r(initial);
  1294.         if (d.time > CLOAKING_WALL_TIME) {
  1295.  
  1296.                 back.w.state = WALL_DOOR_CLOSED;
  1297.                 front.w.state = WALL_DOOR_CLOSED;
  1298.                 copy_cloaking_wall_light_to_wall(back.uvls, front.uvls, d);
  1299.                 r.remove = true;
  1300.         }
  1301.         else if (d.time > CLOAKING_WALL_TIME/2) {               //fading in
  1302.                 fix light_scale;
  1303.                 front.w.type = back.w.type = WALL_CLOSED;
  1304.  
  1305.                 light_scale = fixdiv(d.time - CLOAKING_WALL_TIME / 2, CLOAKING_WALL_TIME / 2);
  1306.                 scale_cloaking_wall_light_to_wall(back.uvls, front.uvls, d, light_scale);
  1307.         }
  1308.         else {          //cloaking in
  1309.                 const int8_t cloak_value = ((CLOAKING_WALL_TIME / 2 - d.time) * (GR_FADE_LEVELS - 2)) / (CLOAKING_WALL_TIME / 2);
  1310.                 if (front.w.cloak_value != cloak_value)
  1311.                 {
  1312.                         front.w.cloak_value = back.w.cloak_value = cloak_value;
  1313.                         r.record = true;
  1314.                 }
  1315.                 front.w.type = WALL_CLOAKED;
  1316.                 back.w.type = WALL_CLOAKED;
  1317.         }
  1318.         return r;
  1319. }
  1320.  
  1321. bool cw_removal_predicate::operator()(cloaking_wall &d)
  1322. {
  1323.         const cwframe front(vmsegptr, *Walls.vmptr(d.front_wallnum));
  1324.         const auto &&wpback = Walls.imptr(d.back_wallnum);
  1325.         const cwframe back = (wpback ? cwframe(vmsegptr, *wpback) : front);
  1326.         const bool initial = (d.time == 0);
  1327.         d.time += FrameTime;
  1328.  
  1329.         cwresult r;
  1330.         if (front.w.state == WALL_DOOR_CLOAKING)
  1331.                 r = do_cloaking_wall_frame(initial, d, front, back);
  1332.         else if (front.w.state == WALL_DOOR_DECLOAKING)
  1333.                 r = do_decloaking_wall_frame(initial, d, front, back);
  1334.         else
  1335.         {
  1336.                 d_debugbreak(); //unexpected wall state
  1337.                 return false;
  1338.         }
  1339.         if (r.record)
  1340.         {
  1341.                 // check if the actual cloak_value changed in this frame to prevent redundant recordings and wasted bytes
  1342.                 if (Newdemo_state == ND_STATE_RECORDING && r.record)
  1343.                         newdemo_record_cloaking_wall(d.front_wallnum, d.back_wallnum, front.w.type, front.w.state, front.w.cloak_value, front.uvls[0].l, front.uvls[1].l, front.uvls[2].l, front.uvls[3].l);
  1344.         }
  1345.         if (!r.remove)
  1346.                 ++ num_cloaking_walls;
  1347.         return r.remove;
  1348. }
  1349. #endif
  1350.  
  1351. namespace dsx {
  1352. static void process_exploding_walls()
  1353. {
  1354.         if (Newdemo_state == ND_STATE_PLAYBACK)
  1355.                 return;
  1356.         if (unsigned num_exploding_walls = Num_exploding_walls)
  1357.         {
  1358.                 auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1359.                 range_for (auto &&wp, Walls.vmptr)
  1360.                 {
  1361.                         auto &w1 = *wp;
  1362.                         if (w1.flags & WALL_EXPLODING)
  1363.                         {
  1364.                                 assert(num_exploding_walls);
  1365.                                 const auto n = do_exploding_wall_frame(w1);
  1366.                                 num_exploding_walls -= n;
  1367.                                 if (!num_exploding_walls)
  1368.                                         break;
  1369.                         }
  1370.                 }
  1371.         }
  1372. }
  1373.  
  1374. void wall_frame_process()
  1375. {
  1376.         process_exploding_walls();
  1377.         {
  1378.                 auto &ActiveDoors = LevelUniqueWallSubsystemState.ActiveDoors;
  1379.                 const auto &&r = partial_range(ActiveDoors, ActiveDoors.get_count());
  1380.                 auto &&i = std::remove_if(r.begin(), r.end(), ad_removal_predicate());
  1381.                 ActiveDoors.set_count(std::distance(r.begin(), i));
  1382.         }
  1383. #if defined(DXX_BUILD_DESCENT_II)
  1384.         if (Newdemo_state != ND_STATE_PLAYBACK)
  1385.         {
  1386.                 auto &CloakingWalls = LevelUniqueWallSubsystemState.CloakingWalls;
  1387.                 const auto &&r = partial_range(CloakingWalls, CloakingWalls.get_count());
  1388.                 auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1389.                 cw_removal_predicate rp{Segments.vmptr, Walls};
  1390.                 std::remove_if(r.begin(), r.end(), std::ref(rp));
  1391.                 CloakingWalls.set_count(rp.num_cloaking_walls);
  1392.         }
  1393. #endif
  1394. }
  1395.  
  1396. d_level_unique_stuck_object_state LevelUniqueStuckObjectState;
  1397.  
  1398. //      An object got stuck in a door (like a flare).
  1399. //      Add global entry.
  1400. void d_level_unique_stuck_object_state::add_stuck_object(fvcwallptr &vcwallptr, const vmobjptridx_t objp, const shared_segment &segp, const unsigned sidenum)
  1401. {
  1402.         const auto wallnum = segp.sides[sidenum].wall_num;
  1403.         if (wallnum != wall_none)
  1404.         {
  1405.                 if (vcwallptr(wallnum)->flags & WALL_BLASTED)
  1406.                 {
  1407.                         objp->flags |= OF_SHOULD_BE_DEAD;
  1408.                         return;
  1409.                 }
  1410.                 if (Num_stuck_objects >= Stuck_objects.size())
  1411.                 {
  1412.                         assert(Num_stuck_objects <= Stuck_objects.size());
  1413.                         con_printf(CON_NORMAL, "%s:%u: all stuck objects are busy; terminating %hu early", __FILE__, __LINE__, objp.get_unchecked_index());
  1414.                         objp->flags |= OF_SHOULD_BE_DEAD;
  1415.                         return;
  1416.                 }
  1417.                 auto &so = Stuck_objects[Num_stuck_objects++];
  1418.                 so.wallnum = wallnum;
  1419.                 so.objnum = objp;
  1420.         }
  1421. }
  1422.  
  1423. void d_level_unique_stuck_object_state::remove_stuck_object(const vcobjidx_t obj)
  1424. {
  1425.         auto &&pr = partial_range(Stuck_objects, Num_stuck_objects);
  1426.         const auto predicate = [obj](const stuckobj &so) { return so.objnum == obj; };
  1427.         const auto i = std::find_if(pr.begin(), pr.end(), predicate);
  1428.         if (i == pr.end())
  1429.                 /* Objects enter this function if they are able to become stuck,
  1430.                  * without regard to whether they actually are stuck.  If the
  1431.                  * object terminated without being stuck in a wall, then it will
  1432.                  * not be found in Stuck_objects.
  1433.                  */
  1434.                 return;
  1435.         /* If pr.begin() == pr.end(), then i == pr.end(), and this line
  1436.          * cannot be reached.
  1437.          *
  1438.          * If pr.begin() != pr.end(), then prev(pr.end()) must point to a
  1439.          * valid element.
  1440.          *
  1441.          * Move that valid element to the location vacated by the removed
  1442.          * object.  This may be a self-move if the removed object is the
  1443.          * last object.
  1444.          */
  1445.         auto &last_element = *std::prev(pr.end());
  1446.         static_assert(std::is_trivially_move_assignable<stuckobj>::value, "stuckobj move may require a check to prevent self-move");
  1447.         *i = std::move(last_element);
  1448.         DXX_POISON_VAR(last_element.wallnum, 0xcc);
  1449.         DXX_POISON_VAR(last_element.objnum, 0xcc);
  1450.         -- Num_stuck_objects;
  1451. }
  1452.  
  1453. //      ----------------------------------------------------------------------------------------------------
  1454. //      Door with wall index wallnum is opening, kill all objects stuck in it.
  1455. void d_level_unique_stuck_object_state::kill_stuck_objects(fvmobjptr &vmobjptr, const vcwallidx_t wallnum)
  1456. {
  1457.         if (!Num_stuck_objects)
  1458.                 return;
  1459.         auto &&pr = partial_range(Stuck_objects, Num_stuck_objects);
  1460.         const auto predicate = [&vmobjptr, wallnum](const stuckobj &so)
  1461.         {
  1462.                 if (so.wallnum != wallnum)
  1463.                         return false;
  1464.                 auto &obj = *vmobjptr(so.objnum);
  1465. #if defined(DXX_BUILD_DESCENT_I)
  1466. #define DXX_WEAPON_LIFELEFT     F1_0/4
  1467. #elif defined(DXX_BUILD_DESCENT_II)
  1468. #define DXX_WEAPON_LIFELEFT     F1_0/8
  1469. #endif
  1470.                 assert(obj.type == OBJ_WEAPON);
  1471.                 assert(obj.movement_type == MT_PHYSICS);
  1472.                 assert(obj.mtype.phys_info.flags & PF_STICK);
  1473.                 obj.lifeleft = DXX_WEAPON_LIFELEFT;
  1474.                 return true;
  1475.         };
  1476.         const auto i = std::remove_if(pr.begin(), pr.end(), predicate);
  1477.         static_assert(std::is_trivially_destructible<stuckobj>::value, "stuckobj destructor not called");
  1478.         Num_stuck_objects = std::distance(pr.begin(), i);
  1479.         std::array<stuckobj, 1> empty;
  1480.         DXX_POISON_VAR(empty, 0xcc);
  1481.         std::fill(i, pr.end(), empty[0]);
  1482. }
  1483.  
  1484. }
  1485.  
  1486. namespace dcx {
  1487. // -----------------------------------------------------------------------------------
  1488. // Initialize stuck objects array.  Called at start of level
  1489. void d_level_unique_stuck_object_state::init_stuck_objects()
  1490. {
  1491.         DXX_POISON_VAR(Stuck_objects, 0xcc);
  1492.         Num_stuck_objects = 0;
  1493. }
  1494. }
  1495.  
  1496. #if defined(DXX_BUILD_DESCENT_II)
  1497. // -----------------------------------------------------------------------------------
  1498. #define MAX_BLAST_GLASS_DEPTH   5
  1499.  
  1500. namespace dsx {
  1501. namespace {
  1502.  
  1503. class blast_nearby_glass_context
  1504. {
  1505.         using TmapInfo_array = d_level_unique_tmap_info_state::TmapInfo_array;
  1506.         const object &obj;
  1507.         const fix damage;
  1508.         const d_eclip_array &Effects;
  1509.         const GameBitmaps_array &GameBitmaps;
  1510.         const Textures_array &Textures;
  1511.         const TmapInfo_array &TmapInfo;
  1512.         const d_vclip_array &Vclip;
  1513.         fvcvertptr &vcvertptr;
  1514.         fvcwallptr &vcwallptr;
  1515.         visited_segment_bitarray_t visited;
  1516.         unsigned can_blast(const int16_t &tmap_num2) const;
  1517. public:
  1518.         blast_nearby_glass_context(const object &obj, const fix damage, const d_eclip_array &Effects, const GameBitmaps_array &GameBitmaps, const Textures_array &Textures, const TmapInfo_array &TmapInfo, const d_vclip_array &Vclip, fvcvertptr &vcvertptr, fvcwallptr &vcwallptr) :
  1519.                 obj(obj), damage(damage), Effects(Effects), GameBitmaps(GameBitmaps),
  1520.                 Textures(Textures), TmapInfo(TmapInfo), Vclip(Vclip),
  1521.                 vcvertptr(vcvertptr), vcwallptr(vcwallptr), visited{}
  1522.         {
  1523.         }
  1524.         blast_nearby_glass_context(const blast_nearby_glass_context &) = delete;
  1525.         blast_nearby_glass_context &operator=(const blast_nearby_glass_context &) = delete;
  1526.         void process_segment(vmsegptridx_t segp, unsigned steps_remaining);
  1527. };
  1528.  
  1529. unsigned blast_nearby_glass_context::can_blast(const int16_t &tmap_num2) const
  1530. {
  1531.         const auto tm = tmap_num2 & 0x3fff;                     //tm without flags
  1532.         auto &ti = TmapInfo[tm];
  1533.         const auto ec = ti.eclip_num;
  1534.         if (ec == eclip_none)
  1535.         {
  1536.                 return ti.destroyed != -1;
  1537.         }
  1538.         else
  1539.         {
  1540.                 auto &e = Effects[ec];
  1541.                 return e.dest_bm_num != ~0u && !(e.flags & EF_ONE_SHOT);
  1542.         }
  1543. }
  1544.  
  1545. void blast_nearby_glass_context::process_segment(const vmsegptridx_t segp, const unsigned steps_remaining)
  1546. {
  1547.         visited[segp] = true;
  1548.  
  1549.         const auto &objp = obj;
  1550.         range_for (const auto &&e, enumerate(segp->unique_segment::sides))
  1551.         {
  1552.                 fix                     dist;
  1553.  
  1554.                 //      Process only walls which have glass.
  1555.                 auto &&uside = e.value;
  1556.                 if (const auto &tmap_num2 = uside.tmap_num2)
  1557.                 {
  1558.                         if (can_blast(tmap_num2))
  1559.                         {
  1560.                                 auto &&sidenum = e.idx;
  1561.                                 const auto &&pnt = compute_center_point_on_side(vcvertptr, segp, sidenum);
  1562.                                 dist = vm_vec_dist_quick(pnt, objp.pos);
  1563.                                 if (dist < damage/2) {
  1564.                                         dist = find_connected_distance(pnt, segp, objp.pos, segp.absolute_sibling(objp.segnum), MAX_BLAST_GLASS_DEPTH, WID_RENDPAST_FLAG);
  1565.                                         if ((dist > 0) && (dist < damage/2))
  1566.                                         {
  1567.                                                 assert(objp.type == OBJ_WEAPON);
  1568.                                                 check_effect_blowup(LevelSharedSegmentState.DestructibleLights, Vclip, segp, sidenum, pnt, objp.ctype.laser_info, 1, 0);
  1569.                                         }
  1570.                                 }
  1571.                         }
  1572.                 }
  1573.         }
  1574.  
  1575.         const unsigned next_steps_remaining = steps_remaining - 1;
  1576.         if (!next_steps_remaining)
  1577.                 return;
  1578.         range_for (const auto &&e, enumerate(segp->children))
  1579.         {
  1580.                 auto &&segnum = e.value;
  1581.                 if (segnum != segment_none) {
  1582.                         if (!visited[segnum]) {
  1583.                                 auto &&i = e.idx;
  1584.                                 if (WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, segp, i) & WID_FLY_FLAG)
  1585.                                 {
  1586.                                         process_segment(segp.absolute_sibling(segnum), next_steps_remaining);
  1587.                                 }
  1588.                         }
  1589.                 }
  1590.         }
  1591. }
  1592.  
  1593. struct d1wclip
  1594. {
  1595.         wclip *const wc;
  1596.         d1wclip(wclip &w) : wc(&w) {}
  1597. };
  1598.  
  1599. DEFINE_SERIAL_UDT_TO_MESSAGE(d1wclip, dwc, (dwc.wc->play_time, dwc.wc->num_frames, dwc.wc->d1_frames, dwc.wc->open_sound, dwc.wc->close_sound, dwc.wc->flags, dwc.wc->filename, serial::pad<1>()));
  1600. ASSERT_SERIAL_UDT_MESSAGE_SIZE(d1wclip, 26 + (sizeof(int16_t) * MAX_CLIP_FRAMES_D1));
  1601.  
  1602. }
  1603.  
  1604. // -----------------------------------------------------------------------------------
  1605. //      objp is going to detonate
  1606. //      blast nearby monitors, lights, maybe other things
  1607. void blast_nearby_glass(const object &objp, const fix damage)
  1608. {
  1609.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1610.         auto &Effects = LevelUniqueEffectsClipState.Effects;
  1611.         auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
  1612.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1613.         auto &vcvertptr = Vertices.vcptr;
  1614.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1615.         auto &vcwallptr = Walls.vcptr;
  1616.         blast_nearby_glass_context{objp, damage, Effects, GameBitmaps, Textures, TmapInfo, Vclip, vcvertptr, vcwallptr}.process_segment(vmsegptridx(objp.segnum), MAX_BLAST_GLASS_DEPTH);
  1617. }
  1618.  
  1619. }
  1620.  
  1621. #endif
  1622.  
  1623. DEFINE_SERIAL_UDT_TO_MESSAGE(wclip, wc, (wc.play_time, wc.num_frames, wc.frames, wc.open_sound, wc.close_sound, wc.flags, wc.filename, serial::pad<1>()));
  1624. ASSERT_SERIAL_UDT_MESSAGE_SIZE(wclip, 26 + (sizeof(int16_t) * MAX_CLIP_FRAMES));
  1625.  
  1626. /*
  1627.  * reads a wclip structure from a PHYSFS_File
  1628.  */
  1629. void wclip_read(PHYSFS_File *fp, wclip &wc)
  1630. {
  1631.         PHYSFSX_serialize_read(fp, wc);
  1632. }
  1633.  
  1634. #if 0
  1635. void wclip_write(PHYSFS_File *fp, const wclip &wc)
  1636. {
  1637.         PHYSFSX_serialize_write(fp, wc);
  1638. }
  1639. #endif
  1640.  
  1641. struct wrap_v16_wall
  1642. {
  1643.         const wall *w;
  1644.         wrap_v16_wall(const wall &t) : w(&t) {}
  1645. };
  1646.  
  1647. #define _SERIAL_UDT_WALL_V16_MEMBERS(P) (P type, P flags, P hps, P trigger, P clip_num, P keys)
  1648.  
  1649. DEFINE_SERIAL_UDT_TO_MESSAGE(v16_wall, w, _SERIAL_UDT_WALL_V16_MEMBERS(w.));
  1650. DEFINE_SERIAL_UDT_TO_MESSAGE(wrap_v16_wall, w, _SERIAL_UDT_WALL_V16_MEMBERS(w.w->));
  1651. ASSERT_SERIAL_UDT_MESSAGE_SIZE(wrap_v16_wall, 9);
  1652.  
  1653. /*
  1654.  * reads a v16_wall structure from a PHYSFS_File
  1655.  */
  1656. void v16_wall_read(PHYSFS_File *fp, v16_wall &w)
  1657. {
  1658.         PHYSFSX_serialize_read(fp, w);
  1659. }
  1660.  
  1661. struct wrap_v19_wall
  1662. {
  1663.         const wall *w;
  1664.         wrap_v19_wall(const wall &t) : w(&t) {}
  1665. };
  1666.  
  1667. DEFINE_SERIAL_UDT_TO_MESSAGE(v19_wall, w, (w.segnum, serial::pad<2>(), w.sidenum, w.type, w.flags, w.hps, w.trigger, w.clip_num, w.keys, w.linked_wall));
  1668. DEFINE_SERIAL_UDT_TO_MESSAGE(wrap_v19_wall, w, (w.w->segnum, serial::pad<2>(), w.w->sidenum, serial::pad<3>(), w.w->type, w.w->flags, w.w->hps, w.w->trigger, w.w->clip_num, w.w->keys, w.w->linked_wall, serial::pad<2>()));
  1669. ASSERT_SERIAL_UDT_MESSAGE_SIZE(v19_wall, 21);
  1670. ASSERT_SERIAL_UDT_MESSAGE_SIZE(wrap_v19_wall, 21);
  1671.  
  1672. /*
  1673.  * reads a v19_wall structure from a PHYSFS_File
  1674.  */
  1675. void v19_wall_read(PHYSFS_File *fp, v19_wall &w)
  1676. {
  1677.         PHYSFSX_serialize_read(fp, w);
  1678. }
  1679.  
  1680. #if defined(DXX_BUILD_DESCENT_I)
  1681. #define _SERIAL_UDT_WALL_D2X_MEMBERS    serial::pad<2>()
  1682. #elif defined(DXX_BUILD_DESCENT_II)
  1683. #define _SERIAL_UDT_WALL_D2X_MEMBERS    w.controlling_trigger, w.cloak_value
  1684. #endif
  1685. DEFINE_SERIAL_UDT_TO_MESSAGE(wall, w, (serial::sign_extend<int>(w.segnum), w.sidenum, serial::pad<3, 0>(), w.hps, serial::sign_extend<int>(w.linked_wall), w.type, w.flags, w.state, w.trigger, w.clip_num, w.keys, _SERIAL_UDT_WALL_D2X_MEMBERS));
  1686. ASSERT_SERIAL_UDT_MESSAGE_SIZE(wall, 24);
  1687.  
  1688. /*
  1689.  * reads a wall structure from a PHYSFS_File
  1690.  */
  1691. void wall_read(PHYSFS_File *fp, wall &w)
  1692. {
  1693.         PHYSFSX_serialize_read(fp, w);
  1694.         w.flags &= ~WALL_EXPLODING;
  1695. }
  1696.  
  1697. DEFINE_SERIAL_UDT_TO_MESSAGE(active_door, d, (d.n_parts, d.front_wallnum, d.back_wallnum, d.time));
  1698. ASSERT_SERIAL_UDT_MESSAGE_SIZE(active_door, 16);
  1699.  
  1700. /*
  1701.  * reads an active_door structure from a PHYSFS_File
  1702.  */
  1703. void active_door_read(PHYSFS_File *fp, active_door &ad)
  1704. {
  1705.         PHYSFSX_serialize_read(fp, ad);
  1706. }
  1707.  
  1708. void active_door_write(PHYSFS_File *fp, const active_door &ad)
  1709. {
  1710.         PHYSFSX_serialize_write(fp, ad);
  1711. }
  1712.  
  1713. void wall_write(PHYSFS_File *fp, const wall &w, short version)
  1714. {
  1715.         if (version <= 16)
  1716.                 PHYSFSX_serialize_write<wrap_v16_wall>(fp, w);
  1717.         else if (version <= 19)
  1718.                 PHYSFSX_serialize_write<wrap_v19_wall>(fp, w);
  1719.         else
  1720.                 PHYSFSX_serialize_write(fp, w);
  1721. }
  1722.  
  1723. #if defined(DXX_BUILD_DESCENT_II)
  1724. DEFINE_SERIAL_UDT_TO_MESSAGE(cloaking_wall, cw, (cw.front_wallnum, cw.back_wallnum, cw.front_ls, cw.back_ls, cw.time));
  1725. ASSERT_SERIAL_UDT_MESSAGE_SIZE(cloaking_wall, 40);
  1726.  
  1727. namespace dsx {
  1728. void cloaking_wall_read(cloaking_wall &cw, PHYSFS_File *fp)
  1729. {
  1730.         PHYSFSX_serialize_read(fp, cw);
  1731. }
  1732.  
  1733. void cloaking_wall_write(const cloaking_wall &cw, PHYSFS_File *fp)
  1734. {
  1735.         PHYSFSX_serialize_write(fp, cw);
  1736. }
  1737. }
  1738. #endif
  1739.