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 |  * Stuff for rendering the HUD | 
        ||
| 23 |  * | 
        ||
| 24 |  */ | 
        ||
| 25 | |||
| 26 | #include <stdio.h> | 
        ||
| 27 | #include <string.h> | 
        ||
| 28 | #include <stdlib.h> | 
        ||
| 29 | #include "timer.h" | 
        ||
| 30 | #include "pstypes.h" | 
        ||
| 31 | #include "console.h" | 
        ||
| 32 | #include "inferno.h" | 
        ||
| 33 | #include "dxxerror.h" | 
        ||
| 34 | #include "gr.h" | 
        ||
| 35 | #include "palette.h" | 
        ||
| 36 | #include "bm.h" | 
        ||
| 37 | #include "player.h" | 
        ||
| 38 | #include "render.h" | 
        ||
| 39 | #include "menu.h" | 
        ||
| 40 | #include "newmenu.h" | 
        ||
| 41 | #include "screens.h" | 
        ||
| 42 | #include "config.h" | 
        ||
| 43 | #include "maths.h" | 
        ||
| 44 | #include "robot.h" | 
        ||
| 45 | #include "game.h" | 
        ||
| 46 | #include "gauges.h" | 
        ||
| 47 | #include "gamefont.h" | 
        ||
| 48 | #include "newdemo.h" | 
        ||
| 49 | #include "text.h" | 
        ||
| 50 | #include "multi.h" | 
        ||
| 51 | #include "hudmsg.h" | 
        ||
| 52 | #include "endlevel.h" | 
        ||
| 53 | #include "cntrlcen.h" | 
        ||
| 54 | #include "powerup.h" | 
        ||
| 55 | #include "laser.h" | 
        ||
| 56 | #include "playsave.h" | 
        ||
| 57 | #include "automap.h" | 
        ||
| 58 | #include "mission.h" | 
        ||
| 59 | #include "gameseq.h" | 
        ||
| 60 | #include "args.h" | 
        ||
| 61 | #include "object.h" | 
        ||
| 62 | |||
| 63 | #include "compiler-range_for.h" | 
        ||
| 64 | #include "d_range.h" | 
        ||
| 65 | |||
| 66 | #if DXX_USE_OGL | 
        ||
| 67 | #include "ogl_init.h" | 
        ||
| 68 | #endif | 
        ||
| 69 | |||
| 70 | namespace dcx {  | 
        ||
| 71 | int netplayerinfo_on;  | 
        ||
| 72 | } | 
        ||
| 73 | |||
| 74 | namespace dsx {  | 
        ||
| 75 | #if defined(DXX_BUILD_DESCENT_I) | 
        ||
| 76 | static inline void game_draw_marker_message(grs_canvas &)  | 
        ||
| 77 | { | 
        ||
| 78 | } | 
        ||
| 79 | #elif defined(DXX_BUILD_DESCENT_II) | 
        ||
| 80 | static void game_draw_marker_message(grs_canvas &canvas)  | 
        ||
| 81 | { | 
        ||
| 82 | if (MarkerState.DefiningMarkerMessage())  | 
        ||
| 83 |         { | 
        ||
| 84 | gr_set_fontcolor(canvas, BM_XRGB(0, 63, 0),-1);  | 
        ||
| 85 | auto &game_font = *GAME_FONT;  | 
        ||
| 86 | gr_printf(canvas, game_font, 0x8000, (LINE_SPACING(game_font, game_font) * 5) + FSPACY(1), "Marker: %s%c", &Marker_input[0], Marker_input[Marker_input.size() - 2] ? 0 : '_');  | 
        ||
| 87 |         } | 
        ||
| 88 | } | 
        ||
| 89 | #endif | 
        ||
| 90 | } | 
        ||
| 91 | |||
| 92 | namespace dcx {  | 
        ||
| 93 | |||
| 94 | static void game_draw_multi_message(grs_canvas &canvas)  | 
        ||
| 95 | { | 
        ||
| 96 | if (!(Game_mode&GM_MULTI))  | 
        ||
| 97 | return;  | 
        ||
| 98 | const auto sending = multi_sending_message[Player_num];  | 
        ||
| 99 | int defining;  | 
        ||
| 100 | if (!sending && !(defining = multi_defining_message))  | 
        ||
| 101 | return;  | 
        ||
| 102 | gr_set_fontcolor(canvas, BM_XRGB(0, 63, 0),-1);  | 
        ||
| 103 | auto &game_font = *GAME_FONT;  | 
        ||
| 104 | const auto &&y = (LINE_SPACING(game_font, game_font) * 5) + FSPACY(1);  | 
        ||
| 105 | if (sending)  | 
        ||
| 106 | gr_printf(canvas, game_font, 0x8000, y, "%s: %s_", TXT_MESSAGE, Network_message.data());  | 
        ||
| 107 |         else | 
        ||
| 108 | gr_printf(canvas, game_font, 0x8000, y, "%s #%d: %s_", TXT_MACRO, defining, Network_message.data());  | 
        ||
| 109 | } | 
        ||
| 110 | |||
| 111 | static void show_framerate(grs_canvas &canvas)  | 
        ||
| 112 | { | 
        ||
| 113 | static int fps_count = 0, fps_rate = 0;  | 
        ||
| 114 | static fix64 fps_time = 0;  | 
        ||
| 115 | fps_count++;  | 
        ||
| 116 | const auto tq = timer_query();  | 
        ||
| 117 | if (tq >= fps_time + F1_0)  | 
        ||
| 118 |         { | 
        ||
| 119 | fps_rate = fps_count;  | 
        ||
| 120 | fps_count = 0;  | 
        ||
| 121 | fps_time += F1_0;  | 
        ||
| 122 |         } | 
        ||
| 123 | const auto &&line_spacing = LINE_SPACING(*canvas.cv_font, *GAME_FONT);  | 
        ||
| 124 | unsigned line_displacement;  | 
        ||
| 125 | switch (PlayerCfg.CockpitMode[1])  | 
        ||
| 126 |         { | 
        ||
| 127 | case CM_FULL_COCKPIT:  | 
        ||
| 128 | line_displacement = line_spacing * 2;  | 
        ||
| 129 | break;  | 
        ||
| 130 | case CM_STATUS_BAR:  | 
        ||
| 131 | line_displacement = line_spacing;  | 
        ||
| 132 | break;  | 
        ||
| 133 | case CM_FULL_SCREEN:  | 
        ||
| 134 | switch (PlayerCfg.HudMode)  | 
        ||
| 135 |                         { | 
        ||
| 136 | case HudType::Standard:  | 
        ||
| 137 | line_displacement = line_spacing * 4;  | 
        ||
| 138 | break;  | 
        ||
| 139 | case HudType::Alternate1:  | 
        ||
| 140 | line_displacement = line_spacing * 6;  | 
        ||
| 141 | if (Game_mode & GM_MULTI)  | 
        ||
| 142 | line_displacement -= line_spacing * 4;  | 
        ||
| 143 | break;  | 
        ||
| 144 | case HudType::Alternate2:  | 
        ||
| 145 | line_displacement = line_spacing;  | 
        ||
| 146 | break;  | 
        ||
| 147 | case HudType::Hidden:  | 
        ||
| 148 | default:  | 
        ||
| 149 | return;  | 
        ||
| 150 |                         } | 
        ||
| 151 | break;  | 
        ||
| 152 | case CM_LETTERBOX:  | 
        ||
| 153 | case CM_REAR_VIEW:  | 
        ||
| 154 | default:  | 
        ||
| 155 | return;  | 
        ||
| 156 |         } | 
        ||
| 157 | const auto &game_font = *GAME_FONT;  | 
        ||
| 158 | gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0),-1);  | 
        ||
