Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 1 | pmbaty | 1 | /* | 
        
| 2 |  * Portions of this file are copyright Rebirth contributors and licensed as | 
        ||
| 3 |  * described in COPYING.txt. | 
        ||
| 4 |  * Portions of this file are copyright Parallax Software and licensed | 
        ||
| 5 |  * according to the Parallax license below. | 
        ||
| 6 |  * See COPYING.txt for license details. | 
        ||
| 7 | |||
| 8 | THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX | 
        ||
| 9 | SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO | 
        ||
| 10 | END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A | 
        ||
| 11 | ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS | 
        ||
| 12 | IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS | 
        ||
| 13 | SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE | 
        ||
| 14 | FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE | 
        ||
| 15 | CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS | 
        ||
| 16 | AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. | 
        ||
| 17 | COPYRIGHT 1993-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 | } |