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. /*
  21.  *
  22.  * Routines for displaying the auto-map.
  23.  *
  24.  */
  25.  
  26. #include "dxxsconf.h"
  27. #include <algorithm>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31.  
  32. #if DXX_USE_OGL
  33. #include "ogl_init.h"
  34. #endif
  35.  
  36. #include "dxxerror.h"
  37. #include "3d.h"
  38. #include "inferno.h"
  39. #include "u_mem.h"
  40. #include "render.h"
  41. #include "object.h"
  42. #include "vclip.h"
  43. #include "game.h"
  44. #include "polyobj.h"
  45. #include "sounds.h"
  46. #include "player.h"
  47. #include "bm.h"
  48. #include "key.h"
  49. #include "newmenu.h"
  50. #include "menu.h"
  51. #include "screens.h"
  52. #include "textures.h"
  53. #include "hudmsg.h"
  54. #include "mouse.h"
  55. #include "timer.h"
  56. #include "segpoint.h"
  57. #include "joy.h"
  58. #include "iff.h"
  59. #include "pcx.h"
  60. #include "palette.h"
  61. #include "wall.h"
  62. #include "hostage.h"
  63. #include "fuelcen.h"
  64. #include "physfsx.h"
  65. #include "gameseq.h"
  66. #include "gamefont.h"
  67. #include "gameseg.h"
  68. #include "common/3d/globvars.h"
  69. #include "multi.h"
  70. #include "kconfig.h"
  71. #include "endlevel.h"
  72. #include "text.h"
  73. #include "gauges.h"
  74. #include "powerup.h"
  75. #include "switch.h"
  76. #include "automap.h"
  77. #include "cntrlcen.h"
  78. #include "timer.h"
  79. #include "config.h"
  80. #include "playsave.h"
  81. #include "rbaudio.h"
  82. #include "window.h"
  83. #include "playsave.h"
  84. #include "args.h"
  85. #include "physics.h"
  86.  
  87. #include "compiler-range_for.h"
  88. #include "d_range.h"
  89. #include "d_zip.h"
  90. #include "partial_range.h"
  91. #include <memory>
  92.  
  93. #define LEAVE_TIME 0x4000
  94.  
  95. #define EF_USED     1   // This edge is used
  96. #define EF_DEFINING 2   // A structure defining edge that should always draw.
  97. #define EF_FRONTIER 4   // An edge between the known and the unknown.
  98. #define EF_SECRET   8   // An edge that is part of a secret wall.
  99. #define EF_GRATE    16  // A grate... draw it all the time.
  100. #define EF_NO_FADE  32  // An edge that doesn't fade with distance
  101. #define EF_TOO_FAR  64  // An edge that is too far away
  102.  
  103. namespace dcx {
  104.  
  105. namespace {
  106.  
  107. struct Edge_info
  108. {
  109.         std::array<unsigned, 2> verts;     // 8  bytes
  110.         std::array<uint8_t, 4> sides;     // 4  bytes
  111.         std::array<segnum_t, 4> segnum;    // 16 bytes  // This might not need to be stored... If you can access the normals of a side.
  112.         ubyte flags;        // 1  bytes  // See the EF_??? defines above.
  113.         color_t color;        // 1  bytes
  114.         ubyte num_faces;    // 1  bytes  // 31 bytes...
  115. };
  116.  
  117. }
  118.  
  119. }
  120.  
  121. namespace dsx {
  122.  
  123. namespace {
  124.  
  125. struct automap : ignore_window_pointer_t
  126. {
  127.         fix64                   entry_time;
  128.         fix64                   t1, t2;
  129.         int                     leave_mode;
  130.         int                     pause_game;
  131.         vms_angvec              tangles;
  132.         ushort                  old_wiggle; // keep 4 byte aligned
  133.         int                     max_segments_away;
  134.         int                     segment_limit;
  135.        
  136.         // Edge list variables
  137.         int                     num_edges;
  138.         unsigned max_edges; //set each frame
  139.         unsigned end_valid_edges;
  140.         std::unique_ptr<Edge_info[]>            edges;
  141.         std::unique_ptr<Edge_info *[]>                  drawingListBright;
  142.        
  143.         // Screen canvas variables
  144.         grs_subcanvas           automap_view;
  145.        
  146.         grs_main_bitmap         automap_background;
  147.        
  148.         // Rendering variables
  149.         fix                     zoom;
  150.         vms_vector              view_target;
  151.         vms_vector              view_position;
  152.         fix                     farthest_dist;
  153.         vms_matrix              viewMatrix;
  154.         fix                     viewDist;
  155.        
  156.         color_t                 wall_normal_color;
  157.         color_t                 wall_door_color;
  158.         color_t                 wall_door_blue;
  159.         color_t                 wall_door_gold;
  160.         color_t                 wall_door_red;
  161. #if defined(DXX_BUILD_DESCENT_II)
  162.         color_t                 wall_revealed_color;
  163. #endif
  164.         color_t                 hostage_color;
  165.         color_t                 green_31;
  166.         color_t                 white_63;
  167.         color_t                 blue_48;
  168.         color_t                 red_48;
  169.         control_info controls;
  170.         segment_depth_array_t depth_array;
  171. };
  172.  
  173. static void init_automap_subcanvas(grs_subcanvas &view, grs_canvas &container)
  174. {
  175. #if defined(DXX_BUILD_DESCENT_I)
  176.         if (MacHog)
  177.                 gr_init_sub_canvas(view, container, 38*(SWIDTH/640.0), 77*(SHEIGHT/480.0), 564*(SWIDTH/640.0), 381*(SHEIGHT/480.0));
  178.         else
  179. #endif
  180.                 gr_init_sub_canvas(view, container, (SWIDTH/23), (SHEIGHT/6), (SWIDTH/1.1), (SHEIGHT/1.45));
  181. }
  182.  
  183. }
  184.  
  185. }
  186.  
  187. namespace dcx {
  188.  
  189. #define MAX_EDGES_FROM_VERTS(v)     ((v)*4)
  190.  
  191. #define K_WALL_NORMAL_COLOR     BM_XRGB(29, 29, 29 )
  192. #define K_WALL_DOOR_COLOR       BM_XRGB(5, 27, 5 )
  193. #define K_WALL_DOOR_BLUE        BM_XRGB(0, 0, 31)
  194. #define K_WALL_DOOR_GOLD        BM_XRGB(31, 31, 0)
  195. #define K_WALL_DOOR_RED         BM_XRGB(31, 0, 0)
  196. #define K_WALL_REVEALED_COLOR   BM_XRGB(0, 0, 25 ) //what you see when you have the full map powerup
  197. #define K_HOSTAGE_COLOR         BM_XRGB(0, 31, 0 )
  198. #define K_FONT_COLOR_20         BM_XRGB(20, 20, 20 )
  199. #define K_GREEN_31              BM_XRGB(0, 31, 0)
  200.  
  201. int Automap_active = 0;
  202. static int Automap_debug_show_all_segments;
  203.  
  204. static void automap_clear_visited()    
  205. {
  206. #ifndef NDEBUG
  207.         Automap_debug_show_all_segments = 0;
  208. #endif
  209.         LevelUniqueAutomapState.Automap_visited = {};
  210. }
  211.  
  212. }
  213.  
  214. namespace dsx {
  215. static void init_automap_colors(automap *am)
  216. {
  217.         am->wall_normal_color = K_WALL_NORMAL_COLOR;
  218.         am->wall_door_color = K_WALL_DOOR_COLOR;
  219.         am->wall_door_blue = K_WALL_DOOR_BLUE;
  220.         am->wall_door_gold = K_WALL_DOOR_GOLD;
  221.         am->wall_door_red = K_WALL_DOOR_RED;
  222. #if defined(DXX_BUILD_DESCENT_II)
  223.         am->wall_revealed_color = K_WALL_REVEALED_COLOR;
  224. #endif
  225.         am->hostage_color = K_HOSTAGE_COLOR;
  226.         am->green_31 = K_GREEN_31;
  227.  
  228.         am->white_63 = gr_find_closest_color_current(63,63,63);
  229.         am->blue_48 = gr_find_closest_color_current(0,0,48);
  230.         am->red_48 = gr_find_closest_color_current(48,0,0);
  231. }
  232.  
  233. // Map movement defines
  234. #define PITCH_DEFAULT 9000
  235. #define ZOOM_DEFAULT i2f(20*10)
  236. #define ZOOM_MIN_VALUE i2f(20*5)
  237. #define ZOOM_MAX_VALUE i2f(20*100)
  238.  
  239. // Function Prototypes
  240. static void adjust_segment_limit(automap *am, int SegmentLimit);
  241. static void automap_build_edge_list(automap *am, int add_all_edges);
  242. }
  243.  
  244. /* MAX_DROP_MULTI_* must be a power of 2 for LastMarkerDropped to work
  245.  * properly.
  246.  */
  247. #define MAX_DROP_MULTI_COOP_0   2
  248. #define MAX_DROP_MULTI_COOP_1   4
  249. #define MAX_DROP_MULTI_COOP_P   (max_numplayers > 4)
  250. #define MAX_DROP_MULTI_COOP     (MAX_DROP_MULTI_COOP_P ? MAX_DROP_MULTI_COOP_0 : MAX_DROP_MULTI_COOP_1)
  251. #define MAX_DROP_MULTI_COMPETITIVE      2
  252. #define MAX_DROP_SINGLE 9
  253.  
  254. #if defined(DXX_BUILD_DESCENT_II)
  255.  
  256. namespace dsx {
  257. marker_message_text_t Marker_input;
  258. static float MarkerScale=2.0;
  259.  
  260. d_marker_state MarkerState;
  261.  
  262. game_marker_index convert_player_marker_index_to_game_marker_index(const unsigned game_mode, const unsigned max_numplayers, const unsigned player_num, const player_marker_index player_marker_num)
  263. {
  264.         if (game_mode & GM_MULTI_COOP)
  265.                 return static_cast<game_marker_index>((player_num * MAX_DROP_MULTI_COOP) + static_cast<unsigned>(player_marker_num));
  266.         if (game_mode & GM_MULTI)
  267.                 return static_cast<game_marker_index>((player_num * MAX_DROP_MULTI_COMPETITIVE) + static_cast<unsigned>(player_marker_num));
  268.         return game_marker_index{player_marker_num};
  269. }
  270.  
  271. unsigned d_marker_state::get_markers_per_player(const unsigned game_mode, const unsigned max_numplayers)
  272. {
  273.         if (game_mode & GM_MULTI_COOP)
  274.                 return MAX_DROP_MULTI_COOP;
  275.         if (game_mode & GM_MULTI)
  276.                 return MAX_DROP_MULTI_COMPETITIVE;
  277.         return MAX_DROP_SINGLE;
  278. }
  279.  
  280. xrange<player_marker_index> get_player_marker_range(const unsigned maxdrop)
  281. {
  282.         const auto base = player_marker_index::_0;
  283.         return {base, static_cast<player_marker_index>(static_cast<unsigned>(base) + maxdrop)};
  284. }
  285.  
  286. playernum_t get_marker_owner(const unsigned game_mode, const game_marker_index gmi, const unsigned max_numplayers)
  287. {
  288.         const auto ugmi = static_cast<unsigned>(gmi);
  289.         if (game_mode & GM_MULTI_COOP)
  290.         {
  291.                 /* This is split out to encourage the compiler to recognize that
  292.                  * the divisor is a constant in every path, and in every path,
  293.                  * the divisor was chosen to allow use of right shift in place
  294.                  * of division.
  295.                  */
  296.                 if (MAX_DROP_MULTI_COOP_P)
  297.                         return ugmi / MAX_DROP_MULTI_COOP_0;
  298.                 return ugmi / MAX_DROP_MULTI_COOP_1;
  299.         }
  300.         if (game_mode & GM_MULTI)
  301.                 return ugmi / MAX_DROP_MULTI_COMPETITIVE;
  302.         return 0;
  303. }
  304.  
  305. namespace {
  306.  
  307. xrange<game_marker_index> get_game_marker_range(const unsigned game_mode, const unsigned max_numplayers, const unsigned player_num, const unsigned maxdrop)
  308. {
  309.         const auto base = convert_player_marker_index_to_game_marker_index(game_mode, max_numplayers, player_num, player_marker_index::_0);
  310.         return {base, static_cast<game_marker_index>(static_cast<unsigned>(base) + maxdrop)};
  311. }
  312.  
  313. }
  314.  
  315. }
  316. #endif
  317.  
  318. # define automap_draw_line g3_draw_line
  319. #if DXX_USE_OGL
  320. #define DrawMarkerNumber(C,a,b,c,d)     DrawMarkerNumber(a,b,c,d)
  321. #define draw_all_edges(C,a)     draw_all_edges(a)
  322. #endif
  323.  
  324. // -------------------------------------------------------------
  325.  
  326. namespace dsx {
  327. static void draw_all_edges(grs_canvas &, automap *am);
  328. #if defined(DXX_BUILD_DESCENT_I)
  329. static inline void DrawMarkers(fvcobjptr &, grs_canvas &, automap *)
  330. {
  331. }
  332.  
  333. static inline void ClearMarkers()
  334. {
  335. }
  336. #elif defined(DXX_BUILD_DESCENT_II)
  337. static void DrawMarkerNumber(grs_canvas &canvas, const automap *am, const game_marker_index gmi, const player_marker_index pmi, const g3s_point &BasePoint)
  338. {
  339.         struct xy
  340.         {
  341.                 float x0, y0, x1, y1;
  342.         };
  343.         static constexpr enumerated_array<std::array<xy, 5>, 9, player_marker_index> sArray = {{{
  344.                 {{
  345.                         {-0.25, 0.75, 0, 1},
  346.                         {0, 1, 0, -1},
  347.                         {-1, -1, 1, -1},
  348.                 }},
  349.                 {{
  350.                         {-1, 1, 1, 1},
  351.                         {1, 1, 1, 0},
  352.                         {-1, 0, 1, 0},
  353.                         {-1, 0, -1, -1},
  354.                         {-1, -1, 1, -1}
  355.                 }},
  356.                 {{
  357.                         {-1, 1, 1, 1},
  358.                         {1, 1, 1, -1},
  359.                         {-1, -1, 1, -1},
  360.                         {0, 0, 1, 0},
  361.                 }},
  362.                 {{
  363.                         {-1, 1, -1, 0},
  364.                         {-1, 0, 1, 0},
  365.                         {1, 1, 1, -1},
  366.                 }},
  367.                 {{
  368.                         {-1, 1, 1, 1},
  369.                         {-1, 1, -1, 0},
  370.                         {-1, 0, 1, 0},
  371.                         {1, 0, 1, -1},
  372.                         {-1, -1, 1, -1}
  373.                 }},
  374.                 {{
  375.                         {-1, 1, 1, 1},
  376.                         {-1, 1, -1, -1},
  377.                         {-1, -1, 1, -1},
  378.                         {1, -1, 1, 0},
  379.                         {-1, 0, 1, 0}
  380.                 }},
  381.                 {{
  382.                         {-1, 1, 1, 1},
  383.                         {1, 1, 1, -1},
  384.                 }},
  385.                 {{
  386.                         {-1, 1, 1, 1},
  387.                         {1, 1, 1, -1},
  388.                         {-1, -1, 1, -1},
  389.                         {-1, -1, -1, 1},
  390.                         {-1, 0, 1, 0}
  391.                 }},
  392.                 {{
  393.                         {-1, 1, 1, 1},
  394.                         {1, 1, 1, -1},
  395.                         {-1, 0, 1, 0},
  396.                         {-1, 0, -1, 1},
  397.                  }}
  398.         }}};
  399.         static constexpr enumerated_array<uint_fast8_t, 9, player_marker_index> NumOfPoints = {{{3, 5, 4, 3, 5, 5, 2, 5, 4}}};
  400.  
  401.         const auto color = (gmi == MarkerState.HighlightMarker ? am->white_63 : am->blue_48);
  402.         const auto scale_x = Matrix_scale.x;
  403.         const auto scale_y = Matrix_scale.y;
  404.         range_for (const auto &i, unchecked_partial_range(sArray[pmi].data(), NumOfPoints[pmi]))
  405.         {
  406.                 const auto ax0 = i.x0 * MarkerScale;
  407.                 const auto ay0 = i.y0 * MarkerScale;
  408.                 const auto ax1 = i.x1 * MarkerScale;
  409.                 const auto ay1 = i.y1 * MarkerScale;
  410.                 auto FromPoint = BasePoint;
  411.                 auto ToPoint = BasePoint;
  412.                 FromPoint.p3_x += fixmul(fl2f(ax0), scale_x);
  413.                 FromPoint.p3_y += fixmul(fl2f(ay0), scale_y);
  414.                 ToPoint.p3_x += fixmul(fl2f(ax1), scale_x);
  415.                 ToPoint.p3_y += fixmul(fl2f(ay1), scale_y);
  416.                 g3_code_point(FromPoint);
  417.                 g3_code_point(ToPoint);
  418.                 g3_project_point(FromPoint);
  419.                 g3_project_point(ToPoint);
  420.                 automap_draw_line(canvas, FromPoint, ToPoint, color);
  421.         }
  422. }
  423.  
  424. static void DropMarker(fvmobjptridx &vmobjptridx, fvmsegptridx &vmsegptridx, const object &plrobj, const game_marker_index marker_num, const player_marker_index player_marker_num)
  425. {
  426.         auto &marker_objidx = MarkerState.imobjidx[marker_num];
  427.         if (marker_objidx != object_none)
  428.                 obj_delete(LevelUniqueObjectState, Segments, vmobjptridx(marker_objidx));
  429.  
  430.         marker_objidx = drop_marker_object(plrobj.pos, vmsegptridx(plrobj.segnum), plrobj.orient, marker_num);
  431.  
  432.         if (Game_mode & GM_MULTI)
  433.                 multi_send_drop_marker(Player_num, plrobj.pos, player_marker_num, MarkerState.message[marker_num]);
  434. }
  435.  
  436. void DropBuddyMarker(object &objp)
  437. {
  438.         auto &Objects = LevelUniqueObjectState.Objects;
  439.         auto &vmobjptridx = Objects.vmptridx;
  440.  
  441.         constexpr auto marker_num = game_marker_index::GuidebotDeathSite;
  442.         static_assert(MarkerState.message.valid_index(marker_num), "not enough markers");
  443.  
  444.         auto &MarkerMessage = MarkerState.message[marker_num];
  445.         snprintf(&MarkerMessage[0], MarkerMessage.size(), "RIP: %s", static_cast<const char *>(PlayerCfg.GuidebotName));
  446.  
  447.         auto &marker_objidx = MarkerState.imobjidx[marker_num];
  448.         if (marker_objidx != object_none)
  449.                 obj_delete(LevelUniqueObjectState, Segments, vmobjptridx(marker_objidx));
  450.  
  451.         marker_objidx = drop_marker_object(objp.pos, vmsegptridx(objp.segnum), objp.orient, marker_num);
  452. }
  453.  
  454. #define MARKER_SPHERE_SIZE 0x58000
  455.  
  456. static void DrawMarkers(fvcobjptr &vcobjptr, grs_canvas &canvas, automap *const am)
  457. {
  458.         static int cyc=10,cycdir=1;
  459.  
  460.         const auto game_mode = Game_mode;
  461.         const auto max_numplayers = Netgame.max_numplayers;
  462.         const auto maxdrop = MarkerState.get_markers_per_player(game_mode, max_numplayers);
  463.         const auto &&game_marker_range = get_game_marker_range(game_mode, max_numplayers, Player_num, maxdrop);
  464.         const auto &&player_marker_range = get_player_marker_range(maxdrop);
  465.         const auto &&zipped_marker_range = zip(game_marker_range, player_marker_range, unchecked_partial_range(&MarkerState.imobjidx[*game_marker_range.begin()], maxdrop));
  466.         const auto &&mb = zipped_marker_range.begin();
  467.         const auto &&me = zipped_marker_range.end();
  468.         auto iter = mb;
  469.         /* Find the first marker object in the player's marker range that is
  470.          * not object_none.  If every marker object in the range is
  471.          * object_none, then there are no markers to draw, so return.
  472.          */
  473.         for (;;)
  474.         {
  475.                 auto &&[gmi, pmi, objidx] = *iter;
  476.                 (void)gmi;
  477.                 (void)pmi;
  478.                 if (objidx != object_none)
  479.                         break;
  480.                 if (++ iter == me)
  481.                         return;
  482.         }
  483.         /* A marker was found, so at least one marker will be drawn.  Set up
  484.          * colors for the markers.
  485.          */
  486.         const auto current_cycle_color = cyc;
  487.         const std::array<color_t, 3> colors{{
  488.                 gr_find_closest_color_current(current_cycle_color, 0, 0),
  489.                 gr_find_closest_color_current(current_cycle_color + 10, 0, 0),
  490.                 gr_find_closest_color_current(current_cycle_color + 20, 0, 0),
  491.         }};
  492.         for (; iter != me; ++iter)
  493.         {
  494.                 auto &&[gmi, pmi, objidx] = *iter;
  495.                 if (objidx != object_none)
  496.                 {
  497.                         const auto &&sphere_point = g3_rotate_point(vcobjptr(objidx)->pos);
  498.                         g3_draw_sphere(canvas, sphere_point, MARKER_SPHERE_SIZE, colors[0]);
  499.                         g3_draw_sphere(canvas, sphere_point, MARKER_SPHERE_SIZE / 2, colors[1]);
  500.                         g3_draw_sphere(canvas, sphere_point, MARKER_SPHERE_SIZE / 4, colors[2]);
  501.                         DrawMarkerNumber(canvas, am, gmi, pmi, sphere_point);
  502.                 }
  503.         }
  504.  
  505.         if (cycdir)
  506.                 cyc+=2;
  507.         else
  508.                 cyc-=2;
  509.  
  510.         if (cyc>43)
  511.         {
  512.                 cyc=43;
  513.                 cycdir=0;
  514.         }
  515.         else if (cyc<10)
  516.         {
  517.                 cyc=10;
  518.                 cycdir=1;
  519.         }
  520.  
  521. }
  522.  
  523. static void ClearMarkers()
  524. {
  525.         static_cast<d_marker_object_numbers &>(MarkerState) = {};
  526.         MarkerState.message = {};
  527. }
  528. #endif
  529.  
  530. void automap_clear_visited()   
  531. {
  532.         ::dcx::automap_clear_visited();
  533.                 ClearMarkers();
  534. }
  535.  
  536. static void draw_player(grs_canvas &canvas, const object_base &obj, const uint8_t color)
  537. {
  538.         // Draw Console player -- shaped like a ellipse with an arrow.
  539.         auto sphere_point = g3_rotate_point(obj.pos);
  540.         const auto obj_size = obj.size;
  541.         g3_draw_sphere(canvas, sphere_point, obj_size, color);
  542.  
  543.         // Draw shaft of arrow
  544.         const auto &&head_pos = vm_vec_scale_add(obj.pos, obj.orient.fvec, obj_size * 2);
  545.         {
  546.         auto &&arrow_point = g3_rotate_point(vm_vec_scale_add(obj.pos, obj.orient.fvec, obj_size * 3));
  547.         automap_draw_line(canvas, sphere_point, arrow_point, color);
  548.  
  549.         // Draw right head of arrow
  550.         {
  551.                 const auto &&rhead_pos = vm_vec_scale_add(head_pos, obj.orient.rvec, obj_size);
  552.                 auto head_point = g3_rotate_point(rhead_pos);
  553.                 automap_draw_line(canvas, arrow_point, head_point, color);
  554.         }
  555.  
  556.         // Draw left head of arrow
  557.         {
  558.                 const auto &&lhead_pos = vm_vec_scale_add(head_pos, obj.orient.rvec, -obj_size);
  559.                 auto head_point = g3_rotate_point(lhead_pos);
  560.                 automap_draw_line(canvas, arrow_point, head_point, color);
  561.         }
  562.         }
  563.  
  564.         // Draw player's up vector
  565.         {
  566.                 const auto &&arrow_pos = vm_vec_scale_add(obj.pos, obj.orient.uvec, obj_size * 2);
  567.         auto arrow_point = g3_rotate_point(arrow_pos);
  568.                 automap_draw_line(canvas, sphere_point, arrow_point, color);
  569.         }
  570. }
  571.  
  572. #if defined(DXX_BUILD_DESCENT_II)
  573. //name for each group.  maybe move somewhere else
  574. constexpr char system_name[][17] = {
  575.                         "Zeta Aquilae",
  576.                         "Quartzon System",
  577.                         "Brimspark System",
  578.                         "Limefrost Spiral",
  579.                         "Baloris Prime",
  580.                         "Omega System"};
  581. #endif
  582.  
  583. static void name_frame(grs_canvas &canvas, automap *const am)
  584. {
  585.         gr_set_fontcolor(canvas, am->green_31, -1);
  586.         char            name_level_left[128];
  587.  
  588.         auto &game_font = *GAME_FONT;
  589. #if defined(DXX_BUILD_DESCENT_I)
  590.         const char *name_level;
  591.         if (Current_level_num > 0)
  592.         {
  593.                 snprintf(name_level_left, sizeof(name_level_left), "%s %i: %s",TXT_LEVEL, Current_level_num, static_cast<const char *>(Current_level_name));
  594.                 name_level = name_level_left;
  595.         }
  596.         else
  597.                 name_level = Current_level_name;
  598.  
  599.         gr_string(canvas, game_font, (SWIDTH / 64), (SHEIGHT / 48), name_level);
  600. #elif defined(DXX_BUILD_DESCENT_II)
  601.         char    name_level_right[128];
  602.         if (Current_level_num > 0)
  603.                 snprintf(name_level_left, sizeof(name_level_left), "%s %i",TXT_LEVEL, Current_level_num);
  604.         else
  605.                 snprintf(name_level_left, sizeof(name_level_left), "Secret Level %i",-Current_level_num);
  606.  
  607.         const char *const current_level_name = Current_level_name;
  608.         if (PLAYING_BUILTIN_MISSION && Current_level_num > 0)
  609.                 snprintf(name_level_right, sizeof(name_level_right), "%s %d: %s", system_name[(Current_level_num-1)/4], ((Current_level_num - 1) % 4) + 1, current_level_name);
  610.         else
  611.                 snprintf(name_level_right, sizeof(name_level_right), " %s", current_level_name);
  612.  
  613.         gr_string(canvas, game_font, (SWIDTH / 64), (SHEIGHT / 48), name_level_left);
  614.         int wr,h;
  615.         gr_get_string_size(game_font, name_level_right, &wr, &h, nullptr);
  616.         gr_string(canvas, game_font, canvas.cv_bitmap.bm_w - wr - (SWIDTH / 64), (SHEIGHT / 48), name_level_right, wr, h);
  617. #endif
  618. }
  619.  
  620. static void automap_apply_input(automap *am, const vms_matrix &plrorient, const vms_vector &plrpos)
  621. {
  622.         constexpr int SLIDE_SPEED = 350;
  623.         constexpr int ZOOM_SPEED_FACTOR = 500;  //(1500)
  624.         constexpr int ROT_SPEED_DIVISOR = 115000;
  625.         if (PlayerCfg.AutomapFreeFlight)
  626.         {
  627.                 if ( am->controls.state.fire_primary)
  628.                 {
  629.                         // Reset orientation
  630.                         am->controls.state.fire_primary = 0;
  631.                         am->viewMatrix = plrorient;
  632.                         vm_vec_scale_add(am->view_position, plrpos, am->viewMatrix.fvec, -ZOOM_DEFAULT);
  633.                 }
  634.                
  635.                 if (am->controls.pitch_time || am->controls.heading_time || am->controls.bank_time)
  636.                 {
  637.                         vms_angvec tangles;
  638.  
  639.                         tangles.p = fixdiv( am->controls.pitch_time, ROT_SPEED_DIVISOR );
  640.                         tangles.h = fixdiv( am->controls.heading_time, ROT_SPEED_DIVISOR );
  641.                         tangles.b = fixdiv( am->controls.bank_time, ROT_SPEED_DIVISOR*2 );
  642.  
  643.                         const auto &&tempm = vm_angles_2_matrix(tangles);
  644.                         am->viewMatrix = vm_matrix_x_matrix(am->viewMatrix,tempm);
  645.                         check_and_fix_matrix(am->viewMatrix);
  646.                 }
  647.                
  648.                 if ( am->controls.forward_thrust_time || am->controls.vertical_thrust_time || am->controls.sideways_thrust_time )
  649.                 {
  650.                         vm_vec_scale_add2( am->view_position, am->viewMatrix.fvec, am->controls.forward_thrust_time*ZOOM_SPEED_FACTOR );
  651.                         vm_vec_scale_add2( am->view_position, am->viewMatrix.uvec, am->controls.vertical_thrust_time*SLIDE_SPEED );
  652.                         vm_vec_scale_add2( am->view_position, am->viewMatrix.rvec, am->controls.sideways_thrust_time*SLIDE_SPEED );
  653.                        
  654.                         // Crude wrapping check
  655.                         clamp_fix_symmetric(am->view_position.x, F1_0*32000);
  656.                         clamp_fix_symmetric(am->view_position.y, F1_0*32000);
  657.                         clamp_fix_symmetric(am->view_position.z, F1_0*32000);
  658.                 }
  659.         }
  660.         else
  661.         {
  662.                 if ( am->controls.state.fire_primary)
  663.                 {
  664.                         // Reset orientation
  665.                         am->viewDist = ZOOM_DEFAULT;
  666.                         am->tangles.p = PITCH_DEFAULT;
  667.                         am->tangles.h  = 0;
  668.                         am->tangles.b  = 0;
  669.                         am->view_target = plrpos;
  670.                         am->controls.state.fire_primary = 0;
  671.                 }
  672.  
  673.                 am->viewDist -= am->controls.forward_thrust_time*ZOOM_SPEED_FACTOR;
  674.                 am->tangles.p += fixdiv( am->controls.pitch_time, ROT_SPEED_DIVISOR );
  675.                 am->tangles.h  += fixdiv( am->controls.heading_time, ROT_SPEED_DIVISOR );
  676.                 am->tangles.b  += fixdiv( am->controls.bank_time, ROT_SPEED_DIVISOR*2 );
  677.  
  678.                 if ( am->controls.vertical_thrust_time || am->controls.sideways_thrust_time )
  679.                 {
  680.                         vms_angvec      tangles1;
  681.                         vms_vector      old_vt;
  682.  
  683.                         old_vt = am->view_target;
  684.                         tangles1 = am->tangles;
  685.                         const auto &&tempm = vm_angles_2_matrix(tangles1);
  686.                         vm_matrix_x_matrix(am->viewMatrix, plrorient, tempm);
  687.                         vm_vec_scale_add2( am->view_target, am->viewMatrix.uvec, am->controls.vertical_thrust_time*SLIDE_SPEED );
  688.                         vm_vec_scale_add2( am->view_target, am->viewMatrix.rvec, am->controls.sideways_thrust_time*SLIDE_SPEED );
  689.                         if (vm_vec_dist_quick(am->view_target, plrpos) > i2f(1000))
  690.                                 am->view_target = old_vt;
  691.                 }
  692.  
  693.                 const auto &&tempm = vm_angles_2_matrix(am->tangles);
  694.                 vm_matrix_x_matrix(am->viewMatrix, plrorient, tempm);
  695.  
  696.                 clamp_fix_lh(am->viewDist, ZOOM_MIN_VALUE, ZOOM_MAX_VALUE);
  697.         }
  698. }
  699.  
  700. static void draw_automap(fvcobjptr &vcobjptr, automap *am)
  701. {
  702.         if ( am->leave_mode==0 && am->controls.state.automap && (timer_query()-am->entry_time)>LEAVE_TIME)
  703.                 am->leave_mode = 1;
  704.  
  705.         gr_set_default_canvas();
  706.         {
  707.                 auto &canvas = *grd_curcanv;
  708.                 if (am->automap_background.get_bitmap_data())
  709.                         show_fullscr(canvas, am->automap_background);
  710.                 gr_set_fontcolor(canvas, BM_XRGB(20, 20, 20), -1);
  711.         {
  712.                 int x, y;
  713. #if defined(DXX_BUILD_DESCENT_I)
  714.         if (MacHog)
  715.                         x = 80 * (SWIDTH / 640.), y = 36 * (SHEIGHT / 480.);
  716.         else
  717. #endif
  718.                         x = SWIDTH / 8, y = SHEIGHT / 16;
  719.                 gr_string(canvas, *HUGE_FONT, x, y, TXT_AUTOMAP);
  720.         }
  721.                 gr_set_fontcolor(canvas, BM_XRGB(20, 20, 20), -1);
  722.         {
  723.                 int x;
  724.                 int y0, y1, y2;
  725. #if defined(DXX_BUILD_DESCENT_I)
  726.                 const auto s1 = TXT_SLIDE_UPDOWN;
  727.                 const auto &s2 = "F9/F10 Changes viewing distance";
  728.         if (!MacHog)
  729.         {
  730.                         x = SWIDTH / 4.923;
  731.                         y0 = SHEIGHT / 1.126;
  732.                         y1 = SHEIGHT / 1.083;
  733.                         y2 = SHEIGHT / 1.043;
  734.         }
  735.         else
  736.         {
  737.                 // for the Mac automap they're shown up the top, hence the different layout
  738.                         x = 265 * (SWIDTH / 640.);
  739.                         y0 = 27 * (SHEIGHT / 480.);
  740.                         y1 = 44 * (SHEIGHT / 480.);
  741.                         y2 = 61 * (SHEIGHT / 480.);
  742.         }
  743. #elif defined(DXX_BUILD_DESCENT_II)
  744.                 const auto &s1 = "F9/F10 Changes viewing distance";
  745.                 const auto s2 = TXT_AUTOMAP_MARKER;
  746.                 x = SWIDTH / 10.666;
  747.                 y0 = SHEIGHT / 1.126;
  748.                 y1 = SHEIGHT / 1.083;
  749.                 y2 = SHEIGHT / 1.043;
  750. #endif
  751.                 auto &game_font = *GAME_FONT;
  752.                 gr_string(canvas, game_font, x, y0, TXT_TURN_SHIP);
  753.                 gr_string(canvas, game_font, x, y1, s1);
  754.                 gr_string(canvas, game_font, x, y2, s2);
  755.         }
  756.  
  757.         }
  758.         gr_set_current_canvas(am->automap_view);
  759.         auto &canvas = *grd_curcanv;
  760.  
  761.         gr_clear_canvas(canvas, BM_XRGB(0,0,0));
  762.  
  763.         g3_start_frame(canvas);
  764.         render_start_frame();
  765.  
  766.         if (!PlayerCfg.AutomapFreeFlight)
  767.                 vm_vec_scale_add(am->view_position,am->view_target,am->viewMatrix.fvec,-am->viewDist);
  768.  
  769.         g3_set_view_matrix(am->view_position,am->viewMatrix,am->zoom);
  770.  
  771.         draw_all_edges(*grd_curcanv, am);
  772.  
  773.         // Draw player...
  774.         const auto &self_ship_rgb = player_rgb[get_player_or_team_color(Player_num)];
  775.         const auto closest_color = BM_XRGB(self_ship_rgb.r, self_ship_rgb.g, self_ship_rgb.b);
  776.         draw_player(canvas, vcobjptr(get_local_player().objnum), closest_color);
  777.  
  778.         DrawMarkers(vcobjptr, canvas, am);
  779.        
  780.         // Draw player(s)...
  781.         const unsigned show_all_players = (Game_mode & GM_MULTI_COOP) || Netgame.game_flag.show_on_map;
  782.         if (show_all_players || (Game_mode & GM_TEAM))
  783.         {
  784.                 const unsigned local_player_team = get_team(Player_num);
  785.                 for (unsigned i = 0; i < N_players; ++i)
  786.                 {
  787.                         if (i == Player_num)
  788.                                 continue;
  789.                         if (show_all_players || local_player_team == get_team(i))
  790.                         {
  791.                                 auto &plr = *vcplayerptr(i);
  792.                                 auto &objp = *vcobjptr(plr.objnum);
  793.                                 if (objp.type == OBJ_PLAYER)
  794.                                 {
  795.                                         const auto &other_ship_rgb = player_rgb[get_player_or_team_color(i)];
  796.                                         draw_player(canvas, objp, BM_XRGB(other_ship_rgb.r, other_ship_rgb.g, other_ship_rgb.b));
  797.                                 }
  798.                         }
  799.                 }
  800.         }
  801.  
  802.         range_for (const auto &&objp, vcobjptr)
  803.         {
  804.                 switch( objp->type )    {
  805.                 case OBJ_HOSTAGE:
  806.                         {
  807.                         auto sphere_point = g3_rotate_point(objp->pos);
  808.                         g3_draw_sphere(canvas, sphere_point, objp->size, am->hostage_color);
  809.                         }
  810.                         break;
  811.                 case OBJ_POWERUP:
  812.                         if (LevelUniqueAutomapState.Automap_visited[objp->segnum] || Automap_debug_show_all_segments)
  813.                         {
  814.                                 ubyte id = get_powerup_id(objp);
  815.                                 unsigned r, g, b;
  816.                                 if (id==POW_KEY_RED)
  817.                                         r = 63 * 2, g = 5 * 2, b = 5 * 2;
  818.                                 else if (id==POW_KEY_BLUE)
  819.                                         r = 5 * 2, g = 5 * 2, b = 63 * 2;
  820.                                 else if (id==POW_KEY_GOLD)
  821.                                         r = 63 * 2, g = 63 * 2, b = 10 * 2;
  822.                                 else
  823.                                         break;
  824.                                 {
  825.                                         const auto color = gr_find_closest_color(r, g, b);
  826.                                 auto sphere_point = g3_rotate_point(objp->pos);
  827.                                 g3_draw_sphere(canvas, sphere_point, objp->size * 4, color);
  828.                                 }
  829.                         }
  830.                         break;
  831.                         default:
  832.                                 break;
  833.                 }
  834.         }
  835.  
  836.         g3_end_frame();
  837.  
  838.         name_frame(canvas, am);
  839.  
  840. #if defined(DXX_BUILD_DESCENT_II)
  841.         {
  842.                 const auto HighlightMarker = MarkerState.HighlightMarker;
  843.                 if (MarkerState.message.valid_index(HighlightMarker))
  844.                 {
  845.                         auto &m = MarkerState.message[HighlightMarker];
  846.                         gr_printf(canvas, *canvas.cv_font, (SWIDTH/64), (SHEIGHT/18), "Marker %u%c %s", static_cast<unsigned>(HighlightMarker) + 1, m[0] ? ':' : 0, &m[0]);
  847.                 }
  848.         }
  849. #endif
  850.  
  851.         if (PlayerCfg.MouseFlightSim && PlayerCfg.MouseFSIndicator)
  852.         {
  853.                 const auto gwidth = canvas.cv_bitmap.bm_w;
  854.                 const auto gheight = canvas.cv_bitmap.bm_h;
  855.                 auto &raw_mouse_axis = am->controls.raw_mouse_axis;
  856.                 show_mousefs_indicator(canvas, raw_mouse_axis[0], raw_mouse_axis[1], raw_mouse_axis[2], gwidth - (gheight / 8), gheight - (gheight / 8), gheight / 5);
  857.         }
  858.  
  859.         am->t2 = timer_query();
  860.         const auto vsync = CGameCfg.VSync;
  861.         const auto bound = F1_0 / (vsync ? MAXIMUM_FPS : CGameArg.SysMaxFPS);
  862.         const auto may_sleep = !CGameArg.SysNoNiceFPS && !vsync;
  863.         while (am->t2 - am->t1 < bound) // ogl is fast enough that the automap can read the input too fast and you start to turn really slow.  So delay a bit (and free up some cpu :)
  864.         {
  865.                 if (Game_mode & GM_MULTI)
  866.                         multi_do_frame(); // during long wait, keep packets flowing
  867.                 if (may_sleep)
  868.                         timer_delay(F1_0>>8);
  869.                 am->t2 = timer_update();
  870.         }
  871.         if (am->pause_game)
  872.         {
  873.                 FrameTime=am->t2-am->t1;
  874.                 calc_d_tick();
  875.         }
  876.         am->t1 = am->t2;
  877. }
  878.  
  879. #if defined(DXX_BUILD_DESCENT_I)
  880. #define MAP_BACKGROUND_FILENAME (((SWIDTH>=640&&SHEIGHT>=480) && PHYSFSX_exists("maph.pcx",1))?"maph.pcx":"map.pcx")
  881. #elif defined(DXX_BUILD_DESCENT_II)
  882. #define MAP_BACKGROUND_FILENAME ((HIRESMODE && PHYSFSX_exists("mapb.pcx",1))?"mapb.pcx":"map.pcx")
  883. #endif
  884.  
  885. static void recompute_automap_segment_visibility(const object &plrobj, automap *const am)
  886. {
  887.         auto &player_info = plrobj.ctype.player_info;
  888.         int compute_depth_all_segments = (cheats.fullautomap || (player_info.powerup_flags & PLAYER_FLAGS_MAP_ALL));
  889.         if (Automap_debug_show_all_segments)
  890.                 compute_depth_all_segments = 1;
  891.         automap_build_edge_list(am, compute_depth_all_segments);
  892.         am->max_segments_away = set_segment_depths(plrobj.segnum, compute_depth_all_segments ? nullptr : &LevelUniqueAutomapState.Automap_visited, am->depth_array);
  893.         am->segment_limit = am->max_segments_away;
  894.         adjust_segment_limit(am, am->segment_limit);
  895. }
  896.  
  897. static window_event_result automap_key_command(window *, const d_event &event, automap *am)
  898. {
  899.         auto &Objects = LevelUniqueObjectState.Objects;
  900. #if defined(DXX_BUILD_DESCENT_I) || !defined(NDEBUG)
  901.         auto &vmobjptr = Objects.vmptr;
  902. #endif
  903. #if defined(DXX_BUILD_DESCENT_II)
  904.         auto &vmobjptridx = Objects.vmptridx;
  905. #endif
  906.         int c = event_key_get(event);
  907.  
  908.         switch (c)
  909.         {
  910. #if DXX_USE_SCREENSHOT
  911.                 case KEY_PRINT_SCREEN: {
  912.                         gr_set_default_canvas();
  913.                         save_screen_shot(1);
  914.                         return window_event_result::handled;
  915.                 }
  916. #endif
  917.                 case KEY_ESC:
  918.                         if (am->leave_mode==0)
  919.                         {
  920.                                 return window_event_result::close;
  921.                         }
  922.                         return window_event_result::handled;
  923. #if defined(DXX_BUILD_DESCENT_I)
  924.                 case KEY_ALTED+KEY_F:           // Alt+F shows full map, if cheats enabled
  925.                         if (cheats.enabled)      
  926.                         {
  927.                                 cheats.fullautomap = !cheats.fullautomap;
  928.                                 // if cheat of map powerup, work with full depth
  929.                                 auto &plrobj = get_local_plrobj();
  930.                                 recompute_automap_segment_visibility(plrobj, am);
  931.                         }
  932.                         return window_event_result::handled;
  933. #endif
  934. #ifndef NDEBUG
  935.                 case KEY_DEBUGGED+KEY_F:        {
  936.                                 Automap_debug_show_all_segments = !Automap_debug_show_all_segments;
  937.                                 auto &plrobj = get_local_plrobj();
  938.                                 recompute_automap_segment_visibility(plrobj, am);
  939.                         }
  940.                         return window_event_result::handled;
  941. #endif
  942.                 case KEY_F9:
  943.                         if (am->segment_limit > 1)              {
  944.                                 am->segment_limit--;
  945.                                 adjust_segment_limit(am, am->segment_limit);
  946.                         }
  947.                         return window_event_result::handled;
  948.                 case KEY_F10:
  949.                         if (am->segment_limit < am->max_segments_away)  {
  950.                                 am->segment_limit++;
  951.                                 adjust_segment_limit(am, am->segment_limit);
  952.                         }
  953.                         return window_event_result::handled;
  954. #if defined(DXX_BUILD_DESCENT_II)
  955.                 case KEY_1:
  956.                 case KEY_2:
  957.                 case KEY_3:
  958.                 case KEY_4:
  959.                 case KEY_5:
  960.                 case KEY_6:
  961.                 case KEY_7:
  962.                 case KEY_8:
  963.                 case KEY_9:
  964.                 case KEY_0:
  965.                         {
  966.                                 const auto game_mode = Game_mode;
  967.                                 const auto max_numplayers = Netgame.max_numplayers;
  968.                                 const auto maxdrop = MarkerState.get_markers_per_player(game_mode, max_numplayers);
  969.                                 const uint8_t marker_num = c - KEY_1;
  970.                                 if (marker_num <= maxdrop)
  971.                                 {
  972.                                         const auto gmi = convert_player_marker_index_to_game_marker_index(game_mode, max_numplayers, Player_num, player_marker_index{marker_num});
  973.                                         if (MarkerState.imobjidx[gmi] != object_none)
  974.                                                 MarkerState.HighlightMarker = gmi;
  975.                                 }
  976.                                 else
  977.                                         MarkerState.HighlightMarker = game_marker_index::None;
  978.                         }
  979.                         return window_event_result::handled;
  980.                 case KEY_D+KEY_CTRLED:
  981.                         {
  982.                                 const auto HighlightMarker = MarkerState.HighlightMarker;
  983.                                 if (!MarkerState.imobjidx.valid_index(HighlightMarker))
  984.                                         return window_event_result::handled;
  985.                                 auto &mo = MarkerState.imobjidx[HighlightMarker];
  986.                                 if (mo == object_none)
  987.                                         return window_event_result::handled;
  988.                                 gr_set_default_canvas();
  989.                                 if (nm_messagebox( NULL, 2, TXT_YES, TXT_NO, "Delete Marker?" ) == 0) {
  990.                                         /* FIXME: this event should be sent to other players
  991.                                          * so that they remove the marker.
  992.                                          */
  993.                                         obj_delete(LevelUniqueObjectState, Segments, vmobjptridx(std::exchange(mo, object_none)));
  994.                                         MarkerState.message[HighlightMarker] = {};
  995.                                         MarkerState.HighlightMarker = game_marker_index::None;
  996.                                 }
  997.                                 set_screen_mode(SCREEN_GAME);
  998.                         }
  999.                         return window_event_result::handled;
  1000. #ifndef RELEASE
  1001.                 case KEY_F11:   //KEY_COMMA:
  1002.                         if (MarkerScale>.5)
  1003.                                 MarkerScale-=.5;
  1004.                         return window_event_result::handled;
  1005.                 case KEY_F12:   //KEY_PERIOD:
  1006.                         if (MarkerScale<30.0)
  1007.                                 MarkerScale+=.5;
  1008.                         return window_event_result::handled;
  1009. #endif
  1010. #endif
  1011.         }
  1012.         return window_event_result::ignored;
  1013. }
  1014.  
  1015. static window_event_result automap_process_input(window *, const d_event &event, automap *am)
  1016. {
  1017.         kconfig_read_controls(am->controls, event, 1);
  1018.         Controls = {};
  1019.  
  1020.         if ( !am->controls.state.automap && (am->leave_mode==1) )
  1021.         {
  1022.                 return window_event_result::close;
  1023.         }
  1024.        
  1025.         if ( am->controls.state.automap)
  1026.         {
  1027.                 am->controls.state.automap = 0;
  1028.                 if (am->leave_mode==0)
  1029.                 {
  1030.                         return window_event_result::close;
  1031.                 }
  1032.         }
  1033.        
  1034.         return window_event_result::ignored;
  1035. }
  1036.  
  1037. static window_event_result automap_handler(window *wind,const d_event &event, automap *am)
  1038. {
  1039.         auto &Objects = LevelUniqueObjectState.Objects;
  1040.         auto &vcobjptr = Objects.vcptr;
  1041.         auto &vmobjptr = Objects.vmptr;
  1042.         switch (event.type)
  1043.         {
  1044.                 case EVENT_WINDOW_ACTIVATED:
  1045.                         game_flush_inputs();
  1046.                         event_toggle_focus(1);
  1047.                         key_toggle_repeat(0);
  1048.                         break;
  1049.  
  1050.                 case EVENT_WINDOW_DEACTIVATED:
  1051.                         event_toggle_focus(0);
  1052.                         key_toggle_repeat(1);
  1053.                         break;
  1054.  
  1055. #if SDL_MAJOR_VERSION == 2
  1056.                 case EVENT_WINDOW_RESIZE:
  1057.                         init_automap_subcanvas(am->automap_view, grd_curscreen->sc_canvas);
  1058.                         break;
  1059. #endif
  1060.  
  1061.                 case EVENT_IDLE:
  1062.                 case EVENT_JOYSTICK_BUTTON_UP:
  1063.                 case EVENT_JOYSTICK_BUTTON_DOWN:
  1064.                 case EVENT_JOYSTICK_MOVED:
  1065.                 case EVENT_MOUSE_BUTTON_UP:
  1066.                 case EVENT_MOUSE_BUTTON_DOWN:
  1067.                 case EVENT_MOUSE_MOVED:
  1068.                 case EVENT_KEY_RELEASE:
  1069.                         return automap_process_input(wind, event, am);
  1070.                 case EVENT_KEY_COMMAND:
  1071.                 {
  1072.                         window_event_result kret = automap_key_command(wind, event, am);
  1073.                         if (kret == window_event_result::ignored)
  1074.                                 kret = automap_process_input(wind, event, am);
  1075.                         return kret;
  1076.                 }
  1077.                        
  1078.                 case EVENT_WINDOW_DRAW:
  1079.                         {
  1080.                                 auto &plrobj = get_local_plrobj();
  1081.                                 automap_apply_input(am, plrobj.orient, plrobj.pos);
  1082.                         }
  1083.                         draw_automap(vcobjptr, am);
  1084.                         break;
  1085.                        
  1086.                 case EVENT_WINDOW_CLOSE:
  1087.                         if (!am->pause_game)
  1088.                                 ConsoleObject->mtype.phys_info.flags |= am->old_wiggle;         // Restore wiggle
  1089.                         event_toggle_focus(0);
  1090.                         key_toggle_repeat(1);
  1091.                         /* grd_curcanv points to `am->automap_view`, so grd_curcanv
  1092.                          * would become a dangling pointer after the call to delete.
  1093.                          * Redirect it to the default screen to avoid pointing to
  1094.                          * freed memory.  Setting grd_curcanv to nullptr would be
  1095.                          * better, but some code assumes that grd_curcanv is never
  1096.                          * nullptr, so instead set it to the default canvas.
  1097.                          * Eventually, grd_curcanv will be removed entirely.
  1098.                          */
  1099.                         gr_set_default_canvas();
  1100.                         std::default_delete<automap>()(am);
  1101.                         window_set_visible(Game_wind, 1);
  1102.                         Automap_active = 0;
  1103.                         multi_send_msgsend_state(msgsend_none);
  1104.                         return window_event_result::ignored;    // continue closing
  1105.                         break;
  1106.  
  1107.                 case EVENT_LOOP_BEGIN_LOOP:
  1108.                         kconfig_begin_loop(am->controls);
  1109.                         break;
  1110.  
  1111.                 default:
  1112.                         return window_event_result::ignored;
  1113.                         break;
  1114.         }
  1115.         return window_event_result::handled;
  1116. }
  1117.  
  1118. void do_automap()
  1119. {
  1120.         auto &Objects = LevelUniqueObjectState.Objects;
  1121.         auto &vmobjptr = Objects.vmptr;
  1122.         palette_array_t pal;
  1123.         automap *am = new automap{};
  1124.         window_create(grd_curscreen->sc_canvas, 0, 0, SWIDTH, SHEIGHT, automap_handler, am);
  1125.         am->leave_mode = 0;
  1126.         am->max_segments_away = 0;
  1127.         am->segment_limit = 1;
  1128.         am->num_edges = 0;
  1129.         am->end_valid_edges = 0;
  1130.         const auto max_edges = LevelSharedSegmentState.Num_segments * 12;
  1131.         am->max_edges = max_edges;
  1132.         am->edges = std::make_unique<Edge_info[]>(max_edges);
  1133.         am->drawingListBright = std::make_unique<Edge_info *[]>(max_edges);
  1134.         am->zoom = 0x9000;
  1135.         am->farthest_dist = (F1_0 * 20 * 50); // 50 segments away
  1136.         am->viewDist = 0;
  1137.  
  1138.         init_automap_colors(am);
  1139.         am->pause_game = !((Game_mode & GM_MULTI) && (!Endlevel_sequence)); // Set to 1 if everything is paused during automap...No pause during net.
  1140.  
  1141.         if (am->pause_game) {
  1142.                 window_set_visible(Game_wind, 0);
  1143.         }
  1144.         else
  1145.         {
  1146.                 am->old_wiggle = ConsoleObject->mtype.phys_info.flags & PF_WIGGLE;      // Save old wiggle
  1147.                 ConsoleObject->mtype.phys_info.flags &= ~PF_WIGGLE;             // Turn off wiggle
  1148.         }
  1149.  
  1150.         //Max_edges = min(MAX_EDGES_FROM_VERTS(Num_vertices),MAX_EDGES); //make maybe smaller than max
  1151.  
  1152.         gr_set_default_canvas();
  1153.  
  1154.         if ( am->viewDist==0 )
  1155.                 am->viewDist = ZOOM_DEFAULT;
  1156.  
  1157.         auto &plrobj = get_local_plrobj();
  1158.         am->viewMatrix = plrobj.orient;
  1159.         am->tangles.p = PITCH_DEFAULT;
  1160.         am->tangles.h  = 0;
  1161.         am->tangles.b  = 0;
  1162.         am->view_target = plrobj.pos;
  1163.        
  1164.         if (PlayerCfg.AutomapFreeFlight)
  1165.                 vm_vec_scale_add(am->view_position, plrobj.pos, am->viewMatrix.fvec, -ZOOM_DEFAULT);
  1166.  
  1167.         am->t1 = am->entry_time = timer_query();
  1168.         am->t2 = am->t1;
  1169.  
  1170.         //Fill in Automap_visited from Objects[Players[Player_num].objnum].segnum
  1171.         recompute_automap_segment_visibility(plrobj, am);
  1172.  
  1173.         // ZICO - code from above to show frame in OGL correctly. Redundant, but better readable.
  1174.         // KREATOR - Now applies to all platforms so double buffering is supported
  1175.         {
  1176.                 const auto pcx_error = pcx_read_bitmap(MAP_BACKGROUND_FILENAME, am->automap_background, pal);
  1177.                 if (pcx_error != pcx_result::SUCCESS)
  1178.                         con_printf(CON_URGENT, DXX_STRINGIZE_FL(__FILE__, __LINE__, "automap: File %s - PCX error: %s"), MAP_BACKGROUND_FILENAME, pcx_errormsg(pcx_error));
  1179.                 else
  1180.                         gr_remap_bitmap_good(am->automap_background, pal, -1, -1);
  1181.         }
  1182.         init_automap_subcanvas(am->automap_view, grd_curscreen->sc_canvas);
  1183.  
  1184.         gr_palette_load( gr_palette );
  1185.         Automap_active = 1;
  1186.         multi_send_msgsend_state(msgsend_automap);
  1187. }
  1188.  
  1189. void adjust_segment_limit(automap *am, int SegmentLimit)
  1190. {
  1191.         const auto &depth_array = am->depth_array;
  1192.         const auto predicate = [&depth_array, SegmentLimit](const segnum_t &e1) {
  1193.                 return depth_array[e1] <= SegmentLimit;
  1194.         };
  1195.         range_for (auto &i, unchecked_partial_range(am->edges.get(), am->end_valid_edges))
  1196.         {
  1197.                 const auto e = &i;
  1198.                 // Unchecked for speed
  1199.                 const auto &&range = unchecked_partial_range(e->segnum.begin(), e->num_faces);
  1200.                 if (std::any_of(range.begin(), range.end(), predicate))
  1201.                         e->flags &= ~EF_TOO_FAR;
  1202.                 else
  1203.                         e->flags |= EF_TOO_FAR;
  1204.         }
  1205. }
  1206.  
  1207. void draw_all_edges(grs_canvas &canvas, automap *const am)
  1208. {
  1209.         auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
  1210.         auto &Vertices = LevelSharedVertexState.get_vertices();
  1211.         int j;
  1212.         unsigned nbright = 0;
  1213.         ubyte nfacing,nnfacing;
  1214.         fix distance;
  1215.         fix min_distance = INT32_MAX;
  1216.  
  1217.         auto &vcvertptr = Vertices.vcptr;
  1218.         range_for (auto &i, unchecked_partial_range(am->edges.get(), am->end_valid_edges))
  1219.         {
  1220.                 const auto e = &i;
  1221.                 if (!(e->flags & EF_USED)) continue;
  1222.  
  1223.                 if ( e->flags & EF_TOO_FAR) continue;
  1224.  
  1225.                 if (e->flags&EF_FRONTIER) {     // A line that is between what we have seen and what we haven't
  1226.                         if ( (!(e->flags&EF_SECRET))&&(e->color==am->wall_normal_color))
  1227.                                 continue;       // If a line isn't secret and is normal color, then don't draw it
  1228.                 }
  1229.                 distance = Segment_points[e->verts[1]].p3_z;
  1230.  
  1231.                 if (min_distance>distance )
  1232.                         min_distance = distance;
  1233.  
  1234.                 if (!rotate_list(vcvertptr, e->verts).uand)
  1235.                 {                       //all off screen?
  1236.                         nfacing = nnfacing = 0;
  1237.                         auto &tv1 = *vcvertptr(e->verts[0]);
  1238.                         j = 0;
  1239.                         while( j<e->num_faces && (nfacing==0 || nnfacing==0) )  {
  1240.                                 if (!g3_check_normal_facing(tv1, vcsegptr(e->segnum[j])->shared_segment::sides[e->sides[j]].normals[0]))
  1241.                                         nfacing++;
  1242.                                 else
  1243.                                         nnfacing++;
  1244.                                 j++;
  1245.                         }
  1246.  
  1247.                         if ( nfacing && nnfacing )      {
  1248.                                 // a contour line
  1249.                                 am->drawingListBright[nbright++] = e;
  1250.                         } else if ( e->flags&(EF_DEFINING|EF_GRATE) )   {
  1251.                                 if ( nfacing == 0 )     {
  1252.                                         const uint8_t color = (e->flags & EF_NO_FADE)
  1253.                                                 ? e->color
  1254.                                                 : gr_fade_table[8][e->color];
  1255.                                         g3_draw_line(canvas, Segment_points[e->verts[0]], Segment_points[e->verts[1]], color);
  1256.                                 }       else {
  1257.                                         am->drawingListBright[nbright++] = e;
  1258.                                 }
  1259.                         }
  1260.                 }
  1261.         }
  1262.                
  1263.         if ( min_distance < 0 ) min_distance = 0;
  1264.  
  1265.         // Sort the bright ones using a shell sort
  1266.         const auto &&range = unchecked_partial_range(am->drawingListBright.get(), nbright);
  1267.         std::sort(range.begin(), range.end(), [](const Edge_info *const a, const Edge_info *const b) {
  1268.                 const auto &v1 = a->verts[0];
  1269.                 const auto &v2 = b->verts[0];
  1270.                 return Segment_points[v1].p3_z < Segment_points[v2].p3_z;
  1271.         });
  1272.         // Draw the bright ones
  1273.         range_for (const auto e, range)
  1274.         {
  1275.                 const auto p1 = &Segment_points[e->verts[0]];
  1276.                 const auto p2 = &Segment_points[e->verts[1]];
  1277.                 fix dist;
  1278.                 dist = p1->p3_z - min_distance;
  1279.                 // Make distance be 1.0 to 0.0, where 0.0 is 10 segments away;
  1280.                 if ( dist < 0 ) dist=0;
  1281.                 if ( dist >= am->farthest_dist ) continue;
  1282.  
  1283.                 const auto color = (e->flags & EF_NO_FADE)
  1284.                         ? e->color
  1285.                         : gr_fade_table[f2i((F1_0 - fixdiv(dist, am->farthest_dist)) * 31)][e->color]; 
  1286.                 g3_draw_line(canvas, *p1, *p2, color);
  1287.         }
  1288. }
  1289.  
  1290.  
  1291. //==================================================================
  1292. //
  1293. // All routines below here are used to build the Edge list
  1294. //
  1295. //==================================================================
  1296.  
  1297.  
  1298. //finds edge, filling in edge_ptr. if found old edge, returns index, else return -1
  1299. static std::pair<Edge_info &, unsigned> automap_find_edge(automap *const am, const unsigned v0, const unsigned v1)
  1300. {
  1301.         long vv, evv;
  1302.         int hash, oldhash;
  1303.  
  1304.         vv = (v1<<16) + v0;
  1305.  
  1306.         oldhash = hash = ((v0*5+v1) % am->max_edges);
  1307.         for (;;)
  1308.         {
  1309.                 auto &e = am->edges[hash];
  1310.                 const auto ev0 = e.verts[0];
  1311.                 const auto ev1 = e.verts[1];
  1312.                 evv = (ev1<<16)+ev0;
  1313.                 if (e.num_faces == 0)
  1314.                         return {e, hash};
  1315.                 else if (evv == vv)
  1316.                         return {e, UINT32_MAX};
  1317.                 else {
  1318.                         if (++hash==am->max_edges) hash=0;
  1319.                         if (hash==oldhash) Error("Edge list full!");
  1320.                 }
  1321.         }
  1322. }
  1323.  
  1324. static void add_one_edge(automap *const am, unsigned va, unsigned vb, const uint8_t color, const unsigned side, const segnum_t segnum, const uint8_t flags)
  1325. {
  1326.         if ( am->num_edges >= am->max_edges)    {
  1327.                 // GET JOHN! (And tell him that his
  1328.                 // MAX_EDGES_FROM_VERTS formula is hosed.)
  1329.                 // If he's not around, save the mine,
  1330.                 // and send him  mail so he can look
  1331.                 // at the mine later. Don't modify it.
  1332.                 // This is important if this happens.
  1333.                 Int3();         // LOOK ABOVE!!!!!!
  1334.                 return;
  1335.         }
  1336.  
  1337.         if ( va > vb )  {
  1338.                 std::swap(va, vb);
  1339.         }
  1340.         const auto &&ef = automap_find_edge(am, va, vb);
  1341.         const auto e = &ef.first;
  1342.                
  1343.         if (ef.second != UINT32_MAX)
  1344.         {
  1345.                 e->verts[0] = va;
  1346.                 e->verts[1] = vb;
  1347.                 e->color = color;
  1348.                 e->num_faces = 1;
  1349.                 e->flags = EF_USED | EF_DEFINING;                       // Assume a normal line
  1350.                 e->sides[0] = side;
  1351.                 e->segnum[0] = segnum;
  1352.                 am->num_edges++;
  1353.                 const auto i = ef.second + 1;
  1354.                 if (am->end_valid_edges < i)
  1355.                         am->end_valid_edges = i;
  1356.         } else {
  1357.                 if ( color != am->wall_normal_color )
  1358. #if defined(DXX_BUILD_DESCENT_II)
  1359.                         if (color != am->wall_revealed_color)
  1360. #endif
  1361.                                 e->color = color;
  1362.  
  1363.                 if ( e->num_faces < 4 ) {
  1364.                         e->sides[e->num_faces] = side;
  1365.                         e->segnum[e->num_faces] = segnum;
  1366.                         e->num_faces++;
  1367.                 }
  1368.         }
  1369.  
  1370.         e->flags |= flags;
  1371. }
  1372.  
  1373. static void add_one_unknown_edge( automap *am, int va, int vb )
  1374. {
  1375.         if ( va > vb )  {
  1376.                 std::swap(va, vb);
  1377.         }
  1378.  
  1379.         const auto &&ef = automap_find_edge(am, va, vb);
  1380.         if (ef.second == UINT32_MAX)
  1381.                 ef.first.flags |= EF_FRONTIER;          // Mark as a border edge
  1382. }
  1383.  
  1384. static void add_segment_edges(fvcsegptr &vcsegptr, fvcwallptr &vcwallptr, automap *am, const vcsegptridx_t seg)
  1385. {
  1386.         auto &ControlCenterState = LevelUniqueObjectState.ControlCenterState;
  1387.         auto &WallAnims = GameSharedState.WallAnims;
  1388. #if defined(DXX_BUILD_DESCENT_II)
  1389.         auto &Objects = LevelUniqueObjectState.Objects;
  1390.         auto &vmobjptr = Objects.vmptr;
  1391. #endif
  1392.         ubyte   color;
  1393.        
  1394.         for (unsigned sn = 0; sn < MAX_SIDES_PER_SEGMENT; ++sn)
  1395.         {
  1396.                 uint8_t hidden_flag = 0;
  1397.                 uint8_t is_grate = 0;
  1398.                 uint8_t no_fade = 0;
  1399.  
  1400.                 color = 255;
  1401.                 if (seg->children[sn] == segment_none) {
  1402.                         color = am->wall_normal_color;
  1403.                 }
  1404.  
  1405.                 switch( seg->special )  {
  1406.                 case SEGMENT_IS_FUELCEN:
  1407.                         color = BM_XRGB( 29, 27, 13 );
  1408.                         break;
  1409.                 case SEGMENT_IS_CONTROLCEN:
  1410.                         if (ControlCenterState.Control_center_present)
  1411.                                 color = BM_XRGB( 29, 0, 0 );
  1412.                         break;
  1413.                 case SEGMENT_IS_ROBOTMAKER:
  1414.                         color = BM_XRGB( 29, 0, 31 );
  1415.                         break;
  1416.                 }
  1417.  
  1418.                 const auto wall_num = seg->shared_segment::sides[sn].wall_num;
  1419.                 if (wall_num != wall_none)
  1420.                 {
  1421.                         auto &w = *vcwallptr(wall_num);
  1422. #if defined(DXX_BUILD_DESCENT_II)
  1423.                         auto trigger_num = w.trigger;
  1424.                         auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
  1425.                         auto &vmtrgptr = Triggers.vmptr;
  1426.                         if (trigger_num != trigger_none && vmtrgptr(trigger_num)->type == trigger_action::secret_exit)
  1427.                                 {
  1428.                             color = BM_XRGB( 29, 0, 31 );
  1429.                                 no_fade = EF_NO_FADE;
  1430.                                  goto Here;
  1431.                                 }      
  1432. #endif
  1433.  
  1434.                         switch(w.type)
  1435.                         {
  1436.                         case WALL_DOOR:
  1437.                                 if ((w.keys == KEY_BLUE && (color = am->wall_door_blue, true)) ||
  1438.                                         (w.keys == KEY_GOLD && (color = am->wall_door_gold, true)) ||
  1439.                                         (w.keys == KEY_RED && (color = am->wall_door_red, true)))
  1440.                                 {
  1441.                                         no_fade = EF_NO_FADE;
  1442.                                 } else if (!(WallAnims[w.clip_num].flags & WCF_HIDDEN)) {
  1443.                                         auto connected_seg = seg->children[sn];
  1444.                                         if (connected_seg != segment_none) {
  1445.                                                 const shared_segment &vcseg = *vcsegptr(connected_seg);
  1446.                                                 const auto &connected_side = find_connect_side(seg, vcseg);
  1447.                                                 auto &wall = *vcwallptr(vcseg.sides[connected_side].wall_num);
  1448.                                                 switch (wall.keys)
  1449.                                                 {
  1450.                                                         case KEY_BLUE:
  1451.                                                                 color = am->wall_door_blue;
  1452.                                                                 no_fade = EF_NO_FADE;
  1453.                                                                 break;
  1454.                                                         case KEY_GOLD:
  1455.                                                                 color = am->wall_door_gold;
  1456.                                                                 no_fade = EF_NO_FADE;
  1457.                                                                 break;
  1458.                                                         case KEY_RED:
  1459.                                                                 color = am->wall_door_red;
  1460.                                                                 no_fade = EF_NO_FADE;
  1461.                                                                 break;
  1462.                                                         default:
  1463.                                                                 color = am->wall_door_color;
  1464.                                                                 break;
  1465.                                                 }
  1466.                                         }
  1467.                                 } else {
  1468.                                         color = am->wall_normal_color;
  1469.                                         hidden_flag = EF_SECRET;
  1470.                                 }
  1471.                                 break;
  1472.                         case WALL_CLOSED:
  1473.                                 // Make grates draw properly
  1474.                                 // NOTE: In original D1, is_grate is 1, hidden_flag not used so grates never fade. I (zico) like this so I leave this alone for now.
  1475.                                 if (!(is_grate = WALL_IS_DOORWAY(GameBitmaps, Textures, vcwallptr, seg, sn) & WID_RENDPAST_FLAG))
  1476.                                         hidden_flag = EF_SECRET;
  1477.                                 color = am->wall_normal_color;
  1478.                                 break;
  1479.                         case WALL_BLASTABLE:
  1480.                                 // Hostage doors
  1481.                                 color = am->wall_door_color;   
  1482.                                 break;
  1483.                         }
  1484.                 }
  1485.        
  1486.                 if (seg==Player_init[Player_num].segnum)
  1487.                         color = BM_XRGB(31,0,31);
  1488.  
  1489.                 if ( color != 255 )     {
  1490. #if defined(DXX_BUILD_DESCENT_II)
  1491.                         // If they have a map powerup, draw unvisited areas in dark blue.
  1492.                         // NOTE: D1 originally had this part of code but w/o cheat-check. It's only supposed to draw blue with powerup that does not exist in D1. So make this D2-only
  1493.                         if (!Automap_debug_show_all_segments)
  1494.                         {
  1495.                         auto &player_info = get_local_plrobj().ctype.player_info;
  1496.                                 if ((cheats.fullautomap || player_info.powerup_flags & PLAYER_FLAGS_MAP_ALL) && !LevelUniqueAutomapState.Automap_visited[seg])
  1497.                                 color = am->wall_revealed_color;
  1498.                         }
  1499.                         Here:
  1500. #endif
  1501.                         const auto vertex_list = get_side_verts(seg,sn);
  1502.                         const uint8_t flags = hidden_flag | no_fade;
  1503.                         add_one_edge(am, vertex_list[0], vertex_list[1], color, sn, seg, flags);
  1504.                         add_one_edge(am, vertex_list[1], vertex_list[2], color, sn, seg, flags);
  1505.                         add_one_edge(am, vertex_list[2], vertex_list[3], color, sn, seg, flags);
  1506.                         add_one_edge(am, vertex_list[3], vertex_list[0], color, sn, seg, flags);
  1507.  
  1508.                         if ( is_grate ) {
  1509.                                 const uint8_t grate_flags = flags | EF_GRATE;
  1510.                                 add_one_edge(am, vertex_list[0], vertex_list[2], color, sn, seg, grate_flags);
  1511.                                 add_one_edge(am, vertex_list[1], vertex_list[3], color, sn, seg, grate_flags);
  1512.                         }
  1513.                 }
  1514.         }
  1515. }
  1516.  
  1517.  
  1518. // Adds all the edges from a segment we haven't visited yet.
  1519.  
  1520. static void add_unknown_segment_edges(automap *am, const shared_segment &seg)
  1521. {
  1522.         for (unsigned sn = 0; sn < MAX_SIDES_PER_SEGMENT; ++sn)
  1523.         {
  1524.                 // Only add edges that have no children
  1525.                 if (seg.children[sn] == segment_none) {
  1526.                         const auto vertex_list = get_side_verts(seg, sn);
  1527.        
  1528.                         add_one_unknown_edge( am, vertex_list[0], vertex_list[1] );
  1529.                         add_one_unknown_edge( am, vertex_list[1], vertex_list[2] );
  1530.                         add_one_unknown_edge( am, vertex_list[2], vertex_list[3] );
  1531.                         add_one_unknown_edge( am, vertex_list[3], vertex_list[0] );
  1532.                 }
  1533.         }
  1534. }
  1535.  
  1536. void automap_build_edge_list(automap *am, int add_all_edges)
  1537. {      
  1538.         // clear edge list
  1539.         range_for (auto &i, unchecked_partial_range(am->edges.get(), am->max_edges))
  1540.         {
  1541.                 i.num_faces = 0;
  1542.                 i.flags = 0;
  1543.         }
  1544.         am->num_edges = 0;
  1545.         am->end_valid_edges = 0;
  1546.  
  1547.         auto &Walls = LevelUniqueWallSubsystemState.Walls;
  1548.         auto &vcwallptr = Walls.vcptr;
  1549.         if (add_all_edges)      {
  1550.                 // Cheating, add all edges as visited
  1551.                 range_for (const auto &&segp, vcsegptridx)
  1552.                 {
  1553. #if DXX_USE_EDITOR
  1554.                         if (segp->segnum != segment_none)
  1555. #endif
  1556.                         {
  1557.                                 add_segment_edges(vcsegptr, vcwallptr, am, segp);
  1558.                         }
  1559.                 }
  1560.         } else {
  1561.                 // Not cheating, add visited edges, and then unvisited edges
  1562.                 range_for (const auto &&segp, vcsegptridx)
  1563.                 {
  1564. #if DXX_USE_EDITOR
  1565.                         if (segp->segnum != segment_none)
  1566. #endif
  1567.                                 if (LevelUniqueAutomapState.Automap_visited[segp])
  1568.                                 {
  1569.                                         add_segment_edges(vcsegptr, vcwallptr, am, segp);
  1570.                                 }
  1571.                 }
  1572.                 range_for (const auto &&segp, vcsegptridx)
  1573.                 {
  1574. #if DXX_USE_EDITOR
  1575.                         if (segp->segnum != segment_none)
  1576. #endif
  1577.                                 if (!LevelUniqueAutomapState.Automap_visited[segp])
  1578.                                 {
  1579.                                         add_unknown_segment_edges(am, segp);
  1580.                                 }
  1581.                 }
  1582.         }
  1583.  
  1584.         // Find unnecessary lines (These are lines that don't have to be drawn because they have small curvature)
  1585.         range_for (auto &i, unchecked_partial_range(am->edges.get(), am->end_valid_edges))
  1586.         {
  1587.                 const auto e = &i;
  1588.                 if (!(e->flags&EF_USED)) continue;
  1589.  
  1590.                 const auto num_faces = e->num_faces;
  1591.                 if (num_faces < 2)
  1592.                         continue;
  1593.                 for (unsigned e1 = 0; e1 < num_faces; ++e1)
  1594.                 {
  1595.                         const auto e1segnum = e->segnum[e1];
  1596.                         const auto &e1siden0 = vcsegptr(e1segnum)->shared_segment::sides[e->sides[e1]].normals[0];
  1597.                         for (unsigned e2 = 1; e2 < num_faces; ++e2)
  1598.                         {
  1599.                                 if (e1 == e2)
  1600.                                         continue;
  1601.                                 const auto e2segnum = e->segnum[e2];
  1602.                                 if (e1segnum == e2segnum)
  1603.                                         continue;
  1604.                                 if (vm_vec_dot(e1siden0, vcsegptr(e2segnum)->shared_segment::sides[e->sides[e2]].normals[0]) > (F1_0 - (F1_0 / 10)))
  1605.                                 {
  1606.                                         e->flags &= (~EF_DEFINING);
  1607.                                         break;
  1608.                                 }
  1609.                         }
  1610.                         if (!(e->flags & EF_DEFINING))
  1611.                                 break;
  1612.                 }
  1613.         }
  1614. }
  1615.  
  1616. #if defined(DXX_BUILD_DESCENT_II)
  1617. static unsigned Marker_index;
  1618.  
  1619. void InitMarkerInput ()
  1620. {
  1621.         //find free marker slot
  1622.         const auto game_mode = Game_mode;
  1623.         const auto max_numplayers = Netgame.max_numplayers;
  1624.         const auto maxdrop = MarkerState.get_markers_per_player(game_mode, max_numplayers);
  1625.         const auto &&game_marker_range = get_game_marker_range(game_mode, max_numplayers, Player_num, maxdrop);
  1626.         const auto &&player_marker_range = get_player_marker_range(maxdrop);
  1627.         const auto &&zipped_marker_range = zip(game_marker_range, player_marker_range, unchecked_partial_range(&MarkerState.imobjidx[*game_marker_range.begin()], maxdrop));
  1628.         const auto &&mb = zipped_marker_range.begin();
  1629.         const auto &&me = zipped_marker_range.end();
  1630.         auto iter = mb;
  1631.         for (;;)
  1632.         {
  1633.                 auto &&[gmi, pmi, objidx] = *iter;
  1634.                 if (objidx == object_none)              //found free slot!
  1635.                 {
  1636.                         MarkerState.MarkerBeingDefined = pmi;
  1637.                         break;
  1638.                 }
  1639.                 if (++ iter == me)              //no free slot
  1640.                 {
  1641.                         if (game_mode & GM_MULTI)
  1642.                         {
  1643.                                 //in multi, replace oldest
  1644.                                 MarkerState.MarkerBeingDefined = static_cast<player_marker_index>((static_cast<unsigned>(MarkerState.LastMarkerDropped) + 1) & (maxdrop - 1));
  1645.                                 break;
  1646.                         }
  1647.                         else
  1648.                         {
  1649.                                 HUD_init_message_literal(HM_DEFAULT, "No free marker slots");
  1650.                                 return;
  1651.                         }
  1652.                 }
  1653.         }
  1654.  
  1655.         //got a free slot.  start inputting marker message
  1656.  
  1657.         Marker_input[0]=0;
  1658.         Marker_index=0;
  1659.         key_toggle_repeat(1);
  1660. }
  1661.  
  1662. window_event_result MarkerInputMessage(int key)
  1663. {
  1664.         auto &Objects = LevelUniqueObjectState.Objects;
  1665.         auto &vmobjptr = Objects.vmptr;
  1666.         auto &vmobjptridx = Objects.vmptridx;
  1667.         switch( key )
  1668.         {
  1669.                 case KEY_LEFT:
  1670.                 case KEY_BACKSP:
  1671.                 case KEY_PAD4:
  1672.                         if (Marker_index > 0)
  1673.                                 Marker_index--;
  1674.                         Marker_input[Marker_index] = 0;
  1675.                         break;
  1676.                 case KEY_ENTER:
  1677.                         {
  1678.                                 const auto player_marker_num = MarkerState.MarkerBeingDefined;
  1679.                                 MarkerState.LastMarkerDropped = player_marker_num;
  1680.                                 const auto game_marker_num = convert_player_marker_index_to_game_marker_index(Game_mode, Netgame.max_numplayers, Player_num, player_marker_num);
  1681.                                 MarkerState.message[game_marker_num] = Marker_input;
  1682.                                 DropMarker(vmobjptridx, vmsegptridx, get_local_plrobj(), game_marker_num, player_marker_num);
  1683.                         }
  1684.                         DXX_BOOST_FALLTHROUGH;
  1685.                 case KEY_F8:
  1686.                 case KEY_ESC:
  1687.                         MarkerState.MarkerBeingDefined = player_marker_index::None;
  1688.                         key_toggle_repeat(0);
  1689.                         game_flush_inputs();
  1690.                         break;
  1691.                 default:
  1692.                 {
  1693.                         int ascii = key_ascii();
  1694.                         if ((ascii < 255 ))
  1695.                                 if (Marker_index < Marker_input.size() - 1)
  1696.                                 {
  1697.                                         Marker_input[Marker_index++] = ascii;
  1698.                                         Marker_input[Marker_index] = 0;
  1699.                                 }
  1700.                         return window_event_result::ignored;
  1701.                 }
  1702.         }
  1703.         return window_event_result::handled;
  1704. }
  1705. #endif
  1706. }
  1707.