| 159 | char buf[16];  | 
        ||
| 160 | if (CGameArg.DbgVerbose)  | 
        ||
| 161 | snprintf(buf, sizeof(buf), "%iFPS (%.2fms)", fps_rate, (FrameTime * 1000.) / F1_0);  | 
        ||
| 162 |         else | 
        ||
| 163 | snprintf(buf, sizeof(buf), "%iFPS", fps_rate);  | 
        ||
| 164 | int w, h;  | 
        ||
| 165 | gr_get_string_size(game_font, buf, &w, &h, nullptr);  | 
        ||
| 166 | const auto bm_h = canvas.cv_bitmap.bm_h;  | 
        ||
| 167 | gr_string(canvas, game_font, FSPACX(318) - w, bm_h - line_displacement, buf, w, h);  | 
        ||
| 168 | } | 
        ||
| 169 | |||
| 170 | } | 
        ||
| 171 | |||
| 172 | namespace dsx {  | 
        ||
| 173 | static void show_netplayerinfo()  | 
        ||
| 174 | { | 
        ||
| 175 | auto &Objects = LevelUniqueObjectState.Objects;  | 
        ||
| 176 | auto &vcobjptr = Objects.vcptr;  | 
        ||
| 177 | int x=0, y=0;  | 
        ||
| 178 | static const char *const eff_strings[]={"trashing","really hurting","seriously affecting","hurting","affecting","tarnishing"};  | 
        ||
| 179 | |||
| 180 | gr_set_default_canvas();  | 
        ||
| 181 | auto &canvas = *grd_curcanv;  | 
        ||
| 182 | gr_set_fontcolor(canvas, 255, -1);  | 
        ||
| 183 | |||
| 184 | const auto &&fspacx = FSPACX();  | 
        ||
| 185 | const auto &&fspacx120 = fspacx(120);  | 
        ||
| 186 | const auto &&fspacy84 = FSPACY(84);  | 
        ||
| 187 | x = (SWIDTH / 2) - fspacx120;  | 
        ||
| 188 | y = (SHEIGHT / 2) - fspacy84;  | 
        ||
| 189 | |||
| 190 | gr_settransblend(canvas, 14, gr_blend::normal);  | 
        ||
| 191 | const uint8_t color000 = BM_XRGB(0, 0, 0);  | 
        ||
| 192 | gr_rect(canvas, (SWIDTH / 2) - fspacx120, (SHEIGHT / 2) - fspacy84, (SWIDTH / 2) + fspacx120, (SHEIGHT / 2) + fspacy84, color000);  | 
        ||
| 193 | gr_settransblend(canvas, GR_FADE_OFF, gr_blend::normal);  | 
        ||
| 194 | |||
| 195 |         // general game information | 
        ||
| 196 | const auto &&line_spacing = LINE_SPACING(*canvas.cv_font, *GAME_FONT);  | 
        ||
| 197 | y += line_spacing;  | 
        ||
| 198 | auto &game_font = *GAME_FONT;  | 
        ||
| 199 | gr_string(canvas, game_font, 0x8000, y, Netgame.game_name.data());  | 
        ||
| 200 | y += line_spacing;  | 
        ||
| 201 | gr_printf(canvas, game_font, 0x8000, y, "%s - lvl: %i", Netgame.mission_title.data(), Netgame.levelnum);  | 
        ||
| 202 | |||
| 203 | const auto &&fspacx8 = fspacx(8);  | 
        ||
| 204 | x += fspacx8;  | 
        ||
| 205 | y += line_spacing * 2;  | 
        ||
| 206 | unsigned gamemode = Netgame.gamemode;  | 
        ||
| 207 | gr_printf(canvas, game_font, x, y, "game mode: %s", gamemode < GMNames.size() ? GMNames[gamemode] : "INVALID");  | 
        ||
| 208 | y += line_spacing;  | 
        ||
| 209 | gr_printf(canvas, game_font, x, y,"difficulty: %s", MENU_DIFFICULTY_TEXT(Netgame.difficulty));  | 
        ||
| 210 | y += line_spacing;  | 
        ||
| 211 |         { | 
        ||
| 212 | auto &plr = get_local_player();  | 
        ||
| 213 | gr_printf(canvas, game_font, x, y,"level time: %i:%02i:%02i", plr.hours_level, f2i(plr.time_level) / 60 % 60, f2i(plr.time_level) % 60);  | 
        ||
| 214 | y += line_spacing;  | 
        ||
| 215 | gr_printf(canvas, game_font, x, y,"total time: %i:%02i:%02i", plr.hours_total, f2i(plr.time_total) / 60 % 60, f2i(plr.time_total) % 60);  | 
        ||
| 216 |         } | 
        ||
| 217 | y += line_spacing;  | 
        ||
| 218 | if (Netgame.KillGoal)  | 
        ||
| 219 | gr_printf(canvas, game_font, x, y,"Kill goal: %d",Netgame.KillGoal*5);  | 
        ||
| 220 | |||
| 221 |         // player information (name, kills, ping, game efficiency) | 
        ||
| 222 | y += line_spacing * 2;  | 
        ||
| 223 | gr_string(canvas, game_font, x, y, "player");  | 
        ||
| 224 | gr_string(canvas, game_font, x + fspacx8 * 7, y, ((Game_mode & GM_MULTI_COOP)  | 
        ||
| 225 | ? "score"  | 
        ||
| 226 | : (gr_string(canvas, game_font, x + fspacx8 * 12, y, "deaths"), "kills")  | 
        ||
| 227 | ));  | 
        ||
| 228 | gr_string(canvas, game_font, x + fspacx8 * 18, y, "ping");  | 
        ||
| 229 | gr_string(canvas, game_font, x + fspacx8 * 23, y, "efficiency");  | 
        ||
| 230 | |||
| 231 |         // process players table | 
        ||
| 232 | for (unsigned i = 0; i < MAX_PLAYERS; ++i)  | 
        ||
| 233 |         { | 
        ||
| 234 | auto &plr = *vcplayerptr(i);  | 
        ||
| 235 | if (!plr.connected)  | 
        ||
| 236 | continue;  | 
        ||
| 237 | |||
| 238 | y += line_spacing;  | 
        ||
| 239 | |||
| 240 | const auto color = get_player_or_team_color(i);  | 
        ||
| 241 | auto &prgb = player_rgb[color];  | 
        ||
| 242 | gr_set_fontcolor(canvas, BM_XRGB(prgb.r, prgb.g, prgb.b), -1);  | 
        ||
| 243 | gr_string(canvas, game_font, x, y, plr.callsign);  | 
        ||
| 244 |                 { | 
        ||
| 245 | auto &plrobj = *vcobjptr(plr.objnum);  | 
        ||
| 246 | auto &player_info = plrobj.ctype.player_info;  | 
        ||
| 247 | auto v = ((Game_mode & GM_MULTI_COOP)  | 
        ||
| 248 | ? player_info.mission.score  | 
        ||
| 249 | : (gr_printf(canvas, game_font, x + fspacx8 * 12, y,"%-6d", player_info.net_killed_total), player_info.net_kills_total)  | 
        ||
| 250 | );  | 
        ||
| 251 | gr_printf(canvas, game_font, x + fspacx8 * 7, y, "%-6d", v);  | 
        ||
| 252 |                 } | 
        ||
| 253 | |||
| 254 | gr_printf(canvas, game_font, x + fspacx8 * 18, y,"%-6d", Netgame.players[i].ping);  | 
        ||
| 255 | if (i != Player_num)  | 
        ||
| 256 | gr_printf(canvas, game_font, x + fspacx8 * 23, y, "%hu/%hu", kill_matrix[Player_num][i], kill_matrix[i][Player_num]);  | 
        ||
| 257 |         } | 
        ||
| 258 | |||
| 259 | y += (line_spacing * 2) + (line_spacing * (MAX_PLAYERS - N_players));  | 
        ||
| 260 | |||
| 261 |         // printf team scores | 
        ||
| 262 | if (Game_mode & GM_TEAM)  | 
        ||
| 263 |         { | 
        ||
| 264 | gr_set_fontcolor(canvas, 255, -1);  | 
        ||
| 265 | gr_string(canvas, game_font, x, y, "team");  | 
        ||
| 266 | gr_string(canvas, game_font, x + fspacx8 * 8, y, "score");  | 
        ||
| 267 | y += line_spacing;  | 
        ||
| 268 | gr_set_fontcolor(canvas, BM_XRGB(player_rgb[0].r, player_rgb[0].g, player_rgb[0].b),-1);  | 
        ||
| 269 | gr_printf(canvas, game_font, x, y, "%s:", static_cast<const char *>(Netgame.team_name[0]));  | 
        ||
| 270 | gr_printf(canvas, game_font, x + fspacx8 * 8, y, "%i", team_kills[0]);  | 
        ||
| 271 | y += line_spacing;  | 
        ||
| 272 | gr_set_fontcolor(canvas, BM_XRGB(player_rgb[1].r, player_rgb[1].g, player_rgb[1].b),-1);  | 
        ||
| 273 | gr_printf(canvas, game_font, x, y, "%s:", static_cast<const char *>(Netgame.team_name[1]));  | 
        ||
| 274 | gr_printf(canvas, game_font, x + fspacx8 * 8, y, "%i", team_kills[1]);  | 
        ||
| 275 | y += line_spacing * 2;  | 
        ||
| 276 |         } | 
        ||
| 277 |         else | 
        ||
| 278 | y += line_spacing * 4;  | 
        ||
| 279 | |||
| 280 | gr_set_fontcolor(canvas, 255, -1);  | 
        ||
| 281 | |||
| 282 |         // additional information about game - hoard, ranking | 
        ||
| 283 | |||
| 284 | #if defined(DXX_BUILD_DESCENT_II) | 
        ||
| 285 | if (game_mode_hoard())  | 
        ||
| 286 |         { | 
        ||
| 287 | if (hoard_highest_record_stats.player >= Players.size())  | 
        ||
| 288 | gr_string(canvas, game_font, 0x8000, y, "There is no record yet for this level.");  | 
        ||
| 289 |                 else | 
        ||
| 290 | gr_printf(canvas, game_font, 0x8000, y, "%s has the record at %d points.", static_cast<const char *>(vcplayerptr(hoard_highest_record_stats.player)->callsign), hoard_highest_record_stats.points);  | 
        ||
| 291 |         } | 
        ||
| 292 |         else | 
        ||
| 293 | #endif | 
        ||
| 294 | if (!PlayerCfg.NoRankings)  | 
        ||
| 295 |         { | 
        ||
| 296 | const int ieff = (PlayerCfg.NetlifeKills + PlayerCfg.NetlifeKilled <= 0)  | 
        ||
| 297 | ? 0  | 
        ||
| 298 | : static_cast<int>(  | 
        ||
| 299 | static_cast<float>(PlayerCfg.NetlifeKills) / (  | 
        ||
| 300 | static_cast<float>(PlayerCfg.NetlifeKilled) + static_cast<float>(PlayerCfg.NetlifeKills)  | 
        ||
| 301 | ) * 100.0  | 
        ||
| 302 | );  | 
        ||
| 303 | const unsigned eff = ieff < 0 ? 0 : static_cast<unsigned>(ieff);  | 
        ||
| 304 | gr_printf(canvas, game_font, 0x8000, y, "Your lifetime efficiency of %d%% (%d/%d)", eff, PlayerCfg.NetlifeKills, PlayerCfg.NetlifeKilled);  | 
        ||
| 305 | y += line_spacing;  | 
        ||
| 306 | if (eff<60)  | 
        ||
| 307 | gr_printf(canvas, game_font, 0x8000, y, "is %s your ranking.", eff_strings[eff / 10]);  | 
        ||
| 308 |                 else | 
        ||
| 309 | gr_string(canvas, game_font, 0x8000, y, "is serving you well.");  | 
        ||
| 310 | y += line_spacing;  | 
        ||
| 311 | gr_printf(canvas, game_font, 0x8000, y, "your rank is: %s", RankStrings[GetMyNetRanking()]);  | 
        ||
| 312 |         } | 
        ||
| 313 | } | 
        ||
| 314 | } | 
        ||
