Subversion Repositories Games.Descent

Rev

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
}