| 315 | |||
| 316 | #ifndef NDEBUG | 
        ||
| 317 | |||
| 318 | fix Show_view_text_timer = -1;  | 
        ||
| 319 | |||
| 320 | static void draw_window_label(object_array &Objects, grs_canvas &canvas)  | 
        ||
| 321 | { | 
        ||
| 322 | auto &vcobjptridx = Objects.vcptridx;  | 
        ||
| 323 | if ( Show_view_text_timer > 0 )  | 
        ||
| 324 |         { | 
        ||
| 325 | const char *viewer_name,*control_name,*viewer_id;  | 
        ||
| 326 | Show_view_text_timer -= FrameTime;  | 
        ||
| 327 | |||
| 328 | viewer_id = "";  | 
        ||
| 329 | switch( Viewer->type )  | 
        ||
| 330 |                 { | 
        ||
| 331 | case OBJ_FIREBALL: viewer_name = "Fireball"; break;  | 
        ||
| 332 | case OBJ_ROBOT: viewer_name = "Robot";  | 
        ||
| 333 | #if DXX_USE_EDITOR | 
        ||
| 334 | viewer_id = Robot_names[get_robot_id(Objects.vcptr(Viewer))].data();  | 
        ||
| 335 | #endif | 
        ||
| 336 | break;  | 
        ||
| 337 | case OBJ_HOSTAGE: viewer_name = "Hostage"; break;  | 
        ||
| 338 | case OBJ_PLAYER: viewer_name = "Player"; break;  | 
        ||
| 339 | case OBJ_WEAPON: viewer_name = "Weapon"; break;  | 
        ||
| 340 | case OBJ_CAMERA: viewer_name = "Camera"; break;  | 
        ||
| 341 | case OBJ_POWERUP: viewer_name = "Powerup";  | 
        ||
| 342 | #if DXX_USE_EDITOR | 
        ||
| 343 | viewer_id = Powerup_names[get_powerup_id(Objects.vcptr(Viewer))].data();  | 
        ||
| 344 | #endif | 
        ||
| 345 | break;  | 
        ||
| 346 | case OBJ_DEBRIS: viewer_name = "Debris"; break;  | 
        ||
| 347 | case OBJ_CNTRLCEN: viewer_name = "Reactor"; break;  | 
        ||
| 348 | default: viewer_name = "Unknown"; break;  | 
        ||
| 349 |                 } | 
        ||
| 350 | |||
| 351 | switch ( Viewer->control_type) {  | 
        ||
| 352 | case CT_NONE: control_name = "Stopped"; break;  | 
        ||
| 353 | case CT_AI: control_name = "AI"; break;  | 
        ||
| 354 | case CT_FLYING: control_name = "Flying"; break;  | 
        ||
| 355 | case CT_SLEW: control_name = "Slew"; break;  | 
        ||
| 356 | case CT_FLYTHROUGH: control_name = "Flythrough"; break;  | 
        ||
| 357 | case CT_MORPH: control_name = "Morphing"; break;  | 
        ||
| 358 | default: control_name = "Unknown"; break;  | 
        ||
| 359 |                 } | 
        ||
| 360 | |||
| 361 | gr_set_fontcolor(canvas, BM_XRGB(31, 0, 0),-1);  | 
        ||
| 362 | auto &game_font = *GAME_FONT;  | 
        ||
| 363 | gr_printf(canvas, game_font, 0x8000, (SHEIGHT / 10), "%hu: %s [%s] View - %s", static_cast<objnum_t>(vcobjptridx(Viewer)), viewer_name, viewer_id, control_name);  | 
        ||
| 364 | |||
| 365 |         } | 
        ||
| 366 | } | 
        ||
| 367 | #endif | 
        ||
| 368 | |||
| 369 | namespace dsx {  | 
        ||
| 370 | static void render_countdown_gauge(grs_canvas &canvas)  | 
        ||
| 371 | { | 
        ||
| 372 | auto &LevelUniqueControlCenterState = LevelUniqueObjectState.ControlCenterState;  | 
        ||
| 373 | int Countdown_seconds_left;  | 
        ||
| 374 | if (!Endlevel_sequence && LevelUniqueControlCenterState.Control_center_destroyed && (Countdown_seconds_left = LevelUniqueControlCenterState.Countdown_seconds_left) > -1)  | 
        ||
| 375 | { // && (Countdown_seconds_left<127))  | 
        ||
| 376 | #if defined(DXX_BUILD_DESCENT_II) | 
        ||
| 377 | if (!is_D2_OEM && !is_MAC_SHARE && !is_SHAREWARE) // no countdown on registered only  | 
        ||
| 378 |                 { | 
        ||
| 379 |                         //      On last level, we don't want a countdown. | 
        ||
| 380 | if (PLAYING_BUILTIN_MISSION && Current_level_num == Last_level)  | 
        ||
| 381 |                         { | 
        ||
| 382 | if (!(Game_mode & GM_MULTI))  | 
        ||
| 383 | return;  | 
        ||
| 384 | if (Game_mode & GM_MULTI_ROBOTS)  | 
        ||
| 385 | return;  | 
        ||
| 386 |                         } | 
        ||
| 387 |                 } | 
        ||
| 388 | #endif | 
        ||
| 389 | gr_set_fontcolor(canvas, BM_XRGB(0, 63, 0),-1);  | 
        ||
| 390 | auto &game_font = *GAME_FONT;  | 
        ||
| 391 | gr_printf(canvas, game_font, 0x8000, (LINE_SPACING(game_font, game_font) * 6) + FSPACY(1), "T-%d s", Countdown_seconds_left);  | 
        ||
| 392 |         } | 
        ||
| 393 | } | 
        ||
| 394 | } | 
        ||
| 395 | |||
| 396 | static void game_draw_hud_stuff(grs_canvas &canvas)  | 
        ||
| 397 | { | 
        ||
| 398 | auto &Objects = LevelUniqueObjectState.Objects;  | 
        ||
| 399 | auto &vmobjptr = Objects.vmptr;  | 
        ||
| 400 | #ifndef NDEBUG | 
        ||
| 401 | draw_window_label(Objects, canvas);  | 
        ||
| 402 | #endif | 
        ||
| 403 | |||
| 404 | game_draw_multi_message(canvas);  | 
        ||
| 405 | |||
| 406 | game_draw_marker_message(canvas);  | 
        ||
| 407 | |||
| 408 | if (((Newdemo_state == ND_STATE_PLAYBACK) || (Newdemo_state == ND_STATE_RECORDING)) && (PlayerCfg.CockpitMode[1] != CM_REAR_VIEW)) {  | 
        ||
| 409 | int y;  | 
        ||
| 410 | |||
| 411 | auto &game_font = *GAME_FONT;  | 
        ||
| 412 | gr_set_curfont(canvas, GAME_FONT);  | 
        ||
| 413 | gr_set_fontcolor(canvas, BM_XRGB(27, 0, 0), -1);  | 
        ||
| 414 | |||
| 415 | y = canvas.cv_bitmap.bm_h - (LINE_SPACING(*canvas.cv_font, *GAME_FONT) * 2);  | 
        ||
| 416 | |||
| 417 | if (PlayerCfg.CockpitMode[1] == CM_FULL_COCKPIT)  | 
        ||
| 418 | y = canvas.cv_bitmap.bm_h / 1.2 ;  | 
        ||
| 419 | if (Newdemo_state == ND_STATE_PLAYBACK) {  | 
        ||
| 420 | if (Newdemo_show_percentage) {  | 
        ||
| 421 | gr_printf(canvas, game_font, 0x8000, y, "%s (%d%% %s)", TXT_DEMO_PLAYBACK, newdemo_get_percent_done(), TXT_DONE);  | 
        ||
| 422 |                         } | 
        ||
| 423 | } else {  | 
        ||
| 424 | gr_printf(canvas, game_font, 0x8000, y, "%s (%dK)", TXT_DEMO_RECORDING, (Newdemo_num_written / 1024));  | 
        ||
| 425 |                 } | 
        ||
| 426 |         } | 
        ||
| 427 | |||
| 428 | render_countdown_gauge(canvas);  | 
        ||
| 429 | |||
| 430 | if (CGameCfg.FPSIndicator && PlayerCfg.CockpitMode[1] != CM_REAR_VIEW)  | 
        ||
| 431 | show_framerate(canvas);  | 
        ||
| 432 | |||
| 433 | if (Newdemo_state == ND_STATE_PLAYBACK)  | 
        ||
| 434 | Game_mode = Newdemo_game_mode;  | 
        ||
| 435 | |||
| 436 | auto &plrobj = get_local_plrobj();  | 
        ||
| 437 | draw_hud(canvas, plrobj);  | 
        ||
| 438 | |||
| 439 | if (Newdemo_state == ND_STATE_PLAYBACK)  | 
        ||
| 440 | Game_mode = GM_NORMAL;  | 
        ||
| 441 | |||
| 442 | if (Player_dead_state != player_dead_state::no)  | 
        ||
| 443 | player_dead_message(canvas);  | 
        ||
| 444 | } | 
        ||
| 445 | |||
| 446 | namespace dsx {  | 
        ||
| 447 | |||
| 448 | #if defined(DXX_BUILD_DESCENT_II) | 
        ||
| 449 | |||
| 450 | ubyte RenderingType=0;  | 
        ||
| 451 | ubyte DemoDoingRight=0,DemoDoingLeft=0;  | 
        ||
| 452 | |||
| 453 | constexpr char DemoWBUType[]={0,WBU_GUIDED,WBU_MISSILE,WBU_REAR,WBU_ESCORT,WBU_MARKER,0};  | 
        ||
| 454 | constexpr char DemoRearCheck[]={0,0,0,1,0,0,0};  | 
        ||
| 455 | constexpr char DemoExtraMessage[][10] = {  | 
        ||
| 456 |         "PLAYER", | 
        ||
| 457 |         "GUIDED", | 
        ||
| 458 |         "MISSILE", | 
        ||
| 459 |         "REAR", | 
        ||
| 460 |         "GUIDE-BOT", | 
        ||
| 461 |         "MARKER", | 
        ||
| 462 |         "SHIP" | 
        ||
| 463 | };  | 
        ||
| 464 | |||
| 465 | static const char *get_missile_name(const unsigned laser_type)  | 
        ||
| 466 | { | 
        ||
| 467 | switch(laser_type)  | 
        ||
| 468 |         { | 
        ||
| 469 | case weapon_id_type::CONCUSSION_ID:  | 
        ||
| 470 | return "CONCUSSION";  | 
        ||
| 471 | case weapon_id_type::HOMING_ID:  | 
        ||
| 472 | return "HOMING";  | 
        ||
| 473 | case weapon_id_type::SMART_ID:  | 
        ||
| 474 | return "SMART";  | 
        ||
| 475 | case weapon_id_type::MEGA_ID:  | 
        ||
| 476 | return "MEGA";  | 
        ||
| 477 | case weapon_id_type::FLASH_ID:  | 
        ||
| 478 | return "FLASH";  | 
        ||
| 479 | case weapon_id_type::MERCURY_ID:  | 
        ||
| 480 | return "MERCURY";  | 
        ||
| 481 | case weapon_id_type::EARTHSHAKER_ID:  | 
        ||
| 482 | return "SHAKER";  | 
        ||
| 483 | default:  | 
        ||
| 484 | return "MISSILE"; // Bad!  | 
        ||
| 485 |         } | 
        ||
| 486 | } | 
        ||
| 487 | |||
| 488 | static void set_missile_viewer(object &o)  | 
        ||
| 489 | { | 
        ||
| 490 | Missile_viewer = &o;  | 
        ||
| 491 | Missile_viewer_sig = o.signature;  | 
        ||
| 492 | } | 
        ||
| 493 | |||
| 494 | static int clear_missile_viewer()  | 
        ||
| 495 | { | 
        ||
| 496 | if (!Missile_viewer)  | 
        ||
| 497 | return 0;  | 
        ||
| 498 | Missile_viewer = nullptr;  | 
        ||
| 499 | return 1;  | 
        ||
| 500 | } | 
        ||
| 501 | |||
| 502 | __attribute_warn_unused_result  | 
        ||
| 503 | static bool is_viewable_missile(weapon_id_type laser_type)  | 
        ||
| 504 | { | 
        ||
| 505 | return laser_type == weapon_id_type::CONCUSSION_ID ||  | 
        ||
| 506 | laser_type == weapon_id_type::HOMING_ID ||  | 
        ||
| 507 | laser_type == weapon_id_type::SMART_ID ||  | 
        ||
| 508 | laser_type == weapon_id_type::MEGA_ID ||  | 
        ||
| 509 | laser_type == weapon_id_type::FLASH_ID ||  | 
        ||
| 510 | laser_type == weapon_id_type::GUIDEDMISS_ID ||  | 
        ||
| 511 | laser_type == weapon_id_type::MERCURY_ID ||  | 
        ||
| 512 | laser_type == weapon_id_type::EARTHSHAKER_ID;  | 
        ||
| 513 | } | 
        ||
| 514 | |||
| 515 | static bool choose_missile_viewer()  | 
        ||
| 516 | { | 
        ||
| 517 | auto &Objects = LevelUniqueObjectState.Objects;  | 
        ||
| 518 | auto &vcobjptr = Objects.vcptr;  | 
        ||
| 519 | auto &vmobjptr = Objects.vmptr;  | 
        ||
| 520 | const auto MissileViewEnabled = PlayerCfg.MissileViewEnabled;  | 
        ||
| 521 | if (unlikely(MissileViewEnabled == MissileViewMode::None))  | 
        ||
| 522 | return false;  | 
        ||
| 523 | const auto need_new_missile_viewer = []{  | 
        ||
| 524 | if (!Missile_viewer)  | 
        ||
| 525 | return true;  | 
        ||
| 526 | if (Missile_viewer->type != OBJ_WEAPON)  | 
        ||
| 527 | return true;  | 
        ||
| 528 | if (Missile_viewer->signature != Missile_viewer_sig)  | 
        ||
| 529 | return true;  | 
        ||
| 530 |                 /* No check on parent here.  Missile_viewer is cleared if needed | 
        ||
| 531 |                  * when a missile is fired. | 
        ||
| 532 |                  */ | 
        ||
| 533 | return false;  | 
        ||
| 534 | };  | 
        ||
| 535 | if (likely(!need_new_missile_viewer()))  | 
        ||
| 536 |                 /* Valid viewer already set */ | 
        ||
| 537 | return true;  | 
        ||
| 538 | const auto better_match = [](object *const a, object &b) {  | 
        ||
| 539 | if (!a)  | 
        ||
| 540 | return true;  | 
        ||
| 541 | return a->lifeleft < b.lifeleft;  | 
        ||
| 542 | };  | 
        ||
| 543 |         /* Find new missile */ | 
        ||
| 544 | object *local_player_missile = nullptr, *other_player_missile = nullptr;  | 
        ||
| 545 | const auto game_mode = Game_mode;  | 
        ||
| 546 | const auto local_player_objnum = get_local_player().objnum;  | 
        ||
| 547 | range_for (object &o, vmobjptr)  | 
        ||
| 548 |         { | 
        ||
| 549 | if (o.type != OBJ_WEAPON)  | 
        ||
| 550 | continue;  | 
        ||
| 551 | if (o.ctype.laser_info.parent_type != OBJ_PLAYER)  | 
        ||
| 552 | continue;  | 
        ||
| 553 | const auto laser_type = get_weapon_id(o);  | 
        ||
| 554 | if (!is_viewable_missile(laser_type))  | 
        ||
| 555 | continue;  | 
        ||
| 556 | if (o.ctype.laser_info.parent_num == local_player_objnum)  | 
        ||
| 557 |                 { | 
        ||
| 558 | if (!better_match(local_player_missile, o))  | 
        ||
| 559 | continue;  | 
        ||
| 560 | local_player_missile = &o;  | 
        ||
| 561 |                 } | 
        ||
| 562 |                 else | 
        ||
| 563 |                 { | 
        ||
| 564 | if (MissileViewEnabled != MissileViewMode::EnabledSelfAndAllies)  | 
        ||
| 565 | continue;  | 
        ||
| 566 | if (!better_match(other_player_missile, o))  | 
        ||
| 567 | continue;  | 
        ||
| 568 | else if (game_mode & GM_MULTI_COOP)  | 
        ||
| 569 |                         { | 
        ||
| 570 |                                 /* Always allow missiles of other players */ | 
        ||
| 571 |                         } | 
        ||
| 572 | else if (game_mode & GM_TEAM)  | 
        ||
| 573 |                         { | 
        ||
| 574 |                                 /* Allow missiles from same team */ | 
        ||
| 575 | if (get_team(Player_num) != get_team(get_player_id(vcobjptr(o.ctype.laser_info.parent_num))))  | 
        ||
| 576 | continue;  | 
        ||
| 577 |                         } | 
        ||
| 578 |                         else | 
        ||
| 579 | continue;  | 
        ||
| 580 | other_player_missile = &o;  | 
        ||
| 581 |                 } | 
        ||
| 582 |         } | 
        ||
| 583 | object *o;  | 
        ||
| 584 | if ((o = local_player_missile) != nullptr ||  | 
        ||
| 585 | (o = other_player_missile) != nullptr)  | 
        ||
| 586 | set_missile_viewer(*o);  | 
        ||
| 587 |         else | 
        ||
| 588 | return false;  | 
        ||
| 589 | return true;  | 
        ||
| 590 | } | 
        ||
| 591 | |||
| 592 | static void show_one_extra_view(const int w);  | 
        ||
| 593 | static void show_extra_views()  | 
        ||
| 594 | { | 
        ||
| 595 | auto &Objects = LevelUniqueObjectState.Objects;  | 
        ||
| 596 | auto &vmobjptr = Objects.vmptr;  | 
        ||
| 597 | int did_missile_view=0;  | 
        ||
| 598 | int save_newdemo_state = Newdemo_state;  | 
        ||
| 599 | if (Newdemo_state==ND_STATE_PLAYBACK)  | 
        ||
| 600 |         { | 
        ||
| 601 | if (DemoDoLeft)  | 
        ||
| 602 |                 { | 
        ||
| 603 | DemoDoingLeft=DemoDoLeft;  | 
        ||
| 604 | |||
| 605 | if (DemoDoLeft==3)  | 
        ||
| 606 | do_cockpit_window_view(0, *ConsoleObject, 1, WBU_REAR, "REAR");  | 
        ||
| 607 |                         else | 
        ||
| 608 | do_cockpit_window_view(0, DemoLeftExtra, DemoRearCheck[DemoDoLeft], DemoWBUType[DemoDoLeft], DemoExtraMessage[DemoDoLeft], DemoDoLeft == 1 ? &get_local_plrobj().ctype.player_info : nullptr);  | 
        ||
| 609 |                 } | 
        ||
| 610 |                 else | 
        ||
| 611 | do_cockpit_window_view(0,WBU_WEAPON);  | 
        ||
| 612 | |||
| 613 | if (DemoDoRight)  | 
        ||
| 614 |                 { | 
        ||
| 615 | DemoDoingRight=DemoDoRight;  | 
        ||
| 616 | |||
| 617 | if (DemoDoRight==3)  | 
        ||
| 618 | do_cockpit_window_view(1, *ConsoleObject, 1, WBU_REAR, "REAR");  | 
        ||
| 619 |                         else | 
        ||
| 620 |                         { | 
        ||
| 621 | do_cockpit_window_view(1, DemoRightExtra, DemoRearCheck[DemoDoRight], DemoWBUType[DemoDoRight], DemoExtraMessage[DemoDoRight], DemoDoLeft == 1 ? &get_local_plrobj().ctype.player_info : nullptr);  | 
        ||
| 622 |                         } | 
        ||
| 623 |                 } | 
        ||
| 624 |                 else | 
        ||
| 625 | do_cockpit_window_view(1,WBU_WEAPON);  | 
        ||
| 626 | |||
| 627 | DemoDoLeft=DemoDoRight=0;  | 
        ||
| 628 | DemoDoingLeft=DemoDoingRight=0;  | 
        ||
| 629 | return;  | 
        ||
| 630 |         } | 
        ||
| 631 | |||
| 632 | const auto &&gimobj = LevelUniqueObjectState.Guided_missile.get_player_active_guided_missile(vmobjptr, Player_num);  | 
        ||
| 633 | if (gimobj != nullptr)  | 
        ||
| 634 |         { | 
        ||
| 635 | if (PlayerCfg.GuidedInBigWindow)  | 
        ||
| 636 |                 { | 
        ||
| 637 | RenderingType=6+(1<<4);  | 
        ||
| 638 | do_cockpit_window_view(1, *Viewer, 0, WBU_MISSILE, "SHIP");  | 
        ||
| 639 |                 } | 
        ||
| 640 |                 else | 
        ||
| 641 |                 { | 
        ||
| 642 | RenderingType=1+(1<<4);  | 
        ||
| 643 | auto &player_info = get_local_plrobj().ctype.player_info;  | 
        ||
| 644 | do_cockpit_window_view(1, *gimobj, 0, WBU_GUIDED, "GUIDED", &player_info);  | 
        ||
| 645 |                 } | 
        ||
| 646 | |||
| 647 | did_missile_view=1;  | 
        ||
| 648 |         } | 
        ||
| 649 | else {  | 
        ||
| 650 | if (choose_missile_viewer())  | 
        ||
| 651 |                 //do missile view | 
        ||
| 652 |                         { | 
        ||
| 653 | RenderingType=2+(1<<4);  | 
        ||
| 654 | do_cockpit_window_view(1, *Missile_viewer, 0, WBU_MISSILE, get_missile_name(get_weapon_id(*Missile_viewer)));  | 
        ||
| 655 | did_missile_view=1;  | 
        ||
| 656 |                         } | 
        ||
| 657 | else {  | 
        ||
| 658 | if (clear_missile_viewer())  | 
        ||
| 659 | do_cockpit_window_view(1,WBU_STATIC);  | 
        ||
| 660 | RenderingType=255;  | 
        ||
| 661 |                         } | 
        ||
| 662 |         } | 
        ||
| 663 | |||
| 664 | range_for (const int w, xrange(2u)) {  | 
        ||
| 665 | |||
| 666 | if (w==1 && did_missile_view)  | 
        ||
| 667 | continue; //if showing missile view in right window, can't show anything else  | 
        ||
| 668 | |||
| 669 | show_one_extra_view(w);  | 
        ||
| 670 |         } | 
        ||
| 671 | RenderingType=0;  | 
        ||
| 672 | Newdemo_state = save_newdemo_state;  | 
        ||
| 673 | } | 
        ||
| 674 | |||
| 675 | static void show_one_extra_view(const int w)  | 
        ||
| 676 | { | 
        ||
| 677 | auto &Objects = LevelUniqueObjectState.Objects;  | 
        ||
| 678 | auto &vcobjptr = Objects.vcptr;  | 
        ||
| 679 | auto &vmobjptridx = Objects.vmptridx;  | 
        ||
| 680 | auto &Robot_info = LevelSharedRobotInfoState.Robot_info;  | 
        ||
| 681 |                 //show special views if selected | 
        ||
| 682 | switch (PlayerCfg.Cockpit3DView[w]) {  | 
        ||
| 683 | case CV_NONE:  | 
        ||
| 684 | RenderingType=255;  | 
        ||
| 685 | do_cockpit_window_view(w,WBU_WEAPON);  | 
        ||
| 686 | break;  | 
        ||
| 687 | case CV_REAR:  | 
        ||
| 688 | RenderingType=3 + (w << 4);  | 
        ||
| 689 |                                 { | 
        ||
| 690 | int rear_view_flag;  | 
        ||
| 691 | const char *label;  | 
        ||
| 692 | if (Rear_view) //if big window is rear view, show front here  | 
        ||
| 693 |                                         { | 
        ||
| 694 | rear_view_flag = 0;  | 
        ||
| 695 | label = "FRONT";  | 
        ||
| 696 |                                         } | 
        ||
| 697 | else //show normal rear view  | 
        ||
| 698 |                                         { | 
        ||
| 699 | rear_view_flag = 1;  | 
        ||
| 700 | label = "REAR";  | 
        ||
| 701 |                                         } | 
        ||
| 702 | do_cockpit_window_view(w, *ConsoleObject, rear_view_flag, WBU_REAR, label);  | 
        ||
| 703 |                                 } | 
        ||
| 704 | break;  | 
        ||
| 705 | case CV_ESCORT: {  | 
        ||
| 706 | const auto &&buddy = find_escort(vmobjptridx, Robot_info);  | 
        ||
| 707 | if (buddy == object_none) {  | 
        ||
| 708 | do_cockpit_window_view(w,WBU_WEAPON);  | 
        ||
| 709 | PlayerCfg.Cockpit3DView[w] = CV_NONE;  | 
        ||
| 710 |                                 } | 
        ||
| 711 | else {  | 
        ||
| 712 | RenderingType=4+(w<<4);  | 
        ||
| 713 | do_cockpit_window_view(w, *buddy, 0, WBU_ESCORT, PlayerCfg.GuidebotName);  | 
        ||
| 714 |                                 } | 
        ||
| 715 | break;  | 
        ||
| 716 |                         } | 
        ||
| 717 | case CV_COOP: {  | 
        ||
| 718 | const auto player = Coop_view_player[w];  | 
        ||
| 719 | |||
| 720 | RenderingType=255; // don't handle coop stuff  | 
        ||
| 721 | |||
| 722 | if (player < Players.size() && vcplayerptr(player)->connected && ((Game_mode & GM_MULTI_COOP) || ((Game_mode & GM_TEAM) && (get_team(player) == get_team(Player_num)))))  | 
        ||
| 723 |                                 { | 
        ||
| 724 | auto &p = *vcplayerptr(player);  | 
        ||
| 725 | do_cockpit_window_view(w, *vcobjptr(p.objnum), 0, WBU_COOP, p.callsign);  | 
        ||
| 726 |                                 } | 
        ||
| 727 | else {  | 
        ||
| 728 | do_cockpit_window_view(w,WBU_WEAPON);  | 
        ||
| 729 | PlayerCfg.Cockpit3DView[w] = CV_NONE;  | 
        ||
| 730 |                                 } | 
        ||
| 731 | break;  | 
        ||
| 732 |                         } | 
        ||
| 733 | case CV_MARKER: {  | 
        ||
| 734 | char label[10];  | 
        ||
| 735 | RenderingType=5+(w<<4);  | 
        ||
| 736 | const auto mvn = Marker_viewer_num[w];  | 
        ||
| 737 | if (!MarkerState.imobjidx.valid_index(mvn))  | 
        ||
| 738 |                                 { | 
        ||
| 739 | PlayerCfg.Cockpit3DView[w] = CV_NONE;  | 
        ||
| 740 | break;  | 
        ||
| 741 |                                 } | 
        ||
| 742 | const auto mo = MarkerState.imobjidx[mvn];  | 
        ||
| 743 | if (mo == object_none)  | 
        ||
| 744 |                                 { | 
        ||
| 745 | PlayerCfg.Cockpit3DView[w] = CV_NONE;  | 
        ||
| 746 | break;  | 
        ||
| 747 |                                 } | 
        ||
| 748 | const auto displayed_marker_id = static_cast<unsigned>(mvn) + 1;  | 
        ||
| 749 | snprintf(label, sizeof(label), "Marker %u", displayed_marker_id);  | 
        ||
| 750 | do_cockpit_window_view(w, *vcobjptr(mo), 0, WBU_MARKER, label);  | 
        ||
| 751 | break;  | 
        ||
| 752 |                         } | 
        ||
| 753 | default:  | 
        ||
| 754 | Int3(); //invalid window type  | 
        ||
| 755 |                 } | 
        ||
| 756 | } | 
        ||
| 757 | |||
| 758 | int BigWindowSwitch=0;  | 
        ||
| 759 | #endif | 
        ||
| 760 | |||
| 761 | static void update_cockpits();  | 
        ||
| 762 | |||
| 763 | //render a frame for the game | 
        ||
| 764 | void game_render_frame_mono()  | 
        ||
| 765 | { | 
        ||
| 766 | int no_draw_hud=0;  | 
        ||
| 767 | |||
| 768 | gr_set_current_canvas(Screen_3d_window);  | 
        ||
| 769 | #if defined(DXX_BUILD_DESCENT_II) | 
        ||
| 770 | auto &Objects = LevelUniqueObjectState.Objects;  | 
        ||
| 771 | auto &vmobjptr = Objects.vmptr;  | 
        ||
| 772 | if (const auto &&gimobj = (  | 
        ||
| 773 |                         PlayerCfg.GuidedInBigWindow | 
        ||
| 774 | ? LevelUniqueObjectState.Guided_missile.get_player_active_guided_missile(LevelUniqueObjectState.get_objects().vmptr, Player_num)  | 
        ||
| 775 | : nullptr))  | 
        ||
| 776 |         { | 
        ||
| 777 | const auto viewer_save = Viewer;  | 
        ||
| 778 | |||
| 779 | if (PlayerCfg.CockpitMode[1]==CM_FULL_COCKPIT || PlayerCfg.CockpitMode[1]==CM_REAR_VIEW)  | 
        ||
| 780 |                 { | 
        ||
| 781 | BigWindowSwitch=1;  | 
        ||
| 782 | force_cockpit_redraw=1;  | 
        ||
| 783 | PlayerCfg.CockpitMode[1]=CM_STATUS_BAR;  | 
        ||
| 784 | return;  | 
        ||
| 785 |                 } | 
        ||
| 786 | |||
| 787 | Viewer = gimobj;  | 
        ||
| 788 | |||
| 789 |                 window_rendered_data window; | 
        ||
| 790 | update_rendered_data(window, *Viewer, 0);  | 
        ||
| 791 | render_frame(*grd_curcanv, 0, window);  | 
        ||
| 792 | |||
| 793 | wake_up_rendered_objects(*Viewer, window);  | 
        ||
| 794 | show_HUD_names(*grd_curcanv);  | 
        ||
| 795 | |||
| 796 | Viewer = viewer_save;  | 
        ||
| 797 | |||
| 798 | auto &game_font = *GAME_FONT;  | 
        ||
| 799 | gr_set_fontcolor(*grd_curcanv, BM_XRGB(27, 0, 0), -1);  | 
        ||
| 800 | |||
| 801 | gr_string(*grd_curcanv, game_font, 0x8000, FSPACY(1), "Guided Missile View");  | 
        ||
| 802 | |||
| 803 | auto &player_info = get_local_plrobj().ctype.player_info;  | 
        ||
| 804 | show_reticle(*grd_curcanv, player_info, RET_TYPE_CROSS_V1, 0);  | 
        ||
| 805 | |||
| 806 | HUD_render_message_frame(*grd_curcanv);  | 
        ||
| 807 | |||
| 808 | no_draw_hud=1;  | 
        ||
| 809 |         } | 
        ||
| 810 |         else | 
        ||
| 811 | #endif | 
        ||
| 812 |         { | 
        ||
| 813 | #if defined(DXX_BUILD_DESCENT_II) | 
        ||
| 814 | if (BigWindowSwitch)  | 
        ||
| 815 |                 { | 
        ||
| 816 | force_cockpit_redraw=1;  | 
        ||
| 817 | PlayerCfg.CockpitMode[1]=(Rear_view?CM_REAR_VIEW:CM_FULL_COCKPIT);  | 
        ||
| 818 | BigWindowSwitch=0;  | 
        ||
| 819 | return;  | 
        ||
| 820 |                 } | 
        ||
| 821 | #endif | 
        ||
| 822 |                 window_rendered_data window; | 
        ||
| 823 | #if defined(DXX_BUILD_DESCENT_II) | 
        ||
| 824 | update_rendered_data(window, *Viewer, Rear_view);  | 
        ||
| 825 | #endif | 
        ||
| 826 | render_frame(*grd_curcanv, 0, window);  | 
        ||
| 827 |         } | 
        ||
| 828 | |||
| 829 | #if defined(DXX_BUILD_DESCENT_II) | 
        ||
| 830 | gr_set_current_canvas(Screen_3d_window);  | 
        ||
| 831 | #endif | 
        ||
| 832 | |||
| 833 | update_cockpits();  | 
        ||
| 834 | |||
| 835 | if (Newdemo_state == ND_STATE_PLAYBACK)  | 
        ||
| 836 | Game_mode = Newdemo_game_mode;  | 
        ||
| 837 | |||
| 838 | if (PlayerCfg.CockpitMode[1]==CM_FULL_COCKPIT || PlayerCfg.CockpitMode[1]==CM_STATUS_BAR)  | 
        ||
| 839 | render_gauges();  | 
        ||
| 840 | |||
| 841 | if (Newdemo_state == ND_STATE_PLAYBACK)  | 
        ||
| 842 | Game_mode = GM_NORMAL;  | 
        ||
| 843 | |||
| 844 | gr_set_current_canvas(Screen_3d_window);  | 
        ||
| 845 | if (!no_draw_hud)  | 
        ||
| 846 | game_draw_hud_stuff(*grd_curcanv);  | 
        ||
| 847 | |||
| 848 | #if defined(DXX_BUILD_DESCENT_II) | 
        ||
| 849 | gr_set_default_canvas();  | 
        ||
| 850 | |||
| 851 | show_extra_views(); //missile view, buddy bot, etc.  | 
        ||
| 852 | #endif | 
        ||
| 853 | |||
| 854 | if (netplayerinfo_on && Game_mode & GM_MULTI)  | 
        ||
| 855 | show_netplayerinfo();  | 
        ||
| 856 | } | 
        ||
| 857 | |||
| 858 | } | 
        ||
| 859 | |||
| 860 | void toggle_cockpit()  | 
        ||
| 861 | { | 
        ||
| 862 | enum cockpit_mode_t new_mode=CM_FULL_SCREEN;  | 
        ||
| 863 | |||
| 864 | if (Rear_view || Player_dead_state != player_dead_state::no)  | 
        ||
| 865 | return;  | 
        ||
| 866 | |||
| 867 | switch (PlayerCfg.CockpitMode[1])  | 
        ||
| 868 |         { | 
        ||
| 869 | case CM_FULL_COCKPIT:  | 
        ||
| 870 | new_mode = CM_STATUS_BAR;  | 
        ||
| 871 | break;  | 
        ||
| 872 | case CM_STATUS_BAR:  | 
        ||
| 873 | new_mode = CM_FULL_SCREEN;  | 
        ||
| 874 | break;  | 
        ||
| 875 | case CM_FULL_SCREEN:  | 
        ||
| 876 | new_mode = CM_FULL_COCKPIT;  | 
        ||
| 877 | break;  | 
        ||
| 878 | case CM_REAR_VIEW:  | 
        ||
| 879 | case CM_LETTERBOX:  | 
        ||
| 880 | break;  | 
        ||
| 881 |         } | 
        ||
| 882 | |||
| 883 | select_cockpit(new_mode);  | 
        ||
| 884 | HUD_clear_messages();  | 
        ||
| 885 | PlayerCfg.CockpitMode[0] = new_mode;  | 
        ||
| 886 | write_player_file();  | 
        ||
| 887 | } | 
        ||
| 888 | |||
| 889 | namespace dcx {  | 
        ||
| 890 | int last_drawn_cockpit = -1;  | 
        ||
| 891 | } | 
        ||
| 892 | |||
| 893 | namespace dsx {  | 
        ||
| 894 | |||
| 895 | // This actually renders the new cockpit onto the screen. | 
        ||
| 896 | static void update_cockpits()  | 
        ||
| 897 | { | 
        ||
| 898 | grs_bitmap *bm;  | 
        ||
| 899 | int mode = PlayerCfg.CockpitMode[1];  | 
        ||
| 900 | #if defined(DXX_BUILD_DESCENT_II) | 
        ||
| 901 | mode += (HIRESMODE?(Num_cockpits/2):0);  | 
        ||
| 902 | #endif | 
        ||
| 903 | |||
| 904 | switch( PlayerCfg.CockpitMode[1] ) {  | 
        ||
| 905 | case CM_FULL_COCKPIT:  | 
        ||
| 906 | case CM_REAR_VIEW:  | 
        ||
| 907 | PIGGY_PAGE_IN(cockpit_bitmap[mode]);  | 
        ||
| 908 | bm=&GameBitmaps[cockpit_bitmap[mode].index];  | 
        ||
| 909 | gr_set_default_canvas();  | 
        ||
| 910 | #if DXX_USE_OGL | 
        ||
| 911 | ogl_ubitmapm_cs(*grd_curcanv, 0, 0, -1, -1, *bm, 255, F1_0);  | 
        ||
| 912 | #else | 
        ||
| 913 | gr_ubitmapm(*grd_curcanv, 0, 0, *bm);  | 
        ||
| 914 | #endif | 
        ||
| 915 | break;  | 
        ||
| 916 | |||
| 917 | case CM_FULL_SCREEN:  | 
        ||
| 918 | break;  | 
        ||
| 919 | |||
| 920 | case CM_STATUS_BAR:  | 
        ||
| 921 | PIGGY_PAGE_IN(cockpit_bitmap[mode]);  | 
        ||
| 922 | bm=&GameBitmaps[cockpit_bitmap[mode].index];  | 
        ||
| 923 | gr_set_default_canvas();  | 
        ||
| 924 | #if DXX_USE_OGL | 
        ||
| 925 | ogl_ubitmapm_cs(*grd_curcanv, 0, (HIRESMODE ? (SHEIGHT * 2) / 2.6 : (SHEIGHT * 2) / 2.72), -1, (static_cast<int>(static_cast<double>(bm->bm_h) * (HIRESMODE ? static_cast<double>(SHEIGHT) / 480 : static_cast<double>(SHEIGHT) / 200) + 0.5)), *bm, 255, F1_0);  | 
        ||
| 926 | #else | 
        ||
| 927 | gr_ubitmapm(*grd_curcanv, 0, SHEIGHT - bm->bm_h, *bm);  | 
        ||
| 928 | #endif | 
        ||
| 929 | break;  | 
        ||
| 930 | |||
| 931 | case CM_LETTERBOX:  | 
        ||
| 932 | break;  | 
        ||
| 933 |         } | 
        ||
| 934 | gr_set_default_canvas();  | 
        ||
| 935 | |||
| 936 | if (PlayerCfg.CockpitMode[1] != last_drawn_cockpit)  | 
        ||
| 937 | last_drawn_cockpit = PlayerCfg.CockpitMode[1];  | 
        ||
| 938 |         else | 
        ||
| 939 | return;  | 
        ||
| 940 | |||
| 941 | if (PlayerCfg.CockpitMode[1]==CM_FULL_COCKPIT || PlayerCfg.CockpitMode[1]==CM_STATUS_BAR)  | 
        ||
| 942 | init_gauges();  | 
        ||
| 943 | |||
| 944 | } | 
        ||
| 945 | |||
| 946 | } | 
        ||
| 947 | |||
| 948 | void game_render_frame()  | 
        ||
| 949 | { | 
        ||
| 950 | auto &Objects = LevelUniqueObjectState.Objects;  | 
        ||
| 951 | auto &vmobjptr = Objects.vmptr;  | 
        ||
| 952 | set_screen_mode( SCREEN_GAME );  | 
        ||
| 953 | auto &player_info = get_local_plrobj().ctype.player_info;  | 
        ||
| 954 | play_homing_warning(player_info);  | 
        ||
| 955 | game_render_frame_mono();  | 
        ||
| 956 | } | 
        ||
| 957 | |||
| 958 | //show a message in a nice little box | 
        ||
| 959 | void show_boxed_message(const char *msg, int RenderFlag)  | 
        ||
| 960 | { | 
        ||
| 961 | int w,h;  | 
        ||
| 962 | int x,y;  | 
        ||
| 963 | |||
| 964 | gr_set_default_canvas();  | 
        ||
| 965 | auto &canvas = *grd_curcanv;  | 
        ||
| 966 | gr_set_fontcolor(canvas, BM_XRGB(31, 31, 31), -1);  | 
        ||
| 967 | auto &medium1_font = *MEDIUM1_FONT;  | 
        ||
| 968 | gr_get_string_size(medium1_font, msg, &w, &h, nullptr);  | 
        ||
| 969 | |||
| 970 | x = (SWIDTH-w)/2;  | 
        ||
| 971 | y = (SHEIGHT-h)/2;  | 
        ||
| 972 | |||
| 973 | nm_draw_background(canvas, x - BORDERX, y - BORDERY, x + w + BORDERX, y + h + BORDERY);  | 
        ||
| 974 | |||
| 975 | gr_string(canvas, medium1_font, 0x8000, y, msg, w, h);  | 
        ||
| 976 | |||
| 977 |         // If we haven't drawn behind it, need to flip | 
        ||
| 978 | if (!RenderFlag)  | 
        ||
| 979 | gr_flip();  | 
        ||
| 980 | } |