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
 * Inferno gauge drivers
23
 *
24
 */
25
 
26
#include <algorithm>
27
#include <cmath>
28
#include <stdio.h>
29
#include <string.h>
30
#include <stdlib.h>
31
#include <stdarg.h>
32
 
33
#include "hudmsg.h"
34
#include "inferno.h"
35
#include "game.h"
36
#include "screens.h"
37
#include "gauges.h"
38
#include "physics.h"
39
#include "dxxerror.h"
40
#include "menu.h"                       // For the font.
41
#include "collide.h"
42
#include "object.h"
43
#include "newdemo.h"
44
#include "player.h"
45
#include "gamefont.h"
46
#include "bm.h"
47
#include "text.h"
48
#include "powerup.h"
49
#include "sounds.h"
50
#include "multi.h"
51
#include "endlevel.h"
52
#include "cntrlcen.h"
53
#include "controls.h"
54
#include "text.h"
55
#include "render.h"
56
#include "piggy.h"
57
#include "laser.h"
58
#include "weapon.h"
59
#include "common/3d/globvars.h"
60
#include "playsave.h"
61
#include "rle.h"
62
#if DXX_USE_OGL
63
#include "ogl_init.h"
64
#endif
65
#include "args.h"
66
 
67
#include "compiler-range_for.h"
68
#include "partial_range.h"
69
#include <utility>
70
 
71
using std::min;
72
 
73
namespace {
74
 
75
class local_multires_gauge_graphic
76
{
77
        const bool hiresmode = HIRESMODE;
78
public:
79
        bool is_hires() const
80
        {
81
                return hiresmode;
82
        }
83
        template <typename T>
84
                T get(T h, T l) const
85
        {
86
                return is_hires() ? h : l;
87
        }
88
        template <typename T>
89
                const T &rget(const T &h, const T &l) const
90
                {
91
                        return is_hires() ? h : l;
92
                }
93
};
94
 
95
static bool show_cloak_invul_timer()
96
{
97
        return PlayerCfg.CloakInvulTimer && Newdemo_state != ND_STATE_PLAYBACK;
98
}
99
 
100
}
101
 
102
//bitmap numbers for gauges
103
#define GAUGE_SHIELDS                   0               //0..9, in decreasing order (100%,90%...0%)
104
#define GAUGE_INVULNERABLE              10              //10..19
105
#define N_INVULNERABLE_FRAMES           10
106
#define GAUGE_ENERGY_LEFT               21
107
#define GAUGE_ENERGY_RIGHT              22
108
#define GAUGE_NUMERICAL                 23
109
#define GAUGE_BLUE_KEY                  24
110
#define GAUGE_GOLD_KEY                  25
111
#define GAUGE_RED_KEY                   26
112
#define GAUGE_BLUE_KEY_OFF              27
113
#define GAUGE_GOLD_KEY_OFF              28
114
#define GAUGE_RED_KEY_OFF               29
115
#define SB_GAUGE_BLUE_KEY               30
116
#define SB_GAUGE_GOLD_KEY               31
117
#define SB_GAUGE_RED_KEY                32
118
#define SB_GAUGE_BLUE_KEY_OFF           33
119
#define SB_GAUGE_GOLD_KEY_OFF           34
120
#define SB_GAUGE_RED_KEY_OFF            35
121
#define SB_GAUGE_ENERGY                 36
122
#define GAUGE_LIVES                     37
123
#define GAUGE_SHIPS                     38
124
#define RETICLE_CROSS                   46
125
#define RETICLE_PRIMARY                 48
126
#define RETICLE_SECONDARY               51
127
#define GAUGE_HOMING_WARNING_ON 56
128
#define GAUGE_HOMING_WARNING_OFF        57
129
#define KEY_ICON_BLUE                   68
130
#define KEY_ICON_YELLOW                 69
131
#define KEY_ICON_RED                    70
132
 
133
//Coordinats for gauges
134
#if defined(DXX_BUILD_DESCENT_I)
135
#define GAUGE_BLUE_KEY_X_L              45
136
#define GAUGE_BLUE_KEY_X_H              91
137
#define GAUGE_GOLD_KEY_X_L              44
138
#define GAUGE_GOLD_KEY_X_H              89
139
#define GAUGE_RED_KEY_X_L               43
140
#define GAUGE_RED_KEY_X_H               87
141
#define GAUGE_RED_KEY_Y_H               417
142
#define LEFT_ENERGY_GAUGE_X_H   137
143
#define RIGHT_ENERGY_GAUGE_X    ((multires_gauge_graphic.get(380, 190)))
144
#elif defined(DXX_BUILD_DESCENT_II)
145
#define GAUGE_AFTERBURNER               20
146
#define SB_GAUGE_AFTERBURNER            71
147
#define FLAG_ICON_RED                   72
148
#define FLAG_ICON_BLUE                  73
149
#define GAUGE_BLUE_KEY_X_L              272
150
#define GAUGE_BLUE_KEY_X_H              535
151
#define GAUGE_GOLD_KEY_X_L              273
152
#define GAUGE_GOLD_KEY_X_H              537
153
#define GAUGE_RED_KEY_X_L               274
154
#define GAUGE_RED_KEY_X_H               539
155
#define GAUGE_RED_KEY_Y_H               416
156
#define LEFT_ENERGY_GAUGE_X_H   138
157
#define RIGHT_ENERGY_GAUGE_X    ((multires_gauge_graphic.get(379, 190)))
158
#endif
159
 
160
#define GAUGE_BLUE_KEY_Y_L              152
161
#define GAUGE_BLUE_KEY_Y_H              374
162
#define GAUGE_BLUE_KEY_X                ((multires_gauge_graphic.get(GAUGE_BLUE_KEY_X_H, GAUGE_BLUE_KEY_X_L)))
163
#define GAUGE_BLUE_KEY_Y                ((multires_gauge_graphic.get(GAUGE_BLUE_KEY_Y_H, GAUGE_BLUE_KEY_Y_L)))
164
#define GAUGE_GOLD_KEY_Y_L              162
165
#define GAUGE_GOLD_KEY_Y_H              395
166
#define GAUGE_GOLD_KEY_X                ((multires_gauge_graphic.get(GAUGE_GOLD_KEY_X_H, GAUGE_GOLD_KEY_X_L)))
167
#define GAUGE_GOLD_KEY_Y                ((multires_gauge_graphic.get(GAUGE_GOLD_KEY_Y_H, GAUGE_GOLD_KEY_Y_L)))
168
#define GAUGE_RED_KEY_Y_L               172
169
#define GAUGE_RED_KEY_X                 ((multires_gauge_graphic.get(GAUGE_RED_KEY_X_H, GAUGE_RED_KEY_X_L)))
170
#define GAUGE_RED_KEY_Y                 ((multires_gauge_graphic.get(GAUGE_RED_KEY_Y_H, GAUGE_RED_KEY_Y_L)))
171
#define SB_GAUGE_KEYS_X_L               11
172
#define SB_GAUGE_KEYS_X_H               26
173
#define SB_GAUGE_KEYS_X                 ((multires_gauge_graphic.get(SB_GAUGE_KEYS_X_H, SB_GAUGE_KEYS_X_L)))
174
#define SB_GAUGE_BLUE_KEY_Y_L           153
175
#define SB_GAUGE_GOLD_KEY_Y_L           169
176
#define SB_GAUGE_RED_KEY_Y_L            185
177
#define SB_GAUGE_BLUE_KEY_Y_H           390
178
#define SB_GAUGE_GOLD_KEY_Y_H           422
179
#define SB_GAUGE_RED_KEY_Y_H            454
180
#define SB_GAUGE_BLUE_KEY_Y             ((multires_gauge_graphic.get(SB_GAUGE_BLUE_KEY_Y_H, SB_GAUGE_BLUE_KEY_Y_L)))
181
#define SB_GAUGE_GOLD_KEY_Y             ((multires_gauge_graphic.get(SB_GAUGE_GOLD_KEY_Y_H, SB_GAUGE_GOLD_KEY_Y_L)))
182
#define SB_GAUGE_RED_KEY_Y              ((multires_gauge_graphic.get(SB_GAUGE_RED_KEY_Y_H, SB_GAUGE_RED_KEY_Y_L)))
183
#define LEFT_ENERGY_GAUGE_X_L   70
184
#define LEFT_ENERGY_GAUGE_Y_L   131
185
#define LEFT_ENERGY_GAUGE_W_L   64
186
#define LEFT_ENERGY_GAUGE_H_L   8
187
#define LEFT_ENERGY_GAUGE_Y_H   314
188
#define LEFT_ENERGY_GAUGE_W_H   133
189
#define LEFT_ENERGY_GAUGE_H_H   21
190
#define LEFT_ENERGY_GAUGE_X     ((multires_gauge_graphic.get(LEFT_ENERGY_GAUGE_X_H, LEFT_ENERGY_GAUGE_X_L)))
191
#define LEFT_ENERGY_GAUGE_Y     ((multires_gauge_graphic.get(LEFT_ENERGY_GAUGE_Y_H, LEFT_ENERGY_GAUGE_Y_L)))
192
#define LEFT_ENERGY_GAUGE_W     ((multires_gauge_graphic.get(LEFT_ENERGY_GAUGE_W_H, LEFT_ENERGY_GAUGE_W_L)))
193
#define LEFT_ENERGY_GAUGE_H     ((multires_gauge_graphic.get(LEFT_ENERGY_GAUGE_H_H, LEFT_ENERGY_GAUGE_H_L)))
194
#define RIGHT_ENERGY_GAUGE_Y    ((multires_gauge_graphic.get(314, 131)))
195
#define RIGHT_ENERGY_GAUGE_W    ((multires_gauge_graphic.get(133, 64)))
196
#define RIGHT_ENERGY_GAUGE_H    ((multires_gauge_graphic.get(21, 8)))
197
 
198
#if defined(DXX_BUILD_DESCENT_I)
199
#define SB_ENERGY_GAUGE_Y               ((multires_gauge_graphic.get(390, 155)))
200
#define SB_ENERGY_GAUGE_H               ((multires_gauge_graphic.get(82, 41)))
201
#define SB_ENERGY_NUM_Y                 ((multires_gauge_graphic.get(457, 190)))
202
#define SHIELD_GAUGE_Y                  ((multires_gauge_graphic.get(374, 155)))
203
#define SB_SHIELD_NUM_Y                 (SB_SHIELD_GAUGE_Y-((multires_gauge_graphic.get(16, 7))))                       //156 -- MWA used to be hard coded to 156
204
#define NUMERICAL_GAUGE_Y               ((multires_gauge_graphic.get(316, 130)))
205
#define PRIMARY_W_PIC_X                 ((multires_gauge_graphic.get(135, 64)))
206
#define SECONDARY_W_PIC_X               ((multires_gauge_graphic.get(405, 234)))
207
#define SECONDARY_W_PIC_Y               ((multires_gauge_graphic.get(370, 154)))
208
#define SECONDARY_W_TEXT_X              multires_gauge_graphic.get(462, 207)
209
#define SECONDARY_W_TEXT_Y              multires_gauge_graphic.get(400, 157)
210
#define SECONDARY_AMMO_X                multires_gauge_graphic.get(475, 213)
211
#define SECONDARY_AMMO_Y                multires_gauge_graphic.get(425, 171)
212
#define SB_LIVES_X                      ((multires_gauge_graphic.get(550, 266)))
213
#define SB_LIVES_Y                      ((multires_gauge_graphic.get(450, 185)))
214
#define SB_SCORE_RIGHT_H                605
215
#define BOMB_COUNT_X                    ((multires_gauge_graphic.get(468, 210)))
216
#define PRIMARY_W_BOX_RIGHT_H           241
217
#define SECONDARY_W_BOX_RIGHT_L         264     //(SECONDARY_W_BOX_LEFT+54)
218
#define SECONDARY_W_BOX_LEFT_H          403
219
#define SECONDARY_W_BOX_TOP_H           364
220
#define SECONDARY_W_BOX_RIGHT_H         531
221
#define SB_PRIMARY_W_BOX_TOP_L          154
222
#define SB_PRIMARY_W_BOX_BOT_L          (195)
223
#define SB_SECONDARY_W_BOX_TOP_L        154
224
#define SB_SECONDARY_W_BOX_RIGHT_L      (SB_SECONDARY_W_BOX_LEFT_L+54)
225
#define SB_SECONDARY_W_BOX_BOT_L        (153+42)
226
#define SB_SECONDARY_AMMO_X             (SB_SECONDARY_W_BOX_LEFT + (multires_gauge_graphic.get(14, 11)))        //(212+9)
227
#define GET_GAUGE_INDEX(x)              (Gauges[x].index)
228
 
229
#elif defined(DXX_BUILD_DESCENT_II)
230
#define AFTERBURNER_GAUGE_X_L   45-1
231
#define AFTERBURNER_GAUGE_Y_L   158
232
#define AFTERBURNER_GAUGE_H_L   32
233
#define AFTERBURNER_GAUGE_X_H   88
234
#define AFTERBURNER_GAUGE_Y_H   378
235
#define AFTERBURNER_GAUGE_H_H   65
236
#define AFTERBURNER_GAUGE_X     ((multires_gauge_graphic.get(AFTERBURNER_GAUGE_X_H, AFTERBURNER_GAUGE_X_L)))
237
#define AFTERBURNER_GAUGE_Y     ((multires_gauge_graphic.get(AFTERBURNER_GAUGE_Y_H, AFTERBURNER_GAUGE_Y_L)))
238
#define AFTERBURNER_GAUGE_H     ((multires_gauge_graphic.get(AFTERBURNER_GAUGE_H_H, AFTERBURNER_GAUGE_H_L)))
239
#define SB_AFTERBURNER_GAUGE_X          ((multires_gauge_graphic.get(196, 98)))
240
#define SB_AFTERBURNER_GAUGE_Y          ((multires_gauge_graphic.get(445, 184)))
241
#define SB_AFTERBURNER_GAUGE_W          ((multires_gauge_graphic.get(33, 16)))
242
#define SB_AFTERBURNER_GAUGE_H          ((multires_gauge_graphic.get(29, 13)))
243
#define SB_ENERGY_GAUGE_Y               ((multires_gauge_graphic.get(381, 155-2)))
244
#define SB_ENERGY_GAUGE_H               ((multires_gauge_graphic.get(60, 29)))
245
#define SB_ENERGY_NUM_Y                 ((multires_gauge_graphic.get(457, 175)))
246
#define SHIELD_GAUGE_Y                  ((multires_gauge_graphic.get(375, 155)))
247
#define SB_SHIELD_NUM_Y                 (SB_SHIELD_GAUGE_Y-((multires_gauge_graphic.get(16, 8))))                       //156 -- MWA used to be hard coded to 156
248
#define NUMERICAL_GAUGE_Y               ((multires_gauge_graphic.get(314, 130)))
249
#define PRIMARY_W_PIC_X                 ((multires_gauge_graphic.get(135-10, 64)))
250
#define SECONDARY_W_PIC_X               ((multires_gauge_graphic.get(466, 234)))
251
#define SECONDARY_W_PIC_Y               ((multires_gauge_graphic.get(374, 154)))
252
#define SECONDARY_W_TEXT_X              multires_gauge_graphic.get(413, 207)
253
#define SECONDARY_W_TEXT_Y              multires_gauge_graphic.get(378, 157)
254
#define SECONDARY_AMMO_X                multires_gauge_graphic.get(428, 213)
255
#define SECONDARY_AMMO_Y                multires_gauge_graphic.get(407, 171)
256
#define SB_LIVES_X                      ((multires_gauge_graphic.get(550-10-3, 266)))
257
#define SB_LIVES_Y                      ((multires_gauge_graphic.get(450-3, 185)))
258
#define SB_SCORE_RIGHT_H                (605+8)
259
#define BOMB_COUNT_X                    ((multires_gauge_graphic.get(546, 275)))
260
#define PRIMARY_W_BOX_RIGHT_H           242
261
#define SECONDARY_W_BOX_RIGHT_L         263     //(SECONDARY_W_BOX_LEFT+54)
262
#define SECONDARY_W_BOX_LEFT_H          404
263
#define SECONDARY_W_BOX_TOP_H           363
264
#define SECONDARY_W_BOX_RIGHT_H         529
265
#define SB_PRIMARY_W_BOX_TOP_L          153
266
#define SB_PRIMARY_W_BOX_BOT_L          (195+1)
267
#define SB_SECONDARY_W_BOX_TOP_L        153
268
#define SB_SECONDARY_W_BOX_RIGHT_L      (SB_SECONDARY_W_BOX_LEFT_L+54+1)
269
#define SB_SECONDARY_W_BOX_BOT_L        (SB_SECONDARY_W_BOX_TOP_L+43)
270
#define SB_SECONDARY_AMMO_X             (SB_SECONDARY_W_BOX_LEFT + (multires_gauge_graphic.get(14 - 4, 11)))    //(212+9)
271
#define GET_GAUGE_INDEX(x)              ((multires_gauge_graphic.rget(Gauges_hires, Gauges)[x].index))
272
 
273
#endif
274
 
275
// MOVEME
276
#define HOMING_WARNING_X                ((multires_gauge_graphic.get(13, 7)))
277
#define HOMING_WARNING_Y                ((multires_gauge_graphic.get(416, 171)))
278
 
279
#define SB_ENERGY_GAUGE_X               ((multires_gauge_graphic.get(196, 98)))
280
#define SB_ENERGY_GAUGE_W               ((multires_gauge_graphic.get(32, 16)))
281
#define SHIP_GAUGE_X                    (SHIELD_GAUGE_X+((multires_gauge_graphic.get(11, 5))))
282
#define SHIP_GAUGE_Y                    (SHIELD_GAUGE_Y+((multires_gauge_graphic.get(10, 5))))
283
#define SHIELD_GAUGE_X                  ((multires_gauge_graphic.get(292, 146)))
284
#define SB_SHIELD_GAUGE_X               ((multires_gauge_graphic.get(247, 123)))                //139
285
#define SB_SHIELD_GAUGE_Y               ((multires_gauge_graphic.get(395, 163)))
286
#define SB_SHIP_GAUGE_X                 (SB_SHIELD_GAUGE_X+((multires_gauge_graphic.get(11, 5))))
287
#define SB_SHIP_GAUGE_Y                 (SB_SHIELD_GAUGE_Y+((multires_gauge_graphic.get(10, 5))))
288
#define SB_SHIELD_NUM_X                 (SB_SHIELD_GAUGE_X+((multires_gauge_graphic.get(21, 12))))      //151
289
#define NUMERICAL_GAUGE_X               ((multires_gauge_graphic.get(308, 154)))
290
#define PRIMARY_W_PIC_Y                 ((multires_gauge_graphic.get(370, 154)))
291
#define PRIMARY_W_TEXT_X                multires_gauge_graphic.get(182, 87)
292
#define PRIMARY_W_TEXT_Y                multires_gauge_graphic.get(378, 157)
293
#define PRIMARY_AMMO_X                  multires_gauge_graphic.get(186, 93)
294
#define PRIMARY_AMMO_Y                  multires_gauge_graphic.get(407, 171)
295
#define SB_LIVES_LABEL_X                ((multires_gauge_graphic.get(475, 237)))
296
#define SB_SCORE_RIGHT_L                301
297
#define SB_SCORE_RIGHT                  ((multires_gauge_graphic.get(SB_SCORE_RIGHT_H, SB_SCORE_RIGHT_L)))
298
#define SB_SCORE_Y                      ((multires_gauge_graphic.get(398, 158)))
299
#define SB_SCORE_LABEL_X                ((multires_gauge_graphic.get(475, 237)))
300
#define SB_SCORE_ADDED_RIGHT            ((multires_gauge_graphic.get(SB_SCORE_RIGHT_H, SB_SCORE_RIGHT_L)))
301
#define SB_SCORE_ADDED_Y                ((multires_gauge_graphic.get(413, 165)))
302
#define BOMB_COUNT_Y                    ((multires_gauge_graphic.get(445, 186)))
303
#define SB_BOMB_COUNT_X                 ((multires_gauge_graphic.get(342, 171)))
304
#define SB_BOMB_COUNT_Y                 ((multires_gauge_graphic.get(458, 191)))
305
 
306
// defining box boundries for weapon pictures
307
#define PRIMARY_W_BOX_LEFT_L            63
308
#define PRIMARY_W_BOX_TOP_L             151             //154
309
#define PRIMARY_W_BOX_RIGHT_L           (PRIMARY_W_BOX_LEFT_L+58)
310
#define PRIMARY_W_BOX_BOT_L             (PRIMARY_W_BOX_TOP_L+42)
311
#define PRIMARY_W_BOX_LEFT_H            121
312
#define PRIMARY_W_BOX_TOP_H             364
313
#define PRIMARY_W_BOX_BOT_H             (PRIMARY_W_BOX_TOP_H+106)               //470
314
#define PRIMARY_W_BOX_LEFT              ((multires_gauge_graphic.get(PRIMARY_W_BOX_LEFT_H, PRIMARY_W_BOX_LEFT_L)))
315
#define PRIMARY_W_BOX_TOP               ((multires_gauge_graphic.get(PRIMARY_W_BOX_TOP_H, PRIMARY_W_BOX_TOP_L)))
316
#define PRIMARY_W_BOX_RIGHT             ((multires_gauge_graphic.get(PRIMARY_W_BOX_RIGHT_H, PRIMARY_W_BOX_RIGHT_L)))
317
#define PRIMARY_W_BOX_BOT               ((multires_gauge_graphic.get(PRIMARY_W_BOX_BOT_H, PRIMARY_W_BOX_BOT_L)))
318
#define SECONDARY_W_BOX_LEFT_L          202     //207
319
#define SECONDARY_W_BOX_TOP_L           151
320
#define SECONDARY_W_BOX_BOT_L           (SECONDARY_W_BOX_TOP_L+42)
321
#define SECONDARY_W_BOX_BOT_H           (SECONDARY_W_BOX_TOP_H+106)             //470
322
#define SECONDARY_W_BOX_LEFT            ((multires_gauge_graphic.get(SECONDARY_W_BOX_LEFT_H, SECONDARY_W_BOX_LEFT_L)))
323
#define SECONDARY_W_BOX_TOP             ((multires_gauge_graphic.get(SECONDARY_W_BOX_TOP_H, SECONDARY_W_BOX_TOP_L)))
324
#define SECONDARY_W_BOX_RIGHT           ((multires_gauge_graphic.get(SECONDARY_W_BOX_RIGHT_H, SECONDARY_W_BOX_RIGHT_L)))
325
#define SECONDARY_W_BOX_BOT             ((multires_gauge_graphic.get(SECONDARY_W_BOX_BOT_H, SECONDARY_W_BOX_BOT_L)))
326
#define SB_PRIMARY_W_BOX_LEFT_L         34              //50
327
#define SB_PRIMARY_W_BOX_RIGHT_L        (SB_PRIMARY_W_BOX_LEFT_L+55)
328
#define SB_PRIMARY_W_BOX_LEFT_H         68
329
#define SB_PRIMARY_W_BOX_TOP_H          381
330
#define SB_PRIMARY_W_BOX_RIGHT_H        179
331
#define SB_PRIMARY_W_BOX_BOT_H          473
332
#define SB_PRIMARY_W_BOX_LEFT           ((multires_gauge_graphic.get(SB_PRIMARY_W_BOX_LEFT_H, SB_PRIMARY_W_BOX_LEFT_L)))
333
#define SB_SECONDARY_W_BOX_LEFT_L       169
334
#define SB_SECONDARY_W_BOX_LEFT_H       338
335
#define SB_SECONDARY_W_BOX_TOP_H        381
336
#define SB_SECONDARY_W_BOX_RIGHT_H      449
337
#define SB_SECONDARY_W_BOX_BOT_H        473
338
#define SB_SECONDARY_W_BOX_LEFT         ((multires_gauge_graphic.get(SB_SECONDARY_W_BOX_LEFT_H, SB_SECONDARY_W_BOX_LEFT_L)))    //210
339
#define SB_PRIMARY_W_PIC_X              (SB_PRIMARY_W_BOX_LEFT+1)       //51
340
#define SB_PRIMARY_W_PIC_Y              ((multires_gauge_graphic.get(382, 154)))
341
#define SB_PRIMARY_W_TEXT_X             (SB_PRIMARY_W_BOX_LEFT + multires_gauge_graphic.get(50, 24))    //(51+23)
342
#define SB_PRIMARY_W_TEXT_Y             (multires_gauge_graphic.get(390, 157))
343
#define SB_PRIMARY_AMMO_X               (SB_PRIMARY_W_BOX_LEFT + multires_gauge_graphic.get(58, 30))    //((SB_PRIMARY_W_BOX_LEFT+33)-3)        //(51+32)
344
#define SB_PRIMARY_AMMO_Y               multires_gauge_graphic.get(410, 171)
345
#define SB_SECONDARY_W_PIC_X            ((multires_gauge_graphic.get(385, (SB_SECONDARY_W_BOX_LEFT+27))))       //(212+27)
346
#define SB_SECONDARY_W_PIC_Y            ((multires_gauge_graphic.get(382, 154)))
347
#define SB_SECONDARY_W_TEXT_X           (SB_SECONDARY_W_BOX_LEFT + 2)   //212
348
#define SB_SECONDARY_W_TEXT_Y           multires_gauge_graphic.get(390, 157)
349
#define SB_SECONDARY_AMMO_Y             multires_gauge_graphic.get(414, 171)
350
 
351
#define WS_SET                          0               //in correct state
352
#define WS_FADING_OUT                   1
353
#define WS_FADING_IN                    2
354
#define FADE_SCALE                      (2*i2f(GR_FADE_LEVELS)/REARM_TIME)              // fade out and back in REARM_TIME, in fade levels per seconds (int)
355
 
356
#define COCKPIT_PRIMARY_BOX             ((multires_gauge_graphic.get(4, 0)))
357
#define COCKPIT_SECONDARY_BOX           ((multires_gauge_graphic.get(5, 1)))
358
#define SB_PRIMARY_BOX                  ((multires_gauge_graphic.get(6, 2)))
359
#define SB_SECONDARY_BOX                ((multires_gauge_graphic.get(7, 3)))
360
 
361
// scaling gauges
362
#define BASE_WIDTH(G) ((G).get(640, 320))
363
#define BASE_HEIGHT(G)  ((G).get(480, 200))
364
namespace {
365
 
366
#if DXX_USE_OGL
367
template <char tag>
368
class hud_scale_float;
369
#else
370
struct hud_unscaled_int
371
{
372
        long operator()(const unsigned i) const
373
        {
374
                return i;
375
        }
376
};
377
 
378
template <char tag>
379
using hud_scale_float = hud_unscaled_int;
380
#endif
381
 
382
using hud_ar_scale_float = hud_scale_float<'a'>;
383
using hud_x_scale_float = hud_scale_float<'x'>;
384
using hud_y_scale_float = hud_scale_float<'y'>;
385
 
386
#if DXX_USE_OGL
387
class base_hud_scaled_int
388
{
389
        const long v;
390
public:
391
        explicit constexpr base_hud_scaled_int(const long l) :
392
                v(l)
393
        {
394
        }
395
        operator long() const
396
        {
397
                return v;
398
        }
399
};
400
 
401
template <char>
402
class hud_scaled_int : public base_hud_scaled_int
403
{
404
public:
405
        DXX_INHERIT_CONSTRUCTORS(hud_scaled_int, base_hud_scaled_int);
406
};
407
 
408
class base_hud_scale_float
409
{
410
protected:
411
        const double scale;
412
        long operator()(const int i) const
413
        {
414
                return (this->scale * static_cast<double>(i)) + 0.5;
415
        }
416
        double get() const
417
        {
418
                return scale;
419
        }
420
public:
421
        constexpr base_hud_scale_float(const double s) :
422
                scale(s)
423
        {
424
        }
425
};
426
 
427
template <char tag>
428
class hud_scale_float : base_hud_scale_float
429
{
430
public:
431
        using scaled = hud_scaled_int<tag>;
432
        using base_hud_scale_float::get;
433
        DXX_INHERIT_CONSTRUCTORS(hud_scale_float, base_hud_scale_float);
434
        scaled operator()(const int i) const
435
        {
436
                return scaled(this->base_hud_scale_float::operator()(i));
437
        }
438
};
439
 
440
static hud_x_scale_float HUD_SCALE_X(const unsigned screen_width, const local_multires_gauge_graphic multires_gauge_graphic)
441
{
442
        return static_cast<double>(screen_width) / BASE_WIDTH(multires_gauge_graphic);
443
}
444
 
445
static hud_y_scale_float HUD_SCALE_Y(const unsigned screen_height, const local_multires_gauge_graphic multires_gauge_graphic)
446
{
447
        return static_cast<double>(screen_height) / BASE_HEIGHT(multires_gauge_graphic);
448
}
449
 
450
static hud_ar_scale_float HUD_SCALE_AR(const hud_x_scale_float x, const hud_y_scale_float y)
451
{
452
        return std::min(x.get(), y.get());
453
}
454
 
455
static hud_ar_scale_float HUD_SCALE_AR(const unsigned screen_width, const unsigned screen_height, const local_multires_gauge_graphic multires_gauge_graphic)
456
{
457
        return HUD_SCALE_AR(HUD_SCALE_X(screen_width, multires_gauge_graphic), HUD_SCALE_Y(screen_height, multires_gauge_graphic));
458
}
459
 
460
#define draw_numerical_display_draw_context     hud_draw_context_hs
461
#else
462
#define hud_bitblt_free(canvas,x,y,w,h,bm)      hud_bitblt_free(canvas,x,y,bm)
463
#define draw_numerical_display_draw_context     hud_draw_context_hs_mr
464
 
465
static hud_ar_scale_float HUD_SCALE_AR(hud_x_scale_float, hud_y_scale_float)
466
{
467
        return {};
468
}
469
 
470
static hud_ar_scale_float HUD_SCALE_AR(unsigned, unsigned, local_multires_gauge_graphic)
471
{
472
        return {};
473
}
474
#endif
475
 
476
}
477
 
478
#if defined(DXX_BUILD_DESCENT_I)
479
#define PAGE_IN_GAUGE(x,g)      PAGE_IN_GAUGE(x)
480
std::array<bitmap_index, MAX_GAUGE_BMS_MAC> Gauges; // Array of all gauge bitmaps.
481
#elif defined(DXX_BUILD_DESCENT_II)
482
#define PAGE_IN_GAUGE   PAGE_IN_GAUGE
483
std::array<bitmap_index, MAX_GAUGE_BMS> Gauges,   // Array of all gauge bitmaps.
484
        Gauges_hires;   // hires gauges
485
static std::array<int, 2> weapon_box_user{{WBU_WEAPON, WBU_WEAPON}};            //see WBU_ constants in gauges.h
486
#endif
487
static std::array<grs_subbitmap_ptr, 2> WinBoxOverlay; // Overlay subbitmaps for both weapon boxes
488
 
489
namespace dsx {
490
static inline void PAGE_IN_GAUGE(int x, const local_multires_gauge_graphic multires_gauge_graphic)
491
{
492
        const auto &g =
493
#if defined(DXX_BUILD_DESCENT_II)
494
                multires_gauge_graphic.is_hires() ? Gauges_hires :
495
#endif
496
                Gauges;
497
        PIGGY_PAGE_IN(g[x]);
498
}
499
}
500
 
501
static void draw_ammo_info(grs_canvas &, unsigned x, unsigned y, unsigned ammo_count);
502
 
503
static int score_display;
504
static fix score_time;
505
static std::array<int, 2> old_weapon{{-1, -1}};
506
static int old_laser_level              = -1;
507
static int invulnerable_frame;
508
static std::array<int, 2> weapon_box_states;
509
static_assert(WS_SET == 0, "weapon_box_states must start at zero");
510
static std::array<fix, 2> weapon_box_fade_values;
511
int     Color_0_31_0 = -1;
512
 
513
namespace dcx {
514
 
515
namespace {
516
 
517
struct hud_draw_context_canvas
518
{
519
        grs_canvas &canvas;
520
        hud_draw_context_canvas(grs_canvas &c) :
521
                canvas(c)
522
        {
523
        }
524
};
525
 
526
struct hud_draw_context_multires
527
{
528
        const local_multires_gauge_graphic multires_gauge_graphic;
529
        hud_draw_context_multires(const local_multires_gauge_graphic mr) :
530
                multires_gauge_graphic(mr)
531
        {
532
        }
533
};
534
 
535
struct hud_draw_context_mr : hud_draw_context_canvas, hud_draw_context_multires
536
{
537
        hud_draw_context_mr(grs_canvas &c, const local_multires_gauge_graphic mr) :
538
                hud_draw_context_canvas(c), hud_draw_context_multires(mr)
539
        {
540
        }
541
};
542
 
543
struct hud_draw_context_xyscale
544
{
545
        const hud_x_scale_float xscale;
546
        const hud_y_scale_float yscale;
547
#if DXX_USE_OGL
548
        /*constexpr*/ hud_draw_context_xyscale(const hud_x_scale_float x, const hud_y_scale_float y) : // Pierre-Marie Baty -- can't possibly be constexpr
549
                xscale(x), yscale(y)
550
        {
551
        }
552
#else
553
        constexpr hud_draw_context_xyscale() :
554
                xscale{}, yscale{}
555
        {
556
        }
557
#endif
558
};
559
 
560
struct hud_draw_context_hs_mr : hud_draw_context_mr, hud_draw_context_xyscale
561
{
562
#if DXX_USE_OGL
563
        hud_draw_context_hs_mr(grs_canvas &c, const unsigned screen_width, const unsigned screen_height, const local_multires_gauge_graphic multires_gauge_graphic) :
564
                hud_draw_context_mr(c, multires_gauge_graphic),
565
                hud_draw_context_xyscale(HUD_SCALE_X(screen_width, multires_gauge_graphic), HUD_SCALE_Y(screen_height, multires_gauge_graphic))
566
        {
567
        }
568
#else
569
        hud_draw_context_hs_mr(grs_canvas &c, unsigned, unsigned, const local_multires_gauge_graphic multires_gauge_graphic) :
570
                hud_draw_context_mr(c, multires_gauge_graphic)
571
        {
572
        }
573
#endif
574
};
575
 
576
struct hud_draw_context_hs : hud_draw_context_canvas, hud_draw_context_xyscale
577
{
578
        hud_draw_context_hs(const hud_draw_context_hs_mr &hudctx) :
579
                hud_draw_context_canvas(hudctx.canvas), hud_draw_context_xyscale(hudctx)
580
        {
581
        }
582
};
583
 
584
struct gauge_box
585
{
586
        int left,top;
587
        int right,bot;          //maximal box
588
};
589
 
590
const gauge_box gauge_boxes[] = {
591
 
592
// primary left/right low res
593
                {PRIMARY_W_BOX_LEFT_L,PRIMARY_W_BOX_TOP_L,PRIMARY_W_BOX_RIGHT_L,PRIMARY_W_BOX_BOT_L},
594
                {SECONDARY_W_BOX_LEFT_L,SECONDARY_W_BOX_TOP_L,SECONDARY_W_BOX_RIGHT_L,SECONDARY_W_BOX_BOT_L},
595
 
596
//sb left/right low res
597
                {SB_PRIMARY_W_BOX_LEFT_L,SB_PRIMARY_W_BOX_TOP_L,SB_PRIMARY_W_BOX_RIGHT_L,SB_PRIMARY_W_BOX_BOT_L},
598
                {SB_SECONDARY_W_BOX_LEFT_L,SB_SECONDARY_W_BOX_TOP_L,SB_SECONDARY_W_BOX_RIGHT_L,SB_SECONDARY_W_BOX_BOT_L},
599
 
600
// primary left/right hires
601
                {PRIMARY_W_BOX_LEFT_H,PRIMARY_W_BOX_TOP_H,PRIMARY_W_BOX_RIGHT_H,PRIMARY_W_BOX_BOT_H},
602
                {SECONDARY_W_BOX_LEFT_H,SECONDARY_W_BOX_TOP_H,SECONDARY_W_BOX_RIGHT_H,SECONDARY_W_BOX_BOT_H},
603
 
604
// sb left/right hires
605
                {SB_PRIMARY_W_BOX_LEFT_H,SB_PRIMARY_W_BOX_TOP_H,SB_PRIMARY_W_BOX_RIGHT_H,SB_PRIMARY_W_BOX_BOT_H},
606
                {SB_SECONDARY_W_BOX_LEFT_H,SB_SECONDARY_W_BOX_TOP_H,SB_SECONDARY_W_BOX_RIGHT_H,SB_SECONDARY_W_BOX_BOT_H},
607
        };
608
 
609
struct d_gauge_span
610
{
611
        unsigned l, r;
612
};
613
 
614
struct dspan
615
{
616
        d_gauge_span l, r;
617
};
618
 
619
//store delta x values from left of box
620
const std::array<dspan, 43> weapon_windows_lowres = {{
621
        {{71,114},              {208,255}},
622
        {{69,116},              {206,257}},
623
        {{68,117},              {205,258}},
624
        {{66,118},              {204,259}},
625
        {{66,119},              {203,260}},
626
        {{66,119},              {203,260}},
627
        {{65,119},              {203,260}},
628
        {{65,119},              {203,260}},
629
        {{65,119},              {203,260}},
630
        {{65,119},              {203,261}},
631
        {{65,119},              {203,261}},
632
        {{65,119},              {203,261}},
633
        {{65,119},              {203,261}},
634
        {{65,119},              {203,261}},
635
        {{65,119},              {203,261}},
636
        {{64,119},              {203,261}},
637
        {{64,119},              {203,261}},
638
        {{64,119},              {203,261}},
639
        {{64,119},              {203,262}},
640
        {{64,119},              {203,262}},
641
        {{64,119},              {203,262}},
642
        {{64,119},              {203,262}},
643
        {{64,119},              {203,262}},
644
        {{64,119},              {203,262}},
645
        {{63,119},              {203,262}},
646
        {{63,118},              {203,262}},
647
        {{63,118},              {204,263}},
648
        {{63,118},              {204,263}},
649
        {{63,118},              {204,263}},
650
        {{63,118},              {204,263}},
651
        {{63,118},              {204,263}},
652
        {{63,118},              {204,263}},
653
        {{63,118},              {204,263}},
654
        {{63,118},              {204,263}},
655
        {{63,118},              {204,263}},
656
        {{63,118},              {204,263}},
657
        {{63,118},              {204,263}},
658
        {{63,117},              {204,263}},
659
        {{63,117},              {205,263}},
660
        {{64,116},              {206,262}},
661
        {{65,115},              {207,261}},
662
        {{66,113},              {208,260}},
663
        {{68,111},              {211,255}},
664
}};
665
 
666
//store delta x values from left of box
667
const std::array<dspan, 107> weapon_windows_hires = {{
668
        {{141,231},             {416,509}},
669
        {{139,234},             {413,511}},
670
        {{137,235},             {412,513}},
671
        {{136,237},             {410,514}},
672
        {{135,238},             {409,515}},
673
        {{134,239},             {408,516}},
674
        {{133,240},             {407,517}},
675
        {{132,240},             {407,518}},
676
        {{131,241},             {406,519}},
677
        {{131,241},             {406,519}},
678
        {{130,242},             {405,520}},
679
        {{129,242},             {405,521}},
680
        {{129,242},             {405,521}},
681
        {{129,243},             {404,521}},
682
        {{128,243},             {404,522}},
683
        {{128,243},             {404,522}},
684
        {{128,243},             {404,522}},
685
        {{128,243},             {404,522}},
686
        {{128,243},             {404,522}},
687
        {{127,243},             {404,523}},
688
        {{127,243},             {404,523}},
689
        {{127,243},             {404,523}},
690
        {{127,243},             {404,523}},
691
        {{127,243},             {404,523}},
692
        {{127,243},             {404,523}},
693
        {{127,243},             {404,523}},
694
        {{127,243},             {404,523}},
695
        {{127,243},             {404,523}},
696
        {{127,243},             {404,523}},
697
        {{126,243},             {404,524}},
698
        {{126,243},             {404,524}},
699
        {{126,243},             {404,524}},
700
        {{126,243},             {404,524}},
701
        {{126,242},             {405,524}},
702
        {{126,242},             {405,524}},
703
        {{126,242},             {405,524}},
704
        {{126,242},             {405,524}},
705
        {{126,242},             {405,524}},
706
        {{126,242},             {405,524}},
707
        {{125,242},             {405,525}},
708
        {{125,242},             {405,525}},
709
        {{125,242},             {405,525}},
710
        {{125,242},             {405,525}},
711
        {{125,242},             {405,525}},
712
        {{125,242},             {405,525}},
713
        {{125,242},             {405,525}},
714
        {{125,242},             {405,525}},
715
        {{125,242},             {405,525}},
716
        {{125,242},             {405,525}},
717
        {{124,242},             {405,526}},
718
        {{124,242},             {405,526}},
719
        {{124,241},             {406,526}},
720
        {{124,241},             {406,526}},
721
        {{124,241},             {406,526}},
722
        {{124,241},             {406,526}},
723
        {{124,241},             {406,526}},
724
        {{124,241},             {406,526}},
725
        {{124,241},             {406,526}},
726
        {{124,241},             {406,526}},
727
        {{124,241},             {406,527}},
728
        {{123,241},             {406,527}},
729
        {{123,241},             {406,527}},
730
        {{123,241},             {406,527}},
731
        {{123,241},             {406,527}},
732
        {{123,241},             {406,527}},
733
        {{123,241},             {406,527}},
734
        {{123,241},             {406,527}},
735
        {{123,241},             {406,527}},
736
        {{123,241},             {406,527}},
737
        {{123,241},             {406,527}},
738
        {{123,241},             {406,527}},
739
        {{123,241},             {406,527}},
740
        {{122,241},             {406,528}},
741
        {{122,241},             {406,528}},
742
        {{122,240},             {407,528}},
743
        {{122,240},             {407,528}},
744
        {{122,240},             {407,528}},
745
        {{122,240},             {407,528}},
746
        {{122,240},             {407,528}},
747
        {{122,240},             {407,528}},
748
        {{122,240},             {407,528}},
749
        {{122,240},             {407,529}},
750
        {{121,240},             {407,529}},
751
        {{121,240},             {407,529}},
752
        {{121,240},             {407,529}},
753
        {{121,240},             {407,529}},
754
        {{121,240},             {407,529}},
755
        {{121,240},             {407,529}},
756
        {{121,240},             {407,529}},
757
        {{121,239},             {408,529}},
758
        {{121,239},             {408,529}},
759
        {{121,239},             {408,529}},
760
        {{121,238},             {409,529}},
761
        {{121,238},             {409,529}},
762
        {{121,238},             {409,529}},
763
        {{122,237},             {410,529}},
764
        {{122,237},             {410,528}},
765
        {{123,236},             {411,527}},
766
        {{123,235},             {412,527}},
767
        {{124,234},             {413,526}},
768
        {{125,233},             {414,525}},
769
        {{126,232},             {415,524}},
770
        {{126,231},             {416,524}},
771
        {{128,230},             {417,522}},
772
        {{130,228},             {419,521}},
773
        {{131,226},             {422,519}},
774
        {{133,223},             {424,518}},
775
}};
776
 
777
static inline void hud_bitblt_free(grs_canvas &canvas, const unsigned x, const unsigned y, const unsigned w, const unsigned h, grs_bitmap &bm)
778
{
779
#if DXX_USE_OGL
780
        ogl_ubitmapm_cs(canvas, x, y, w, h, bm, ogl_colors::white, F1_0);
781
#else
782
        gr_ubitmapm(canvas, x, y, bm);
783
#endif
784
}
785
 
786
static void hud_bitblt_scaled_xy(const hud_draw_context_hs hudctx, const unsigned x, const unsigned y, grs_bitmap &bm)
787
{
788
        hud_bitblt_free(hudctx.canvas, x, y, hudctx.xscale(bm.bm_w), hudctx.yscale(bm.bm_h), bm);
789
}
790
 
791
static void hud_bitblt(const hud_draw_context_hs hudctx, const unsigned x, const unsigned y, grs_bitmap &bm)
792
{
793
        hud_bitblt_scaled_xy(hudctx, hudctx.xscale(x), hudctx.yscale(y), bm);
794
}
795
 
796
}
797
 
798
}
799
 
800
namespace dsx {
801
 
802
namespace {
803
 
804
#if defined(DXX_BUILD_DESCENT_I)
805
#define hud_gauge_bitblt_draw_context   hud_draw_context_hs
806
#elif defined(DXX_BUILD_DESCENT_II)
807
#define hud_gauge_bitblt_draw_context   hud_draw_context_hs_mr
808
#endif
809
static void hud_gauge_bitblt(const hud_gauge_bitblt_draw_context hudctx, const unsigned x, const unsigned y, const unsigned gauge)
810
{
811
#if defined(DXX_BUILD_DESCENT_II)
812
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
813
#endif
814
        PAGE_IN_GAUGE(gauge, multires_gauge_graphic);
815
        hud_bitblt(hudctx, x, y, GameBitmaps[GET_GAUGE_INDEX(gauge)]);
816
}
817
 
818
class draw_keys_state
819
{
820
        const hud_draw_context_hs_mr hudctx;
821
        const player_flags player_key_flags;
822
public:
823
        draw_keys_state(const hud_draw_context_hs_mr hc, const player_flags f) :
824
                hudctx(hc), player_key_flags(f)
825
        {
826
        }
827
        void draw_all_cockpit_keys();
828
        void draw_all_statusbar_keys();
829
protected:
830
        void draw_one_key(const unsigned x, const unsigned y, const unsigned gauge, const PLAYER_FLAG flag) const
831
        {
832
                hud_gauge_bitblt(hudctx, x, y, (player_key_flags & flag) ? gauge : (gauge + 3));
833
        }
834
};
835
 
836
}
837
 
838
}
839
 
840
static void hud_show_score(grs_canvas &canvas, const player_info &player_info)
841
{
842
        char    score_str[20];
843
 
844
        if (HUD_toolong)
845
                return;
846
 
847
        const char *label;
848
        int value;
849
        if ( ((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP)) ) {
850
                label = TXT_KILLS;
851
                value = player_info.net_kills_total;
852
        } else {
853
                label = TXT_SCORE;
854
                value = player_info.mission.score;
855
        }
856
        snprintf(score_str, sizeof(score_str), "%s: %5d", label, value);
857
 
858
        if (Color_0_31_0 == -1)
859
                Color_0_31_0 = BM_XRGB(0,31,0);
860
        gr_set_fontcolor(canvas, Color_0_31_0, -1);
861
 
862
        int     w, h;
863
        auto &game_font = *GAME_FONT;
864
        gr_get_string_size(game_font, score_str, &w, &h, nullptr);
865
        gr_string(canvas, game_font, canvas.cv_bitmap.bm_w - w - FSPACX(1), FSPACY(1), score_str, w, h);
866
}
867
 
868
static void hud_show_timer_count(grs_canvas &canvas)
869
{
870
        auto &LevelUniqueControlCenterState = LevelUniqueObjectState.ControlCenterState;
871
 
872
        if (HUD_toolong)
873
                return;
874
 
875
        if (!(Game_mode & GM_NETWORK))
876
                return;
877
 
878
        if (!Netgame.PlayTimeAllowed.count())
879
                return;
880
 
881
        if (LevelUniqueControlCenterState.Control_center_destroyed)
882
                return;
883
 
884
        if (Netgame.PlayTimeAllowed < ThisLevelTime)
885
                return;
886
 
887
        const auto TicksUntilPlayTimeAllowedElapses = Netgame.PlayTimeAllowed - ThisLevelTime;
888
        const auto SecondsUntilPlayTimeAllowedElapses = f2i(TicksUntilPlayTimeAllowedElapses.count());
889
        if (SecondsUntilPlayTimeAllowedElapses >= 0)
890
        {
891
                if (Color_0_31_0 == -1)
892
                        Color_0_31_0 = BM_XRGB(0,31,0);
893
 
894
                gr_set_fontcolor(canvas, Color_0_31_0, -1);
895
 
896
                        char score_str[20];
897
                        snprintf(score_str, sizeof(score_str), "T - %5d", SecondsUntilPlayTimeAllowedElapses + 1);
898
                        int w, h;
899
                        gr_get_string_size(*canvas.cv_font, score_str, &w, &h, nullptr);
900
                        gr_string(canvas, *canvas.cv_font, canvas.cv_bitmap.bm_w - w - FSPACX(12), LINE_SPACING(*canvas.cv_font, *GAME_FONT) + FSPACY(1), score_str, w, h);
901
        }
902
}
903
 
904
static void hud_show_score_added(grs_canvas &canvas)
905
{
906
        int     color;
907
 
908
        if ( (Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP) )
909
                return;
910
 
911
        if (score_display == 0)
912
                return;
913
 
914
        score_time -= FrameTime;
915
        if (score_time > 0) {
916
                color = f2i(score_time * 20) + 12;
917
 
918
                if (color < 10) color = 12;
919
                if (color > 31) color = 30;
920
 
921
                color = color - (color % 4);
922
 
923
                char score_buf[32];
924
                const auto score_str = cheats.enabled
925
                        ? TXT_CHEATER
926
                        : (snprintf(score_buf, sizeof(score_buf), "%5d", score_display), score_buf);
927
 
928
                gr_set_fontcolor(canvas, BM_XRGB(0, color, 0), -1);
929
                int w, h;
930
                auto &game_font = *GAME_FONT;
931
                gr_get_string_size(game_font, score_str, &w, &h, nullptr);
932
                gr_string(canvas, game_font, canvas.cv_bitmap.bm_w - w - FSPACX(PlayerCfg.CockpitMode[1] == CM_FULL_SCREEN ? 1 : 12), LINE_SPACING(game_font, game_font) + FSPACY(1), score_str, w, h);
933
        } else {
934
                score_time = 0;
935
                score_display = 0;
936
        }
937
}
938
 
939
static void sb_show_score(const hud_draw_context_hs_mr hudctx, const player_info &player_info)
940
{
941
        char    score_str[20];
942
 
943
        auto &canvas = hudctx.canvas;
944
        gr_set_fontcolor(canvas, BM_XRGB(0, 20, 0), -1);
945
 
946
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
947
        const auto y = hudctx.yscale(SB_SCORE_Y);
948
        auto &game_font = *GAME_FONT;
949
        const auto is_multiplayer_non_cooperative = (Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP);
950
        gr_printf(canvas, game_font, hudctx.xscale(SB_SCORE_LABEL_X), y, "%s:", is_multiplayer_non_cooperative ? TXT_KILLS : TXT_SCORE);
951
 
952
        snprintf(score_str, sizeof(score_str), "%5d",
953
                        is_multiplayer_non_cooperative
954
                        ? player_info.net_kills_total
955
                        : (gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1), player_info.mission.score));
956
        int     w, h;
957
        gr_get_string_size(game_font, score_str, &w, &h, nullptr);
958
 
959
        const auto scaled_score_right = hudctx.xscale(SB_SCORE_RIGHT);
960
        const auto x = scaled_score_right - w - FSPACX(1);
961
 
962
        //erase old score
963
        const uint8_t color = BM_XRGB(0, 0, 0);
964
        gr_rect(canvas, x, y, scaled_score_right, y + LINE_SPACING(game_font, game_font), color);
965
 
966
        gr_string(canvas, game_font, x, y, score_str, w, h);
967
}
968
 
969
static void sb_show_score_added(const hud_draw_context_hs_mr hudctx)
970
{
971
        static int x;
972
        static  int last_score_display = -1;
973
 
974
        if ( (Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP) )
975
                return;
976
 
977
        if (score_display == 0)
978
                return;
979
 
980
        auto &canvas = hudctx.canvas;
981
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
982
 
983
        score_time -= FrameTime;
984
        if (score_time > 0) {
985
                if (score_display != last_score_display)
986
                        last_score_display = score_display;
987
 
988
                int     color;
989
                color = f2i(score_time * 20) + 10;
990
 
991
                if (color < 10) color = 10;
992
                if (color > 31) color = 31;
993
 
994
                char score_buf[32];
995
                const auto score_str = cheats.enabled
996
                        ? TXT_CHEATER
997
                        : (snprintf(score_buf, sizeof(score_buf), "%5d", score_display), score_buf);
998
 
999
                int w, h;
1000
                auto &game_font = *GAME_FONT;
1001
                gr_get_string_size(game_font, score_str, &w, &h, nullptr);
1002
                x = hudctx.xscale(SB_SCORE_ADDED_RIGHT) - w - FSPACX(1);
1003
                gr_set_fontcolor(canvas, BM_XRGB(0, color, 0), -1);
1004
                gr_string(canvas, game_font, x, hudctx.yscale(SB_SCORE_ADDED_Y), score_str, w, h);
1005
        } else {
1006
                //erase old score
1007
                const uint8_t color = BM_XRGB(0, 0, 0);
1008
                const auto scaled_score_y = hudctx.yscale(SB_SCORE_ADDED_Y);
1009
                gr_rect(canvas, x, scaled_score_y, hudctx.xscale(SB_SCORE_ADDED_RIGHT), scaled_score_y + LINE_SPACING(*canvas.cv_font, *GAME_FONT), color);
1010
                score_time = 0;
1011
                score_display = 0;
1012
        }
1013
}
1014
 
1015
namespace dsx {
1016
 
1017
//      -----------------------------------------------------------------------------
1018
void play_homing_warning(const player_info &player_info)
1019
{
1020
        fix beep_delay;
1021
        static fix64 Last_warning_beep_time = 0; // Time we last played homing missile warning beep.
1022
 
1023
        if (Endlevel_sequence || Player_dead_state != player_dead_state::no)
1024
                return;
1025
 
1026
        const auto homing_object_dist = player_info.homing_object_dist;
1027
        if (homing_object_dist >= 0) {
1028
                beep_delay = homing_object_dist / 128;
1029
                if (beep_delay > F1_0)
1030
                        beep_delay = F1_0;
1031
                else if (beep_delay < F1_0/8)
1032
                        beep_delay = F1_0/8;
1033
 
1034
                if (GameTime64 - Last_warning_beep_time > beep_delay/2 || Last_warning_beep_time > GameTime64) {
1035
                        digi_play_sample( SOUND_HOMING_WARNING, F1_0 );
1036
                        Last_warning_beep_time = GameTime64;
1037
                }
1038
        }
1039
}
1040
 
1041
}
1042
 
1043
//      -----------------------------------------------------------------------------
1044
static void show_homing_warning(const hud_draw_context_hs_mr hudctx, const int homing_object_dist)
1045
{
1046
        unsigned gauge;
1047
        if (Endlevel_sequence)
1048
        {
1049
                gauge = GAUGE_HOMING_WARNING_OFF;
1050
        }
1051
        else
1052
        {
1053
                gauge = ((GameTime64 & 0x4000) && homing_object_dist >= 0)
1054
                        ? GAUGE_HOMING_WARNING_ON
1055
                        : GAUGE_HOMING_WARNING_OFF;
1056
        }
1057
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
1058
        hud_gauge_bitblt(hudctx, HOMING_WARNING_X, HOMING_WARNING_Y, gauge);
1059
}
1060
 
1061
static void hud_show_homing_warning(grs_canvas &canvas, const int homing_object_dist)
1062
{
1063
        if (homing_object_dist >= 0)
1064
        {
1065
                if (GameTime64 & 0x4000) {
1066
                        gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
1067
                        auto &game_font = *GAME_FONT;
1068
                        gr_string(canvas, game_font, 0x8000, canvas.cv_bitmap.bm_h - LINE_SPACING(*canvas.cv_font, *GAME_FONT), TXT_LOCK);
1069
                }
1070
        }
1071
}
1072
 
1073
static void hud_show_keys(const hud_draw_context_mr hudctx, const hud_ar_scale_float hud_scale_ar, const player_info &player_info)
1074
{
1075
        const auto player_key_flags = player_info.powerup_flags;
1076
        if (!(player_key_flags & (PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_GOLD_KEY | PLAYER_FLAGS_RED_KEY)))
1077
                return;
1078
        class gauge_key
1079
        {
1080
                grs_bitmap *const bm;
1081
        public:
1082
                gauge_key(const unsigned key_icon, const local_multires_gauge_graphic multires_gauge_graphic) :
1083
                        bm(&GameBitmaps[static_cast<void>(multires_gauge_graphic), PAGE_IN_GAUGE(key_icon, multires_gauge_graphic), GET_GAUGE_INDEX(key_icon)])
1084
                {
1085
                }
1086
                grs_bitmap *operator->() const
1087
                {
1088
                        return bm;
1089
                }
1090
                operator grs_bitmap &() const
1091
                {
1092
                        return *bm;
1093
                }
1094
        };
1095
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
1096
        const gauge_key blue(KEY_ICON_BLUE, multires_gauge_graphic);
1097
        const unsigned y = hud_scale_ar(GameBitmaps[ GET_GAUGE_INDEX(GAUGE_LIVES) ].bm_h + 2) + FSPACY(1);
1098
 
1099
        const unsigned blue_bitmap_width = blue->bm_w;
1100
        const auto &&fspacx2 = FSPACX(2);
1101
        auto &canvas = hudctx.canvas;
1102
        if (player_key_flags & PLAYER_FLAGS_BLUE_KEY)
1103
                hud_bitblt_free(canvas, fspacx2, y, hud_scale_ar(blue_bitmap_width), hud_scale_ar(blue->bm_h), blue);
1104
 
1105
        if (!(player_key_flags & (PLAYER_FLAGS_GOLD_KEY | PLAYER_FLAGS_RED_KEY)))
1106
                return;
1107
        const gauge_key yellow(KEY_ICON_YELLOW, multires_gauge_graphic);
1108
        const unsigned yellow_bitmap_width = yellow->bm_w;
1109
        if (player_key_flags & PLAYER_FLAGS_GOLD_KEY)
1110
                hud_bitblt_free(canvas, fspacx2 + hud_scale_ar(blue_bitmap_width + 3), y, hud_scale_ar(yellow_bitmap_width), hud_scale_ar(yellow->bm_h), yellow);
1111
 
1112
        if (player_key_flags & PLAYER_FLAGS_RED_KEY)
1113
        {
1114
                const gauge_key red(KEY_ICON_RED, multires_gauge_graphic);
1115
                hud_bitblt_free(canvas, fspacx2 + hud_scale_ar(blue_bitmap_width + yellow_bitmap_width + 6), y, hud_scale_ar(red->bm_w), hud_scale_ar(red->bm_h), red);
1116
        }
1117
}
1118
 
1119
#if defined(DXX_BUILD_DESCENT_II)
1120
static void hud_show_orbs(grs_canvas &canvas, const player_info &player_info, const local_multires_gauge_graphic multires_gauge_graphic)
1121
{
1122
        if (game_mode_hoard()) {
1123
                const auto &&fspacy1 = FSPACY(1);
1124
                int x, y = LINE_SPACING(*canvas.cv_font, *GAME_FONT) + fspacy1;
1125
                const auto &&hud_scale_ar = HUD_SCALE_AR(grd_curscreen->get_screen_width(), grd_curscreen->get_screen_height(), multires_gauge_graphic);
1126
                if (PlayerCfg.CockpitMode[1] == CM_FULL_COCKPIT) {
1127
                        x = (SWIDTH/18);
1128
                }
1129
                else
1130
                {
1131
                        x = FSPACX(2);
1132
                if (PlayerCfg.CockpitMode[1] == CM_STATUS_BAR) {
1133
                }
1134
                else if (PlayerCfg.CockpitMode[1] == CM_FULL_SCREEN) {
1135
                        y = hud_scale_ar(GameBitmaps[ GET_GAUGE_INDEX(GAUGE_LIVES) ].bm_h + GameBitmaps[ GET_GAUGE_INDEX(KEY_ICON_RED) ].bm_h + 4) + fspacy1;
1136
                }
1137
                else
1138
                {
1139
                        Int3();         //what sort of cockpit?
1140
                        return;
1141
                }
1142
                }
1143
 
1144
                gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
1145
                auto &bm = Orb_icons[multires_gauge_graphic.is_hires()];
1146
                const auto &&scaled_width = hud_scale_ar(bm.bm_w);
1147
                hud_bitblt_free(canvas, x, y, scaled_width, hud_scale_ar(bm.bm_h), bm);
1148
                gr_printf(canvas, *canvas.cv_font, x + scaled_width, y, " x %d", player_info.hoard.orbs);
1149
        }
1150
}
1151
 
1152
static void hud_show_flag(grs_canvas &canvas, const player_info &player_info, const local_multires_gauge_graphic multires_gauge_graphic)
1153
{
1154
        if (game_mode_capture_flag() && (player_info.powerup_flags & PLAYER_FLAGS_FLAG)) {
1155
                int x, y = GameBitmaps[ GET_GAUGE_INDEX(GAUGE_LIVES) ].bm_h + 2, icon;
1156
                const auto &&fspacy1 = FSPACY(1);
1157
                if (PlayerCfg.CockpitMode[1] == CM_FULL_COCKPIT) {
1158
                        x = (SWIDTH/10);
1159
                }
1160
                else
1161
                {
1162
                        x = FSPACX(2);
1163
                if (PlayerCfg.CockpitMode[1] == CM_STATUS_BAR) {
1164
                }
1165
                else if (PlayerCfg.CockpitMode[1] == CM_FULL_SCREEN) {
1166
                        y += GameBitmaps[GET_GAUGE_INDEX(KEY_ICON_RED)].bm_h + 2;
1167
                }
1168
                else
1169
                {
1170
                        Int3();         //what sort of cockpit?
1171
                        return;
1172
                }
1173
                }
1174
 
1175
                icon = (get_team(Player_num) == TEAM_BLUE)?FLAG_ICON_RED:FLAG_ICON_BLUE;
1176
                auto &bm = GameBitmaps[GET_GAUGE_INDEX(icon)];
1177
                PAGE_IN_GAUGE(icon, multires_gauge_graphic);
1178
                const auto &&hud_scale_ar = HUD_SCALE_AR(grd_curscreen->get_screen_width(), grd_curscreen->get_screen_height(), multires_gauge_graphic);
1179
                hud_bitblt_free(canvas, x, hud_scale_ar(y) + fspacy1, hud_scale_ar(bm.bm_w), hud_scale_ar(bm.bm_h), bm);
1180
        }
1181
}
1182
#endif
1183
 
1184
static void hud_show_energy(grs_canvas &canvas, const player_info &player_info, const grs_font &game_font, const unsigned current_y)
1185
{
1186
        auto &energy = player_info.energy;
1187
        if (PlayerCfg.HudMode == HudType::Standard || PlayerCfg.HudMode == HudType::Alternate1)
1188
        {
1189
                gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
1190
                gr_printf(canvas, game_font, FSPACX(1), current_y, "%s: %i", TXT_ENERGY, f2ir(energy));
1191
        }
1192
 
1193
        if (Newdemo_state == ND_STATE_RECORDING)
1194
                newdemo_record_player_energy(f2ir(energy));
1195
}
1196
 
1197
#if defined(DXX_BUILD_DESCENT_I)
1198
#define convert_1s(s)
1199
#elif defined(DXX_BUILD_DESCENT_II)
1200
static void hud_show_afterburner(grs_canvas &canvas, const player_info &player_info, const grs_font &game_font, const unsigned current_y)
1201
{
1202
        if (! (player_info.powerup_flags & PLAYER_FLAGS_AFTERBURNER))
1203
                return;         //don't draw if don't have
1204
 
1205
        gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
1206
        gr_printf(canvas, game_font, FSPACX(1), current_y, "burn: %d%%" , fixmul(Afterburner_charge, 100));
1207
 
1208
        if (Newdemo_state==ND_STATE_RECORDING )
1209
                newdemo_record_player_afterburner(Afterburner_charge);
1210
}
1211
 
1212
//convert '1' characters to special wide ones
1213
#define convert_1s(s) do {char *p=s; while ((p=strchr(p,'1')) != NULL) *p=(char)132;} while(0)
1214
#endif
1215
 
1216
static inline const char *SECONDARY_WEAPON_NAMES_VERY_SHORT(const unsigned u)
1217
{
1218
        switch(u)
1219
        {
1220
                default:
1221
                        Int3();
1222
                        DXX_BOOST_FALLTHROUGH;
1223
                case CONCUSSION_INDEX:  return TXT_CONCUSSION;
1224
                case HOMING_INDEX:              return TXT_HOMING;
1225
                case PROXIMITY_INDEX:   return TXT_PROXBOMB;
1226
                case SMART_INDEX:               return TXT_SMART;
1227
                case MEGA_INDEX:                return TXT_MEGA;
1228
#if defined(DXX_BUILD_DESCENT_II)
1229
                case SMISSILE1_INDEX:   return "Flash";
1230
                case GUIDED_INDEX:              return "Guided";
1231
                case SMART_MINE_INDEX:  return "SmrtMine";
1232
                case SMISSILE4_INDEX:   return "Mercury";
1233
                case SMISSILE5_INDEX:   return "Shaker";
1234
#endif
1235
        }
1236
}
1237
 
1238
namespace dsx {
1239
 
1240
static void show_bomb_count(grs_canvas &canvas, const player_info &player_info, const int x, const int y, const int bg_color, const int always_show, const int right_align)
1241
{
1242
#if defined(DXX_BUILD_DESCENT_I)
1243
        if (!PlayerCfg.BombGauge)
1244
                return;
1245
#endif
1246
 
1247
        const auto bomb = which_bomb();
1248
        int count = player_info.secondary_ammo[bomb];
1249
 
1250
        count = min(count,99);  //only have room for 2 digits - cheating give 200
1251
 
1252
        if (always_show && count == 0)          //no bombs, draw nothing on HUD
1253
                return;
1254
 
1255
        gr_set_fontcolor(canvas, count
1256
                ? (bomb == PROXIMITY_INDEX
1257
                        ? gr_find_closest_color(55, 0, 0)
1258
                        : BM_XRGB(59, 50, 21)
1259
                )
1260
                : bg_color,     //erase by drawing in background color
1261
                bg_color);
1262
 
1263
        char txt[5];
1264
        snprintf(txt, sizeof(txt), "B:%02d", count);
1265
        //convert to wide '1'
1266
        std::replace(&txt[2], &txt[4], '1', '\x84');
1267
 
1268
        int w, h;
1269
        gr_get_string_size(*canvas.cv_font, txt, &w, &h, nullptr);
1270
        gr_string(canvas, *canvas.cv_font, right_align ? x - w : x, y, txt, w, h);
1271
}
1272
}
1273
 
1274
static void draw_primary_ammo_info(const hud_draw_context_hs_mr hudctx, const unsigned ammo_count)
1275
{
1276
        int x, y;
1277
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
1278
        if (PlayerCfg.CockpitMode[1] == CM_STATUS_BAR)
1279
                x = SB_PRIMARY_AMMO_X, y = SB_PRIMARY_AMMO_Y;
1280
        else
1281
                x = PRIMARY_AMMO_X, y = PRIMARY_AMMO_Y;
1282
        draw_ammo_info(hudctx.canvas, hudctx.xscale(x), hudctx.yscale(y), ammo_count);
1283
}
1284
 
1285
namespace dcx {
1286
namespace {
1287
 
1288
enum weapon_type
1289
{
1290
        primary,
1291
        secondary,
1292
};
1293
 
1294
constexpr rgb_t hud_rgb_red = {40, 0, 0};
1295
constexpr rgb_t hud_rgb_green = {0, 30, 0};
1296
constexpr rgb_t hud_rgb_dimgreen = {0, 12, 0};
1297
constexpr rgb_t hud_rgb_gray = {6, 6, 6};
1298
}
1299
}
1300
 
1301
namespace dsx {
1302
#if defined(DXX_BUILD_DESCENT_II)
1303
constexpr rgb_t hud_rgb_yellow = {30, 30, 0};
1304
#endif
1305
 
1306
__attribute_warn_unused_result
1307
static rgb_t hud_get_primary_weapon_fontcolor(const player_info &player_info, const int consider_weapon)
1308
{
1309
        if (player_info.Primary_weapon == consider_weapon)
1310
                return hud_rgb_red;
1311
        else{
1312
                if (player_has_primary_weapon(player_info, consider_weapon).has_weapon())
1313
                {
1314
#if defined(DXX_BUILD_DESCENT_II)
1315
                        const auto is_super = (consider_weapon >= 5);
1316
                        const int base_weapon = is_super ? consider_weapon - 5 : consider_weapon;
1317
                        if (player_info.Primary_last_was_super & (1 << base_weapon))
1318
                        {
1319
                                if (is_super)
1320
                                        return hud_rgb_green;
1321
                                else
1322
                                        return hud_rgb_yellow;
1323
                        }
1324
                        else if (is_super)
1325
                                return hud_rgb_yellow;
1326
                        else
1327
#endif
1328
                                return hud_rgb_green;
1329
                }
1330
                else
1331
                        return hud_rgb_gray;
1332
        }
1333
}
1334
 
1335
static void hud_set_primary_weapon_fontcolor(const player_info &player_info, const unsigned consider_weapon, grs_canvas &canvas)
1336
{
1337
        auto rgb = hud_get_primary_weapon_fontcolor(player_info, consider_weapon);
1338
        gr_set_fontcolor(canvas, gr_find_closest_color(rgb.r, rgb.g, rgb.b), -1);
1339
}
1340
 
1341
__attribute_warn_unused_result
1342
static rgb_t hud_get_secondary_weapon_fontcolor(const player_info &player_info, const int consider_weapon)
1343
{
1344
        if (player_info.Secondary_weapon == consider_weapon)
1345
                return hud_rgb_red;
1346
        else{
1347
                if (player_info.secondary_ammo[consider_weapon])
1348
                {
1349
#if defined(DXX_BUILD_DESCENT_II)
1350
                        const auto is_super = (consider_weapon >= 5);
1351
                        const int base_weapon = is_super ? consider_weapon - 5 : consider_weapon;
1352
                        if (player_info.Secondary_last_was_super & (1 << base_weapon))
1353
                        {
1354
                                if (is_super)
1355
                                        return hud_rgb_green;
1356
                                else
1357
                                        return hud_rgb_yellow;
1358
                        }
1359
                        else if (is_super)
1360
                                return hud_rgb_yellow;
1361
                        else
1362
#endif
1363
                                return hud_rgb_green;
1364
                }
1365
                else
1366
                        return hud_rgb_dimgreen;
1367
        }
1368
}
1369
 
1370
static void hud_set_secondary_weapon_fontcolor(const player_info &player_info, const unsigned consider_weapon, grs_canvas &canvas)
1371
{
1372
        auto rgb = hud_get_secondary_weapon_fontcolor(player_info, consider_weapon);
1373
        gr_set_fontcolor(canvas, gr_find_closest_color(rgb.r, rgb.g, rgb.b), -1);
1374
}
1375
 
1376
__attribute_warn_unused_result
1377
static rgb_t hud_get_vulcan_ammo_fontcolor(const player_info &player_info, const unsigned has_weapon_uses_vulcan_ammo)
1378
{
1379
        if (weapon_index_uses_vulcan_ammo(player_info.Primary_weapon))
1380
                return hud_rgb_red;
1381
        else if (has_weapon_uses_vulcan_ammo)
1382
                return hud_rgb_green;
1383
        else
1384
                return hud_rgb_gray;
1385
}
1386
 
1387
static void hud_set_vulcan_ammo_fontcolor(const player_info &player_info, const unsigned has_weapon_uses_vulcan_ammo, grs_canvas &canvas)
1388
{
1389
        auto rgb = hud_get_vulcan_ammo_fontcolor(player_info, has_weapon_uses_vulcan_ammo);
1390
        gr_set_fontcolor(canvas, gr_find_closest_color(rgb.r, rgb.g, rgb.b), -1);
1391
}
1392
 
1393
static void hud_printf_vulcan_ammo(grs_canvas &canvas, const player_info &player_info, const int x, const int y)
1394
{
1395
        const unsigned primary_weapon_flags = player_info.primary_weapon_flags;
1396
        const auto vulcan_mask = HAS_VULCAN_FLAG;
1397
#if defined(DXX_BUILD_DESCENT_I)
1398
        const auto gauss_mask = vulcan_mask;
1399
#elif defined(DXX_BUILD_DESCENT_II)
1400
        const auto gauss_mask = HAS_GAUSS_FLAG;
1401
#endif
1402
        const auto fmt_vulcan_ammo = vulcan_ammo_scale(player_info.vulcan_ammo);
1403
        const unsigned has_weapon_uses_vulcan_ammo = (primary_weapon_flags & (gauss_mask | vulcan_mask));
1404
        if (!has_weapon_uses_vulcan_ammo && !fmt_vulcan_ammo)
1405
                return;
1406
        hud_set_vulcan_ammo_fontcolor(player_info, has_weapon_uses_vulcan_ammo, canvas);
1407
        const char c =
1408
#if defined(DXX_BUILD_DESCENT_II)
1409
                ((primary_weapon_flags & gauss_mask) && ((player_info.Primary_last_was_super & (1 << primary_weapon_index_t::VULCAN_INDEX)) || !(primary_weapon_flags & vulcan_mask)))
1410
                ? 'G'
1411
                :
1412
#endif
1413
                (primary_weapon_flags & vulcan_mask)
1414
                        ? 'V'
1415
                        : 'A'
1416
        ;
1417
        gr_printf(canvas, *canvas.cv_font, x, y, "%c:%u", c, fmt_vulcan_ammo);
1418
}
1419
 
1420
static void hud_show_primary_weapons_mode(grs_canvas &canvas, const player_info &player_info, const int vertical, const int orig_x, const int orig_y)
1421
{
1422
        int x=orig_x,y=orig_y;
1423
 
1424
        const auto &&line_spacing = LINE_SPACING(*canvas.cv_font, *GAME_FONT);
1425
        if (vertical){
1426
                y += line_spacing * 4;
1427
        }
1428
 
1429
        const auto &&fspacx = FSPACX();
1430
        const auto &&fspacx3 = fspacx(3);
1431
        const auto &&fspacy2 = FSPACY(2);
1432
        {
1433
                for (uint_fast32_t ui = 5; ui --;)
1434
                {
1435
                        const auto i = static_cast<primary_weapon_index_t>(ui);
1436
                        const char *txtweapon;
1437
                        char weapon_str[10];
1438
                        hud_set_primary_weapon_fontcolor(player_info, i, canvas);
1439
                        switch(i)
1440
                        {
1441
                                case primary_weapon_index_t::LASER_INDEX:
1442
                                        {
1443
                                                snprintf(weapon_str, sizeof(weapon_str), "%c%i", (player_info.powerup_flags & PLAYER_FLAGS_QUAD_LASERS) ? 'Q' : 'L', player_info.laser_level + 1);
1444
                                        txtweapon = weapon_str;
1445
                                        }
1446
                                        break;
1447
                                case primary_weapon_index_t::VULCAN_INDEX:
1448
                                        txtweapon = "V";
1449
                                        break;
1450
                                case primary_weapon_index_t::SPREADFIRE_INDEX:
1451
                                        txtweapon = "S";
1452
                                        break;
1453
                                case primary_weapon_index_t::PLASMA_INDEX:
1454
                                        txtweapon = "P";
1455
                                        break;
1456
                                case primary_weapon_index_t::FUSION_INDEX:
1457
                                        txtweapon = "F";
1458
                                        break;
1459
                                default:
1460
                                        continue;
1461
                        }
1462
                        int w, h;
1463
                        gr_get_string_size(*canvas.cv_font, txtweapon, &w, &h, nullptr);
1464
                        if (vertical){
1465
                                y -= h + fspacy2;
1466
                        }else
1467
                                x -= w + fspacx3;
1468
                        gr_string(canvas, *canvas.cv_font, x, y, txtweapon, w, h);
1469
                        if (i == primary_weapon_index_t::VULCAN_INDEX)
1470
                        {
1471
                                /*
1472
                                 * In Descent 1, this will always draw the ammo, but the
1473
                                 * position depends on fullscreen and, if in fullscreen,
1474
                                 * whether in vertical mode.
1475
                                 *
1476
                                 * In Descent 2, this will draw in non-fullscreen and in
1477
                                 * fullscreen non-vertical, but not in fullscreen
1478
                                 * vertical.  The fullscreen vertical case is handled
1479
                                 * specially in a large Descent2 block below.
1480
                                 */
1481
                                int vx, vy;
1482
                                if (PlayerCfg.CockpitMode[1] == CM_FULL_SCREEN ? (
1483
                                                vertical ? (
1484
#if defined(DXX_BUILD_DESCENT_I)
1485
                                                        vx = x, vy = y, true
1486
#else
1487
                                                        false
1488
#endif
1489
                                                ) : (
1490
                                                        vx = x, vy = y - line_spacing, true
1491
                                                )
1492
                                        ) : (
1493
                                                vx = x - (w + fspacx3), vy = y - ((h + fspacy2) * 2), true
1494
                                        )
1495
                                )
1496
                                        hud_printf_vulcan_ammo(canvas, player_info, vx, vy);
1497
                        }
1498
                }
1499
        }
1500
#if defined(DXX_BUILD_DESCENT_II)
1501
        x = orig_x;
1502
        y = orig_y;
1503
        if (vertical)
1504
        {
1505
                x += fspacx(15);
1506
                y += line_spacing * 4;
1507
        }
1508
        else
1509
        {
1510
                y += line_spacing;
1511
        }
1512
 
1513
        {
1514
                for (uint_fast32_t ui = 10; ui -- != 5;)
1515
                {
1516
                        const auto i = static_cast<primary_weapon_index_t>(ui);
1517
                        const char *txtweapon;
1518
                        char weapon_str[10];
1519
                        hud_set_primary_weapon_fontcolor(player_info, i, canvas);
1520
                        switch(i)
1521
                        {
1522
                                case primary_weapon_index_t::SUPER_LASER_INDEX:
1523
                                        txtweapon = " ";
1524
                                        break;
1525
                                case primary_weapon_index_t::GAUSS_INDEX:
1526
                                        txtweapon = "G";
1527
                                        break;
1528
                                case primary_weapon_index_t::HELIX_INDEX:
1529
                                        txtweapon = "H";
1530
                                        break;
1531
                                case primary_weapon_index_t::PHOENIX_INDEX:
1532
                                        txtweapon = "P";
1533
                                        break;
1534
                                case primary_weapon_index_t::OMEGA_INDEX:
1535
                                        if (PlayerCfg.CockpitMode[1] == CM_FULL_SCREEN && (player_info.primary_weapon_flags & HAS_OMEGA_FLAG))
1536
                                        {
1537
                                                snprintf(weapon_str, sizeof(weapon_str), "O%3i", player_info.Omega_charge * 100 / MAX_OMEGA_CHARGE);
1538
                                                txtweapon = weapon_str;
1539
                                        }
1540
                                        else
1541
                                                txtweapon = "O";
1542
                                        break;
1543
                                default:
1544
                                        continue;
1545
                        }
1546
                        int w, h;
1547
                        gr_get_string_size(*canvas.cv_font, txtweapon, &w, &h, nullptr);
1548
                        if (vertical){
1549
                                y -= h + fspacy2;
1550
                        }else
1551
                                x -= w + fspacx3;
1552
                        if (i == primary_weapon_index_t::SUPER_LASER_INDEX)
1553
                        {
1554
                                if (vertical && (PlayerCfg.CockpitMode[1]==CM_FULL_SCREEN))
1555
                                        hud_printf_vulcan_ammo(canvas, player_info, x, y);
1556
                                continue;
1557
                        }
1558
                        gr_string(canvas, *canvas.cv_font, x, y, txtweapon, w, h);
1559
                }
1560
        }
1561
#endif
1562
        gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
1563
}
1564
 
1565
static void hud_show_secondary_weapons_mode(grs_canvas &canvas, const player_info &player_info, const unsigned vertical, const int orig_x, const int orig_y)
1566
{
1567
        int x=orig_x,y=orig_y;
1568
 
1569
        const auto &&line_spacing = LINE_SPACING(*canvas.cv_font, *GAME_FONT);
1570
        if (vertical){
1571
                y += line_spacing * 4;
1572
        }
1573
 
1574
        const auto &&fspacx = FSPACX();
1575
        const auto &&fspacx3 = fspacx(3);
1576
        const auto &&fspacy2 = FSPACY(2);
1577
        auto &secondary_ammo = player_info.secondary_ammo;
1578
        {
1579
                for (uint_fast32_t ui = 5; ui --;)
1580
                {
1581
                        const auto i = static_cast<secondary_weapon_index_t>(ui);
1582
                        char weapon_str[10];
1583
                        hud_set_secondary_weapon_fontcolor(player_info, i, canvas);
1584
                        snprintf(weapon_str, sizeof(weapon_str), "%i", secondary_ammo[i]);
1585
                        int w, h;
1586
                        gr_get_string_size(*canvas.cv_font, weapon_str, &w, &h, nullptr);
1587
                        if (vertical){
1588
                                y -= h + fspacy2;
1589
                        }else
1590
                                x -= w + fspacx3;
1591
                        gr_string(canvas, *canvas.cv_font, x, y, weapon_str, w, h);
1592
                }
1593
        }
1594
 
1595
#if defined(DXX_BUILD_DESCENT_II)
1596
        x = orig_x;
1597
        y = orig_y;
1598
        if (vertical)
1599
        {
1600
                x += fspacx(15);
1601
                y += line_spacing * 4;
1602
        }
1603
        else
1604
        {
1605
                y += line_spacing;
1606
        }
1607
 
1608
        {
1609
                for (uint_fast32_t ui = 10; ui -- != 5;)
1610
                {
1611
                        const auto i = static_cast<secondary_weapon_index_t>(ui);
1612
                        char weapon_str[10];
1613
                        hud_set_secondary_weapon_fontcolor(player_info, i, canvas);
1614
                        snprintf(weapon_str, sizeof(weapon_str), "%u", secondary_ammo[i]);
1615
                        int w, h;
1616
                        gr_get_string_size(*canvas.cv_font, weapon_str, &w, &h, nullptr);
1617
                        if (vertical){
1618
                                y -= h + fspacy2;
1619
                        }else
1620
                                x -= w + fspacx3;
1621
                        gr_string(canvas, *canvas.cv_font, x, y, weapon_str, w, h);
1622
                }
1623
        }
1624
#endif
1625
        gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
1626
}
1627
 
1628
static void hud_show_weapons(grs_canvas &canvas, const object &plrobj, const grs_font &game_font)
1629
{
1630
        auto &player_info = plrobj.ctype.player_info;
1631
        int     y;
1632
        const char      *weapon_name;
1633
        char    weapon_str[32];
1634
 
1635
        gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
1636
 
1637
        y = canvas.cv_bitmap.bm_h;
1638
 
1639
        const auto &&line_spacing = LINE_SPACING(game_font, game_font);
1640
        if (Game_mode & GM_MULTI)
1641
                y -= line_spacing * 4;
1642
 
1643
        if (PlayerCfg.HudMode == HudType::Alternate1)
1644
        {
1645
#if defined(DXX_BUILD_DESCENT_I)
1646
                constexpr unsigned multiplier = 1;
1647
#elif defined(DXX_BUILD_DESCENT_II)
1648
                constexpr unsigned multiplier = 2;
1649
#endif
1650
                hud_show_primary_weapons_mode(canvas, player_info, 0, canvas.cv_bitmap.bm_w, y - (line_spacing * 2 * multiplier));
1651
                hud_show_secondary_weapons_mode(canvas, player_info, 0, canvas.cv_bitmap.bm_w, y - (line_spacing * multiplier));
1652
                return;
1653
        }
1654
        const auto &&fspacx = FSPACX();
1655
        if (PlayerCfg.HudMode == HudType::Alternate2)
1656
        {
1657
                int x1,x2;
1658
                int w;
1659
                gr_get_string_size(game_font, "V1000", &w, nullptr, nullptr);
1660
                gr_get_string_size(game_font, "0 ", &x2, nullptr, nullptr);
1661
                y = canvas.cv_bitmap.bm_h / 1.75;
1662
                x1 = canvas.cv_bitmap.bm_w / 2.1 - (fspacx(40) + w);
1663
                x2 = canvas.cv_bitmap.bm_w / 1.9 + (fspacx(42) + x2);
1664
                hud_show_primary_weapons_mode(canvas, player_info, 1, x1, y);
1665
                hud_show_secondary_weapons_mode(canvas, player_info, 1, x2, y);
1666
                gr_set_fontcolor(canvas, BM_XRGB(14, 14, 23), -1);
1667
                gr_printf(canvas, game_font, x2, y - (line_spacing * 4), "%i", f2ir(plrobj.shields));
1668
                gr_set_fontcolor(canvas, BM_XRGB(25, 18, 6), -1);
1669
                gr_printf(canvas, game_font, x1, y - (line_spacing * 4), "%i", f2ir(player_info.energy));
1670
        }
1671
        else
1672
        {
1673
                const char *disp_primary_weapon_name;
1674
                const auto Primary_weapon = player_info.Primary_weapon;
1675
 
1676
                weapon_name = PRIMARY_WEAPON_NAMES_SHORT(Primary_weapon);
1677
                switch (Primary_weapon) {
1678
                        case primary_weapon_index_t::LASER_INDEX:
1679
                                {
1680
                                if (player_info.powerup_flags & PLAYER_FLAGS_QUAD_LASERS)
1681
                                        snprintf(weapon_str, sizeof(weapon_str), "%s %s %i", TXT_QUAD, weapon_name, player_info.laser_level + 1);
1682
                                else
1683
                                        snprintf(weapon_str, sizeof(weapon_str), "%s %i", weapon_name, player_info.laser_level + 1);
1684
                                }
1685
                                disp_primary_weapon_name = weapon_str;
1686
                                break;
1687
 
1688
                        case primary_weapon_index_t::VULCAN_INDEX:
1689
#if defined(DXX_BUILD_DESCENT_II)
1690
                        case primary_weapon_index_t::GAUSS_INDEX:
1691
#endif
1692
                                snprintf(weapon_str, sizeof(weapon_str), "%s: %u", weapon_name, vulcan_ammo_scale(player_info.vulcan_ammo));
1693
                                convert_1s(weapon_str);
1694
                                disp_primary_weapon_name = weapon_str;
1695
                                break;
1696
 
1697
                        case primary_weapon_index_t::SPREADFIRE_INDEX:
1698
                        case primary_weapon_index_t::PLASMA_INDEX:
1699
                        case primary_weapon_index_t::FUSION_INDEX:
1700
#if defined(DXX_BUILD_DESCENT_II)
1701
                        case primary_weapon_index_t::HELIX_INDEX:
1702
                        case primary_weapon_index_t::PHOENIX_INDEX:
1703
#endif
1704
                                disp_primary_weapon_name = weapon_name;
1705
                                break;
1706
#if defined(DXX_BUILD_DESCENT_II)
1707
                        case primary_weapon_index_t::OMEGA_INDEX:
1708
                                snprintf(weapon_str, sizeof(weapon_str), "%s: %03i", weapon_name, player_info.Omega_charge * 100 / MAX_OMEGA_CHARGE);
1709
                                convert_1s(weapon_str);
1710
                                disp_primary_weapon_name = weapon_str;
1711
                                break;
1712
 
1713
                        case primary_weapon_index_t::SUPER_LASER_INDEX: //no such thing as super laser
1714
#endif
1715
                        default:
1716
                                Int3();
1717
                                disp_primary_weapon_name = "";
1718
                                break;
1719
                }
1720
 
1721
                int     w, h;
1722
                gr_get_string_size(game_font, disp_primary_weapon_name, &w, &h, nullptr);
1723
                const auto &&bmwx = canvas.cv_bitmap.bm_w - fspacx(1);
1724
                gr_string(canvas, game_font, bmwx - w, y - (line_spacing * 2), disp_primary_weapon_name, w, h);
1725
                const char *disp_secondary_weapon_name;
1726
 
1727
                auto &Secondary_weapon = player_info.Secondary_weapon;
1728
                disp_secondary_weapon_name = SECONDARY_WEAPON_NAMES_VERY_SHORT(Secondary_weapon);
1729
 
1730
                snprintf(weapon_str, sizeof(weapon_str), "%s %u", disp_secondary_weapon_name, player_info.secondary_ammo[Secondary_weapon]);
1731
                gr_get_string_size(game_font, weapon_str, &w, &h, nullptr);
1732
                gr_string(canvas, game_font, bmwx - w, y - line_spacing, weapon_str, w, h);
1733
 
1734
                show_bomb_count(canvas, player_info, bmwx, y - (line_spacing * 3), -1, 1, 1);
1735
        }
1736
}
1737
}
1738
 
1739
static void hud_show_cloak_invuln(grs_canvas &canvas, const player_flags player_flags, const fix64 cloak_time, const fix64 invulnerable_time, const unsigned base_y)
1740
{
1741
        if (!(player_flags & (PLAYER_FLAGS_CLOAKED | PLAYER_FLAGS_INVULNERABLE)))
1742
                return;
1743
        gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
1744
        const auto &&line_spacing = LINE_SPACING(*canvas.cv_font, *GAME_FONT);
1745
        const auto gametime64 = GameTime64;
1746
        const auto &&fspacx1 = FSPACX(1);
1747
 
1748
        const auto cloak_invul_timer = show_cloak_invul_timer();
1749
        const auto a = [&](const fix64 effect_end, int y, const char *txt) {
1750
                if (cloak_invul_timer)
1751
                        gr_printf(canvas, *canvas.cv_font, fspacx1, y, "%s: %lu", txt, static_cast<unsigned long>(effect_end / F1_0));
1752
                else
1753
                        gr_string(canvas, *canvas.cv_font, fspacx1, y, txt);
1754
        };
1755
 
1756
        if (player_flags & PLAYER_FLAGS_CLOAKED)
1757
        {
1758
                const fix64 effect_end = cloak_time + CLOAK_TIME_MAX - gametime64;
1759
                if (effect_end > F1_0*3 || gametime64 & 0x8000)
1760
                {
1761
                        a(effect_end, base_y, TXT_CLOAKED);
1762
                }
1763
        }
1764
 
1765
        if (player_flags & PLAYER_FLAGS_INVULNERABLE)
1766
        {
1767
                const fix64 effect_end = invulnerable_time + INVULNERABLE_TIME_MAX - gametime64;
1768
                if (effect_end > F1_0*4 || gametime64 & 0x8000)
1769
                {
1770
                        a(effect_end, base_y - line_spacing, TXT_INVULNERABLE);
1771
                }
1772
        }
1773
}
1774
 
1775
static void hud_show_shield(grs_canvas &canvas, const object &plrobj, const grs_font &game_font, const unsigned current_y)
1776
{
1777
        if (PlayerCfg.HudMode == HudType::Standard || PlayerCfg.HudMode == HudType::Alternate1)
1778
        {
1779
                gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
1780
 
1781
                const auto shields = plrobj.shields;
1782
                gr_printf(canvas, game_font, FSPACX(1), current_y, "%s: %i", TXT_SHIELD, shields >= 0 ? f2ir(shields) : 0);
1783
        }
1784
 
1785
        if (Newdemo_state==ND_STATE_RECORDING )
1786
                newdemo_record_player_shields(f2ir(plrobj.shields));
1787
}
1788
 
1789
//draw the icons for number of lives
1790
static void hud_show_lives(const hud_draw_context_hs_mr hudctx, const hud_ar_scale_float hud_scale_ar, const player_info &player_info)
1791
{
1792
        if (HUD_toolong)
1793
                return;
1794
        if (Newdemo_state == ND_STATE_PLAYBACK)
1795
                return;
1796
 
1797
        const int x = (PlayerCfg.CockpitMode[1] == CM_FULL_COCKPIT)
1798
                ? static_cast<int>(hudctx.xscale(7))
1799
                : static_cast<int>(FSPACX(2));
1800
 
1801
        auto &canvas = hudctx.canvas;
1802
        if (Game_mode & GM_MULTI) {
1803
                gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
1804
                auto &game_font = *GAME_FONT;
1805
                gr_printf(canvas, game_font, x, FSPACY(1), "%s: %d", TXT_DEATHS, player_info.net_killed_total);
1806
        }
1807
        else if (const uint16_t lives = get_local_player().lives - 1)
1808
        {
1809
                gr_set_curfont(canvas, GAME_FONT);
1810
                gr_set_fontcolor(canvas, BM_XRGB(0, 20, 0), -1);
1811
#if defined(DXX_BUILD_DESCENT_II)
1812
                auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
1813
#endif
1814
                PAGE_IN_GAUGE(GAUGE_LIVES, multires_gauge_graphic);
1815
                auto &bm = GameBitmaps[GET_GAUGE_INDEX(GAUGE_LIVES)];
1816
                const auto &&fspacy1 = FSPACY(1);
1817
                hud_bitblt_free(canvas, x, fspacy1, hud_scale_ar(bm.bm_w), hud_scale_ar(bm.bm_h), bm);
1818
                auto &game_font = *GAME_FONT;
1819
                gr_printf(canvas, game_font, hud_scale_ar(bm.bm_w) + x, fspacy1, " x %hu", lives);
1820
        }
1821
}
1822
 
1823
static void sb_show_lives(const hud_draw_context_hs_mr hudctx, const hud_ar_scale_float hud_scale_ar, const player_info &player_info)
1824
{
1825
        if (Newdemo_state == ND_STATE_PLAYBACK)
1826
                return;
1827
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
1828
        const auto y = SB_LIVES_Y;
1829
 
1830
        auto &canvas = hudctx.canvas;
1831
        gr_set_fontcolor(canvas, BM_XRGB(0, 20, 0), -1);
1832
        const auto scaled_y = hudctx.yscale(y);
1833
        auto &game_font = *GAME_FONT;
1834
        const auto is_multiplayer = (Game_mode & GM_MULTI);
1835
        gr_printf(canvas, game_font, hudctx.xscale(SB_LIVES_LABEL_X), scaled_y, "%s:", is_multiplayer ? TXT_DEATHS : TXT_LIVES);
1836
 
1837
        const uint8_t color = BM_XRGB(0,0,0);
1838
        const auto scaled_score_right = hudctx.xscale(SB_SCORE_RIGHT);
1839
        if (is_multiplayer)
1840
        {
1841
                char killed_str[20];
1842
                static std::array<int, 4> last_x{{SB_SCORE_RIGHT_L, SB_SCORE_RIGHT_L, SB_SCORE_RIGHT_H, SB_SCORE_RIGHT_H}};
1843
 
1844
                snprintf(killed_str, sizeof(killed_str), "%5d", player_info.net_killed_total);
1845
                int w, h;
1846
                gr_get_string_size(game_font, killed_str, &w, &h, nullptr);
1847
                const auto x = scaled_score_right - w - FSPACX(1);
1848
                gr_rect(canvas, std::exchange(last_x[multires_gauge_graphic.is_hires()], x), scaled_y, scaled_score_right, scaled_y + LINE_SPACING(game_font, game_font), color);
1849
                gr_string(canvas, game_font, x, scaled_y, killed_str, w, h);
1850
                return;
1851
        }
1852
 
1853
        const int x = SB_LIVES_X;
1854
        //erase old icons
1855
        auto &bm = GameBitmaps[GET_GAUGE_INDEX(GAUGE_LIVES)];
1856
        const auto scaled_x = hudctx.xscale(x);
1857
        gr_rect(canvas, scaled_x, scaled_y, scaled_score_right, hudctx.yscale(y + bm.bm_h), color);
1858
 
1859
        if (const uint16_t lives = get_local_player().lives - 1)
1860
        {
1861
                PAGE_IN_GAUGE(GAUGE_LIVES, multires_gauge_graphic);
1862
                const auto scaled_width = hud_scale_ar(bm.bm_w);
1863
                hud_bitblt_free(canvas, scaled_x, scaled_y, scaled_width, hud_scale_ar(bm.bm_h), bm);
1864
                gr_printf(canvas, game_font, scaled_x + scaled_width, scaled_y, " x %hu", lives);
1865
        }
1866
}
1867
 
1868
#ifndef RELEASE
1869
static void show_time(grs_canvas &canvas)
1870
{
1871
        auto &plr = get_local_player();
1872
        const unsigned secs = f2i(plr.time_level) % 60;
1873
        const unsigned mins = f2i(plr.time_level) / 60;
1874
 
1875
        if (Color_0_31_0 == -1)
1876
                Color_0_31_0 = BM_XRGB(0,31,0);
1877
        gr_set_fontcolor(canvas, Color_0_31_0, -1);
1878
        auto &game_font = *GAME_FONT;
1879
        gr_printf(canvas, game_font, FSPACX(2), (LINE_SPACING(*canvas.cv_font, *GAME_FONT) * 15), "%d:%02d", mins, secs);
1880
}
1881
#endif
1882
 
1883
#define EXTRA_SHIP_SCORE        50000           //get new ship every this many points
1884
 
1885
static void common_add_points_to_score(const int points, int &score)
1886
{
1887
        if (points == 0 || cheats.enabled)
1888
                return;
1889
 
1890
        if (Newdemo_state == ND_STATE_RECORDING)
1891
                newdemo_record_player_score(points);
1892
 
1893
        const auto prev_score = score;
1894
        score += points;
1895
 
1896
        if (Game_mode & GM_MULTI)
1897
                return;
1898
 
1899
        const auto current_ship_score = score / EXTRA_SHIP_SCORE;
1900
        const auto previous_ship_score = prev_score / EXTRA_SHIP_SCORE;
1901
        if (current_ship_score != previous_ship_score)
1902
        {
1903
                int snd;
1904
                get_local_player().lives += current_ship_score - previous_ship_score;
1905
                powerup_basic_str(20, 20, 20, 0, TXT_EXTRA_LIFE);
1906
                if ((snd=Powerup_info[POW_EXTRA_LIFE].hit_sound) > -1 )
1907
                        digi_play_sample( snd, F1_0 );
1908
        }
1909
}
1910
 
1911
namespace dsx {
1912
 
1913
void add_points_to_score(player_info &player_info, int points)
1914
{
1915
        if ((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_COOP))
1916
                return;
1917
        score_time += f1_0*2;
1918
        score_display += points;
1919
        if (score_time > f1_0*4) score_time = f1_0*4;
1920
 
1921
        common_add_points_to_score(points, player_info.mission.score);
1922
        if (Game_mode & GM_MULTI_COOP)
1923
                multi_send_score();
1924
}
1925
 
1926
/* This is only called in single player when the player is between
1927
 * levels.
1928
 */
1929
void add_bonus_points_to_score(player_info &player_info, int points)
1930
{
1931
        assert(!(Game_mode & GM_MULTI));
1932
        common_add_points_to_score(points, player_info.mission.score);
1933
}
1934
 
1935
}
1936
 
1937
// Decode cockpit bitmap to deccpt and add alpha fields to weapon boxes (as it should have always been) so we later can render sub bitmaps over the window canvases
1938
static void cockpit_decode_alpha(const hud_draw_context_mr hudctx, grs_bitmap *const bm)
1939
{
1940
        static const uint8_t *cur;
1941
        static uint16_t cur_w, cur_h;
1942
#ifndef DXX_MAX_COCKPIT_BITMAP_SIZE
1943
        /* 640 wide by 480 high should be enough for all bitmaps shipped
1944
         * with shareware or commercial data.
1945
         * Use a #define so that the value can be easily overridden at build
1946
         * time.
1947
         */
1948
#define DXX_MAX_COCKPIT_BITMAP_SIZE     (640 * 480)
1949
#endif
1950
 
1951
        const unsigned bm_h = bm->bm_h;
1952
        if (unlikely(!bm_h))
1953
                /* Invalid, but later code has undefined results if bm_h==0 */
1954
                return;
1955
        const unsigned bm_w = bm->bm_w;
1956
        // check if we processed this bitmap already
1957
        if (cur == bm->bm_data && cur_w == bm_w && cur_h == bm_h)
1958
                return;
1959
 
1960
        RAIIdmem<uint8_t[]> cockpitbuf;
1961
        MALLOC(cockpitbuf, uint8_t[], DXX_MAX_COCKPIT_BITMAP_SIZE);
1962
 
1963
        // decode the bitmap
1964
        if (bm->get_flag_mask(BM_FLAG_RLE))
1965
        {
1966
                if (!bm_rle_expand(*bm).loop(bm_w, bm_rle_expand_range(cockpitbuf.get(), cockpitbuf.get() + DXX_MAX_COCKPIT_BITMAP_SIZE)))
1967
                {
1968
                                /* Out of space.  Return without adjusting the bitmap.
1969
                                 * The result will look ugly, but run correctly.
1970
                                 */
1971
                                con_printf(CON_URGENT, __FILE__ ":%u: BUG: RLE-encoded bitmap with size %hux%hu exceeds decode buffer size %u", __LINE__, static_cast<uint16_t>(bm_w), static_cast<uint16_t>(bm_h), DXX_MAX_COCKPIT_BITMAP_SIZE);
1972
                                return;
1973
                }
1974
        }
1975
        else
1976
        {
1977
                const std::size_t len = bm_w * bm_h;
1978
                if (len > DXX_MAX_COCKPIT_BITMAP_SIZE)
1979
                {
1980
                        con_printf(CON_URGENT, __FILE__ ":%u: BUG: RLE-encoded bitmap with size %hux%hu exceeds decode buffer size %u", __LINE__, static_cast<uint16_t>(bm_w), static_cast<uint16_t>(bm_h), DXX_MAX_COCKPIT_BITMAP_SIZE);
1981
                        return;
1982
                }
1983
                memcpy(cockpitbuf.get(), bm->bm_data, len);
1984
        }
1985
 
1986
        // add alpha color to the pixels which are inside the window box spans
1987
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
1988
        const unsigned lower_y = ((multires_gauge_graphic.get(364, 151)));
1989
        unsigned i = bm_w * lower_y;
1990
        const auto fill_alpha_one_line = [&cockpitbuf](unsigned o, const d_gauge_span &s) {
1991
                std::fill_n(&cockpitbuf[o + s.l], s.r - s.l + 1, TRANSPARENCY_COLOR);
1992
        };
1993
        range_for (auto &s,
1994
                multires_gauge_graphic.is_hires()
1995
                ? make_range(weapon_windows_hires)
1996
                : make_range(weapon_windows_lowres)
1997
        )
1998
        {
1999
                fill_alpha_one_line(i, s.l);
2000
                fill_alpha_one_line(i, s.r);
2001
                i += bm_w;
2002
        }
2003
        /* deccpt is static because it must remain allocated.  It must
2004
         * remain allocated because WinBoxOverlay[*] refer to the data
2005
         * managed by deccpt, so destroying deccpt would leave WinBoxOverlay
2006
         * dangling.  For OpenGL, the texture would be dangling.  For
2007
         * SDL-only, the bm_data pointers would be dangling.
2008
         */
2009
#if DXX_USE_OGL
2010
        ogl_freebmtexture(*bm);
2011
        static grs_bitmap deccpt;
2012
        gr_init_bitmap(deccpt, bm_mode::linear, 0, 0, bm_w, bm_h, bm_w, cockpitbuf.get());
2013
#else
2014
        static grs_main_bitmap deccpt;
2015
        gr_init_main_bitmap(deccpt, bm_mode::linear, 0, 0, bm_w, bm_h, bm_w, std::move(cockpitbuf));
2016
#endif
2017
        gr_set_transparent(deccpt,1);
2018
#if DXX_USE_OGL
2019
        ogl_ubitmapm_cs(hudctx.canvas, 0, 0, -1, -1, deccpt, 255, F1_0); // render one time to init the texture
2020
#endif
2021
        WinBoxOverlay[0] = gr_create_sub_bitmap(deccpt,(PRIMARY_W_BOX_LEFT)-2,(PRIMARY_W_BOX_TOP)-2,(PRIMARY_W_BOX_RIGHT-PRIMARY_W_BOX_LEFT+4),(PRIMARY_W_BOX_BOT-PRIMARY_W_BOX_TOP+4));
2022
        WinBoxOverlay[1] = gr_create_sub_bitmap(deccpt,(SECONDARY_W_BOX_LEFT)-2,(SECONDARY_W_BOX_TOP)-2,(SECONDARY_W_BOX_RIGHT-SECONDARY_W_BOX_LEFT)+4,(SECONDARY_W_BOX_BOT-SECONDARY_W_BOX_TOP)+4);
2023
#if DXX_USE_OGL
2024
        /* The image has been copied to OpenGL as a texture.  The underlying
2025
         * main application memory will be freed at the end of the function.
2026
         * Clear bm_data to avoid leaving a dangling pointer.
2027
         */
2028
        deccpt.bm_data = nullptr;
2029
        WinBoxOverlay[0]->bm_data = nullptr;
2030
        WinBoxOverlay[1]->bm_data = nullptr;
2031
#endif
2032
 
2033
        cur = bm->get_bitmap_data();
2034
        cur_w = bm_w;
2035
        cur_h = bm_h;
2036
}
2037
 
2038
namespace dsx {
2039
static void draw_wbu_overlay(const hud_draw_context_hs_mr hudctx)
2040
{
2041
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2042
#if defined(DXX_BUILD_DESCENT_I)
2043
        unsigned cockpit_idx = PlayerCfg.CockpitMode[1];
2044
#elif defined(DXX_BUILD_DESCENT_II)
2045
        unsigned cockpit_idx = PlayerCfg.CockpitMode[1]+(multires_gauge_graphic.is_hires() ? (Num_cockpits / 2) : 0);
2046
#endif
2047
        PIGGY_PAGE_IN(cockpit_bitmap[cockpit_idx]);
2048
        grs_bitmap *bm = &GameBitmaps[cockpit_bitmap[cockpit_idx].index];
2049
 
2050
        cockpit_decode_alpha(hudctx, bm);
2051
 
2052
        /* The code that rendered the inset windows drew simple square
2053
         * boxes, which partially overwrote the frame surrounding the inset
2054
         * windows in the cockpit graphic.  These calls reapply the
2055
         * overwritten frame, while leaving untouched the portion that was
2056
         * supposed to be overwritten.
2057
         */
2058
        if (WinBoxOverlay[0])
2059
                hud_bitblt(hudctx, PRIMARY_W_BOX_LEFT - 2, PRIMARY_W_BOX_TOP - 2, *WinBoxOverlay[0].get());
2060
        if (WinBoxOverlay[1])
2061
                hud_bitblt(hudctx, SECONDARY_W_BOX_LEFT - 2, SECONDARY_W_BOX_TOP - 2, *WinBoxOverlay[1].get());
2062
}
2063
}
2064
 
2065
void close_gauges()
2066
{
2067
        WinBoxOverlay = {};
2068
}
2069
 
2070
namespace dsx {
2071
void init_gauges()
2072
{
2073
        old_weapon[0] = old_weapon[1] = -1;
2074
        old_laser_level = -1;
2075
#if defined(DXX_BUILD_DESCENT_II)
2076
        weapon_box_user[0] = weapon_box_user[1] = WBU_WEAPON;
2077
#endif
2078
}
2079
}
2080
 
2081
static void draw_energy_bar(const hud_draw_context_hs_mr hudctx, const int energy)
2082
{
2083
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2084
        const int not_energy = hudctx.xscale(multires_gauge_graphic.is_hires() ? (125 - (energy * 125) / 100) : (63 - (energy * 63) / 100));
2085
        const double aplitscale = static_cast<double>(hudctx.xscale(65) / hudctx.yscale(8)) / (65 / 8); //scale amplitude of energy bar to current resolution aspect
2086
 
2087
        // Draw left energy bar
2088
        hud_gauge_bitblt(hudctx, LEFT_ENERGY_GAUGE_X, LEFT_ENERGY_GAUGE_Y, GAUGE_ENERGY_LEFT);
2089
 
2090
        const auto color = BM_XRGB(0, 0, 0);
2091
 
2092
        if (energy < 100)
2093
        {
2094
                const auto xscale_energy_gauge_x = hudctx.xscale(LEFT_ENERGY_GAUGE_X);
2095
                const auto xscale_energy_gauge_w = hudctx.xscale(LEFT_ENERGY_GAUGE_W);
2096
                const auto xscale_energy_gauge_h2 = hudctx.xscale(LEFT_ENERGY_GAUGE_H - 2);
2097
                const auto yscale_energy_gauge_y = hudctx.yscale(LEFT_ENERGY_GAUGE_Y);
2098
                const auto yscale_energy_gauge_h = hudctx.yscale(LEFT_ENERGY_GAUGE_H);
2099
                for (unsigned y = 0; y < yscale_energy_gauge_h; ++y)
2100
                {
2101
                        const auto bound = xscale_energy_gauge_w - (y * aplitscale) / 3;
2102
                        const auto x1 = xscale_energy_gauge_h2 - y * aplitscale;
2103
                        const auto x2 = std::min(x1 + not_energy, bound);
2104
 
2105
                        if (x2 > x1)
2106
                        {
2107
                                const auto ly = i2f(y + yscale_energy_gauge_y);
2108
                                gr_uline(*grd_curcanv, i2f(x1 + xscale_energy_gauge_x), ly, i2f(x2 + xscale_energy_gauge_x), ly, color);
2109
                        }
2110
                }
2111
        }
2112
 
2113
        // Draw right energy bar
2114
        hud_gauge_bitblt(hudctx, RIGHT_ENERGY_GAUGE_X, RIGHT_ENERGY_GAUGE_Y, GAUGE_ENERGY_RIGHT);
2115
 
2116
        if (energy < 100)
2117
        {
2118
                const auto xscale_energy_gauge_x = hudctx.xscale(RIGHT_ENERGY_GAUGE_X);
2119
                const auto yscale_energy_gauge_y = hudctx.yscale(RIGHT_ENERGY_GAUGE_Y);
2120
                const auto yscale_energy_gauge_h = hudctx.yscale(RIGHT_ENERGY_GAUGE_H);
2121
                const auto xscale_right_energy = hudctx.xscale(RIGHT_ENERGY_GAUGE_W - RIGHT_ENERGY_GAUGE_H + 2);
2122
                for (unsigned y = 0; y < yscale_energy_gauge_h; ++y)
2123
                {
2124
                        const auto bound = (y * aplitscale) / 3;
2125
                        const auto x2 = xscale_right_energy + y * aplitscale;
2126
                        auto x1 = x2 - not_energy;
2127
 
2128
                        if (x1 < bound)
2129
                                x1 = bound;
2130
 
2131
                        if (x2 > x1)
2132
                        {
2133
                                const auto ly = i2f(y + yscale_energy_gauge_y);
2134
                                gr_uline(*grd_curcanv, i2f(x1 + xscale_energy_gauge_x), ly, i2f(x2 + xscale_energy_gauge_x), ly, color);
2135
                        }
2136
                }
2137
        }
2138
 
2139
        gr_set_default_canvas();
2140
}
2141
 
2142
#if defined(DXX_BUILD_DESCENT_II)
2143
static void draw_afterburner_bar(const hud_draw_context_hs_mr hudctx, const int afterburner)
2144
{
2145
        struct lr
2146
        {
2147
                uint8_t l, r;
2148
        };
2149
        static const std::array<lr, AFTERBURNER_GAUGE_H_L> afterburner_bar_table = {{
2150
                {3, 11},
2151
                {3, 11},
2152
                {3, 11},
2153
                {3, 11},
2154
                {3, 11},
2155
                {3, 11},
2156
                {2, 11},
2157
                {2, 10},
2158
                {2, 10},
2159
                {2, 10},
2160
                {2, 10},
2161
                {2, 10},
2162
                {2, 10},
2163
                {1, 10},
2164
                {1, 10},
2165
                {1, 10},
2166
                {1, 9},
2167
                {1, 9},
2168
                {1, 9},
2169
                {1, 9},
2170
                {0, 9},
2171
                {0, 9},
2172
                {0, 8},
2173
                {0, 8},
2174
                {0, 8},
2175
                {0, 8},
2176
                {1, 8},
2177
                {2, 8},
2178
                {3, 8},
2179
                {4, 8},
2180
                {5, 8},
2181
                {6, 7}
2182
        }};
2183
        static const std::array<lr, AFTERBURNER_GAUGE_H_H> afterburner_bar_table_hires = {{
2184
                {5, 20},
2185
                {5, 20},
2186
                {5, 19},
2187
                {5, 19},
2188
                {5, 19},
2189
                {5, 19},
2190
                {4, 19},
2191
                {4, 19},
2192
                {4, 19},
2193
                {4, 19},
2194
                {4, 19},
2195
                {4, 18},
2196
                {4, 18},
2197
                {4, 18},
2198
                {4, 18},
2199
                {3, 18},
2200
                {3, 18},
2201
                {3, 18},
2202
                {3, 18},
2203
                {3, 18},
2204
                {3, 18},
2205
                {3, 17},
2206
                {3, 17},
2207
                {2, 17},
2208
                {2, 17},
2209
                {2, 17},
2210
                {2, 17},
2211
                {2, 17},
2212
                {2, 17},
2213
                {2, 17},
2214
                {2, 17},
2215
                {2, 16},
2216
                {2, 16},
2217
                {1, 16},
2218
                {1, 16},
2219
                {1, 16},
2220
                {1, 16},
2221
                {1, 16},
2222
                {1, 16},
2223
                {1, 16},
2224
                {1, 16},
2225
                {1, 15},
2226
                {1, 15},
2227
                {1, 15},
2228
                {0, 15},
2229
                {0, 15},
2230
                {0, 15},
2231
                {0, 15},
2232
                {0, 15},
2233
                {0, 15},
2234
                {0, 14},
2235
                {0, 14},
2236
                {0, 14},
2237
                {1, 14},
2238
                {2, 14},
2239
                {3, 14},
2240
                {4, 14},
2241
                {5, 14},
2242
                {6, 13},
2243
                {7, 13},
2244
                {8, 13},
2245
                {9, 13},
2246
                {10, 13},
2247
                {11, 13},
2248
                {12, 13}
2249
        }};
2250
 
2251
        // Draw afterburner bar
2252
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2253
        const auto afterburner_gauge_x = AFTERBURNER_GAUGE_X;
2254
        const auto afterburner_gauge_y = AFTERBURNER_GAUGE_Y;
2255
        const auto &&table = multires_gauge_graphic.is_hires()
2256
                ? std::make_pair(afterburner_bar_table_hires.data(), afterburner_bar_table_hires.size())
2257
                : std::make_pair(afterburner_bar_table.data(), afterburner_bar_table.size());
2258
        hud_gauge_bitblt(hudctx, afterburner_gauge_x, afterburner_gauge_y, GAUGE_AFTERBURNER);
2259
        const unsigned not_afterburner = fixmul(f1_0 - afterburner, table.second);
2260
        if (not_afterburner > table.second)
2261
                return;
2262
        const uint8_t color = BM_XRGB(0, 0, 0);
2263
        const int base_top = hudctx.yscale(afterburner_gauge_y - 1);
2264
        const int base_bottom = hudctx.yscale(afterburner_gauge_y);
2265
        int y = 0;
2266
        range_for (auto &ab, unchecked_partial_range(table.first, not_afterburner))
2267
        {
2268
                const int left = hudctx.xscale(afterburner_gauge_x + ab.l);
2269
                const int right = hudctx.xscale(afterburner_gauge_x + ab.r + 1);
2270
                for (int i = hudctx.yscale(y), j = hudctx.yscale(++y); i < j; ++i)
2271
                {
2272
                        gr_rect(hudctx.canvas, left, base_top + i, right, base_bottom + i, color);
2273
                }
2274
        }
2275
}
2276
#endif
2277
 
2278
static void draw_shield_bar(const hud_draw_context_hs_mr hudctx, const int shield)
2279
{
2280
        int bm_num = shield>=100?9:(shield / 10);
2281
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2282
        hud_gauge_bitblt(hudctx, SHIELD_GAUGE_X, SHIELD_GAUGE_Y, GAUGE_SHIELDS + 9 - bm_num);
2283
}
2284
 
2285
static void show_cockpit_cloak_invul_timer(grs_canvas &canvas, const fix64 effect_end, const int y)
2286
{
2287
        char countdown[8];
2288
        int ow, oh;
2289
        snprintf(countdown, sizeof(countdown), "%lu", static_cast<unsigned long>(effect_end / F1_0));
2290
        gr_set_fontcolor(canvas, BM_XRGB(31, 31, 31), -1);
2291
        gr_get_string_size(*canvas.cv_font, countdown, &ow, &oh, nullptr);
2292
        const int x = grd_curscreen->get_screen_width() / (PlayerCfg.CockpitMode[1] == CM_STATUS_BAR
2293
                ? 2.266
2294
                : 1.951
2295
        );
2296
        gr_string(canvas, *canvas.cv_font, x - (ow / 2), y, countdown, ow, oh);
2297
}
2298
 
2299
#define CLOAK_FADE_WAIT_TIME  0x400
2300
 
2301
namespace dsx {
2302
 
2303
static void draw_player_ship(const hud_draw_context_hs_mr hudctx, const player_info &player_info, const int cloak_state, const int x, const int y)
2304
{
2305
        static fix cloak_fade_timer=0;
2306
        static int cloak_fade_value=GR_FADE_LEVELS-1;
2307
 
2308
        if (cloak_state)
2309
        {
2310
                static int step = 0;
2311
                const auto cloak_time = player_info.cloak_time;
2312
 
2313
                if (GameTime64 - cloak_time < F1_0)
2314
                {
2315
                        step = -2;
2316
                }
2317
                else if (cloak_time + CLOAK_TIME_MAX - GameTime64 <= F1_0*3)
2318
                {
2319
                        if (cloak_fade_value >= static_cast<signed>(GR_FADE_LEVELS-1))
2320
                        {
2321
                                step = -2;
2322
                        }
2323
                        else if (cloak_fade_value <= 0)
2324
                        {
2325
                                step = 2;
2326
                        }
2327
                }
2328
                else
2329
                {
2330
                        step = 0;
2331
                        cloak_fade_value = 0;
2332
                }
2333
 
2334
                cloak_fade_timer -= FrameTime;
2335
 
2336
                while (cloak_fade_timer < 0)
2337
                {
2338
                        cloak_fade_timer += CLOAK_FADE_WAIT_TIME;
2339
                        cloak_fade_value += step;
2340
                }
2341
 
2342
                if (cloak_fade_value > static_cast<signed>(GR_FADE_LEVELS-1))
2343
                        cloak_fade_value = (GR_FADE_LEVELS-1);
2344
                if (cloak_fade_value <= 0)
2345
                        cloak_fade_value = 0;
2346
        }
2347
        else
2348
        {
2349
                cloak_fade_timer = 0;
2350
                cloak_fade_value = GR_FADE_LEVELS-1;
2351
        }
2352
 
2353
        const auto color = get_player_or_team_color(Player_num);
2354
#if defined(DXX_BUILD_DESCENT_II)
2355
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2356
#endif
2357
        PAGE_IN_GAUGE(GAUGE_SHIPS+color, multires_gauge_graphic);
2358
        auto &bm = GameBitmaps[GET_GAUGE_INDEX(GAUGE_SHIPS+color)];
2359
        hud_bitblt(hudctx, x, y, bm);
2360
        auto &canvas = hudctx.canvas;
2361
        gr_settransblend(canvas, cloak_fade_value, gr_blend::normal);
2362
        gr_rect(canvas, hudctx.xscale(x - 3), hudctx.yscale(y - 3), hudctx.xscale(x + bm.bm_w + 3), hudctx.yscale(y + bm.bm_h + 3), 0);
2363
        gr_settransblend(canvas, GR_FADE_OFF, gr_blend::normal);
2364
        // Show Cloak Timer if enabled
2365
        if (cloak_fade_value < GR_FADE_LEVELS/2 && show_cloak_invul_timer())
2366
                show_cockpit_cloak_invul_timer(canvas, player_info.cloak_time + CLOAK_TIME_MAX - GameTime64, hudctx.yscale(y + (bm.bm_h / 2)));
2367
}
2368
 
2369
}
2370
 
2371
#define INV_FRAME_TIME  (f1_0/10)               //how long for each frame
2372
 
2373
namespace dcx {
2374
 
2375
static const char *get_gauge_width_string(const unsigned v)
2376
{
2377
        if (v > 199)
2378
                return "200";
2379
        return &"100"[(v > 99)
2380
                ? 0
2381
                : (v > 9) ? 1 : 2
2382
        ];
2383
}
2384
 
2385
}
2386
 
2387
static void draw_numerical_display(const draw_numerical_display_draw_context hudctx, const int shield, const int energy)
2388
{
2389
        auto &canvas = hudctx.canvas;
2390
#if !DXX_USE_OGL
2391
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2392
        hud_gauge_bitblt(hudctx, NUMERICAL_GAUGE_X, NUMERICAL_GAUGE_Y, GAUGE_NUMERICAL);
2393
#endif
2394
        // cockpit is not 100% geometric so we need to divide shield and energy X position by 1.951 which should be most accurate
2395
        // gr_get_string_size is used so we can get the numbers finally in the correct position with sw and ew
2396
        const int xb = grd_curscreen->get_screen_width() / 1.951;
2397
        auto &game_font = *GAME_FONT;
2398
        const auto a = [&canvas, &game_font, xb](int v, int y) {
2399
                int w;
2400
                gr_get_string_size(game_font, get_gauge_width_string(v), &w, nullptr, nullptr);
2401
                gr_printf(canvas, game_font, xb - (w / 2), y, "%d", v);
2402
        };
2403
        gr_set_fontcolor(canvas, BM_XRGB(14, 14, 23), -1);
2404
        const auto screen_height = grd_curscreen->get_screen_height();
2405
        a(shield, screen_height / 1.365);
2406
 
2407
        gr_set_fontcolor(canvas, BM_XRGB(25, 18, 6), -1);
2408
        a(energy, screen_height / 1.5);
2409
}
2410
 
2411
void draw_keys_state::draw_all_cockpit_keys()
2412
{
2413
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2414
        draw_one_key(GAUGE_BLUE_KEY_X, GAUGE_BLUE_KEY_Y, GAUGE_BLUE_KEY, PLAYER_FLAGS_BLUE_KEY);
2415
        draw_one_key(GAUGE_GOLD_KEY_X, GAUGE_GOLD_KEY_Y, GAUGE_GOLD_KEY, PLAYER_FLAGS_GOLD_KEY);
2416
        draw_one_key(GAUGE_RED_KEY_X, GAUGE_RED_KEY_Y, GAUGE_RED_KEY, PLAYER_FLAGS_RED_KEY);
2417
}
2418
 
2419
namespace dsx {
2420
 
2421
static void draw_weapon_info_sub(const hud_draw_context_hs_mr hudctx, const player_info &player_info, const int info_index, const gauge_box *const box, const int pic_x, const int pic_y, const char *const name, const int text_x, const int text_y)
2422
{
2423
        //clear the window
2424
        const uint8_t color = BM_XRGB(0, 0, 0);
2425
        {
2426
#if defined(DXX_BUILD_DESCENT_I)
2427
                constexpr unsigned bottom_bias = 1;
2428
#elif defined(DXX_BUILD_DESCENT_II)
2429
                constexpr unsigned bottom_bias = 0;
2430
#endif
2431
                gr_rect(hudctx.canvas, hudctx.xscale(box->left), hudctx.yscale(box->top), hudctx.xscale(box->right), hudctx.yscale(box->bot + bottom_bias), color);
2432
        }
2433
        const auto &picture =
2434
#if defined(DXX_BUILD_DESCENT_II)
2435
        // !SHAREWARE
2436
                (Piggy_hamfile_version >= 3 && hudctx.multires_gauge_graphic.is_hires()) ?
2437
                        Weapon_info[info_index].hires_picture :
2438
#endif
2439
                        Weapon_info[info_index].picture;
2440
        PIGGY_PAGE_IN(picture);
2441
        auto &bm = GameBitmaps[picture.index];
2442
 
2443
        hud_bitblt(hudctx, pic_x, pic_y, bm);
2444
 
2445
        if (PlayerCfg.HudMode == HudType::Standard)
2446
        {
2447
                auto &canvas = hudctx.canvas;
2448
                gr_set_fontcolor(canvas, BM_XRGB(0, 20, 0), -1);
2449
 
2450
                gr_string(canvas, *canvas.cv_font, text_x, text_y, name);
2451
 
2452
                //      For laser, show level and quadness
2453
#if defined(DXX_BUILD_DESCENT_I)
2454
                if (info_index == primary_weapon_index_t::LASER_INDEX)
2455
#elif defined(DXX_BUILD_DESCENT_II)
2456
                if (info_index == weapon_id_type::LASER_ID || info_index == weapon_id_type::SUPER_LASER_ID)
2457
#endif
2458
                {
2459
                        const auto &&line_spacing = LINE_SPACING(*canvas.cv_font, *GAME_FONT);
2460
                        gr_printf(canvas, *canvas.cv_font, text_x, text_y + line_spacing, "%s: %i", TXT_LVL, player_info.laser_level + 1);
2461
                        if (player_info.powerup_flags & PLAYER_FLAGS_QUAD_LASERS)
2462
                                gr_string(canvas, *canvas.cv_font, text_x, text_y + (line_spacing * 2), TXT_QUAD);
2463
                }
2464
        }
2465
}
2466
 
2467
static void draw_primary_weapon_info(const hud_draw_context_hs_mr hudctx, const player_info &player_info, const unsigned weapon_num, const unsigned laser_level)
2468
{
2469
#if defined(DXX_BUILD_DESCENT_I)
2470
        (void)laser_level;
2471
#endif
2472
        int x,y;
2473
 
2474
        {
2475
                const auto weapon_id = Primary_weapon_to_weapon_info[weapon_num];
2476
                const auto info_index =
2477
#if defined(DXX_BUILD_DESCENT_II)
2478
                        (weapon_id == weapon_id_type::LASER_ID && laser_level > MAX_LASER_LEVEL)
2479
                        ? weapon_id_type::SUPER_LASER_ID
2480
                        :
2481
#endif
2482
                        weapon_id;
2483
 
2484
                const gauge_box *box;
2485
                int pic_x, pic_y, text_x, text_y;
2486
                auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2487
                if (PlayerCfg.CockpitMode[1] == CM_STATUS_BAR)
2488
                {
2489
                        box = &gauge_boxes[SB_PRIMARY_BOX];
2490
                        pic_x = SB_PRIMARY_W_PIC_X;
2491
                        pic_y = SB_PRIMARY_W_PIC_Y;
2492
                        text_x = SB_PRIMARY_W_TEXT_X;
2493
                        text_y = SB_PRIMARY_W_TEXT_Y;
2494
                        x=SB_PRIMARY_AMMO_X;
2495
                        y=SB_PRIMARY_AMMO_Y;
2496
                }
2497
                else
2498
                {
2499
                        box = &gauge_boxes[COCKPIT_PRIMARY_BOX];
2500
                        pic_x = PRIMARY_W_PIC_X;
2501
                        pic_y = PRIMARY_W_PIC_Y;
2502
                        text_x = PRIMARY_W_TEXT_X;
2503
                        text_y = PRIMARY_W_TEXT_Y;
2504
                        x=PRIMARY_AMMO_X;
2505
                        y=PRIMARY_AMMO_Y;
2506
                }
2507
                draw_weapon_info_sub(hudctx, player_info, info_index, box, pic_x, pic_y, PRIMARY_WEAPON_NAMES_SHORT(weapon_num), hudctx.xscale(text_x), hudctx.yscale(text_y));
2508
                if (PlayerCfg.HudMode != HudType::Standard)
2509
                {
2510
#if defined(DXX_BUILD_DESCENT_II)
2511
                        if (weapon_box_user[0] == WBU_WEAPON)
2512
#endif
2513
                                hud_show_primary_weapons_mode(hudctx.canvas, player_info, 1, hudctx.xscale(x), hudctx.yscale(y));
2514
                }
2515
        }
2516
}
2517
 
2518
static void draw_secondary_weapon_info(const hud_draw_context_hs_mr hudctx, const player_info &player_info, const unsigned weapon_num)
2519
{
2520
        int x,y;
2521
        int info_index;
2522
 
2523
        {
2524
                info_index = Secondary_weapon_to_weapon_info[weapon_num];
2525
                const gauge_box *box;
2526
                int pic_x, pic_y, text_x, text_y;
2527
                auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2528
                if (PlayerCfg.CockpitMode[1] == CM_STATUS_BAR)
2529
                {
2530
                        box = &gauge_boxes[SB_SECONDARY_BOX];
2531
                        pic_x = SB_SECONDARY_W_PIC_X;
2532
                        pic_y = SB_SECONDARY_W_PIC_Y;
2533
                        text_x = SB_SECONDARY_W_TEXT_X;
2534
                        text_y = SB_SECONDARY_W_TEXT_Y;
2535
                        x=SB_SECONDARY_AMMO_X;
2536
                        y=SB_SECONDARY_AMMO_Y;
2537
                }
2538
                else
2539
                {
2540
                        box = &gauge_boxes[COCKPIT_SECONDARY_BOX];
2541
                        pic_x = SECONDARY_W_PIC_X;
2542
                        pic_y = SECONDARY_W_PIC_Y;
2543
                        text_x = SECONDARY_W_TEXT_X;
2544
                        text_y = SECONDARY_W_TEXT_Y;
2545
                        x=SECONDARY_AMMO_X;
2546
                        y=SECONDARY_AMMO_Y;
2547
                }
2548
                draw_weapon_info_sub(hudctx, player_info, info_index, box, pic_x, pic_y, SECONDARY_WEAPON_NAMES_SHORT(weapon_num), hudctx.xscale(text_x), hudctx.yscale(text_y));
2549
                if (PlayerCfg.HudMode != HudType::Standard)
2550
                {
2551
#if defined(DXX_BUILD_DESCENT_II)
2552
                        if (weapon_box_user[1] == WBU_WEAPON)
2553
#endif
2554
                                hud_show_secondary_weapons_mode(hudctx.canvas, player_info, 1, hudctx.xscale(x), hudctx.yscale(y));
2555
                }
2556
        }
2557
}
2558
 
2559
static void draw_weapon_info(const hud_draw_context_hs_mr hudctx, const player_info &player_info, const unsigned weapon_num, const unsigned laser_level, const weapon_type wt)
2560
{
2561
        if (wt == weapon_type::primary)
2562
                draw_primary_weapon_info(hudctx, player_info, weapon_num, laser_level);
2563
        else
2564
                draw_secondary_weapon_info(hudctx, player_info, weapon_num);
2565
}
2566
 
2567
}
2568
 
2569
static void draw_ammo_info(grs_canvas &canvas, const unsigned x, const unsigned y, const unsigned ammo_count)
2570
{
2571
        if (PlayerCfg.HudMode == HudType::Standard)
2572
        {
2573
                gr_set_fontcolor(canvas, BM_XRGB(20, 0, 0), -1);
2574
                gr_printf(canvas, *canvas.cv_font, x, y, "%03u", ammo_count);
2575
        }
2576
}
2577
 
2578
static void draw_secondary_ammo_info(const hud_draw_context_hs_mr hudctx, const unsigned ammo_count)
2579
{
2580
        int x, y;
2581
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2582
        if (PlayerCfg.CockpitMode[1] == CM_STATUS_BAR)
2583
                x = SB_SECONDARY_AMMO_X, y = SB_SECONDARY_AMMO_Y;
2584
        else
2585
                x = SECONDARY_AMMO_X, y = SECONDARY_AMMO_Y;
2586
        draw_ammo_info(hudctx.canvas, hudctx.xscale(x), hudctx.yscale(y), ammo_count);
2587
}
2588
 
2589
static void draw_weapon_box(const hud_draw_context_hs_mr hudctx, const player_info &player_info, const unsigned weapon_num, const weapon_type wt)
2590
{
2591
        auto &canvas = hudctx.canvas;
2592
        gr_set_curfont(canvas, GAME_FONT);
2593
 
2594
        const auto laser_level_changed = (wt == weapon_type::primary && weapon_num == primary_weapon_index_t::LASER_INDEX && (player_info.laser_level != old_laser_level));
2595
 
2596
        if ((weapon_num != old_weapon[wt] || laser_level_changed) && weapon_box_states[wt] == WS_SET && (old_weapon[wt] != -1) && PlayerCfg.HudMode == HudType::Standard)
2597
        {
2598
                weapon_box_states[wt] = WS_FADING_OUT;
2599
                weapon_box_fade_values[wt]=i2f(GR_FADE_LEVELS-1);
2600
        }
2601
 
2602
        const local_multires_gauge_graphic multires_gauge_graphic{};
2603
        if (old_weapon[wt] == -1)
2604
        {
2605
                draw_weapon_info(hudctx, player_info, weapon_num, player_info.laser_level, wt);
2606
                old_weapon[wt] = weapon_num;
2607
                weapon_box_states[wt] = WS_SET;
2608
        }
2609
 
2610
        if (weapon_box_states[wt] == WS_FADING_OUT) {
2611
                draw_weapon_info(hudctx, player_info, old_weapon[wt], old_laser_level, wt);
2612
                weapon_box_fade_values[wt] -= FrameTime * FADE_SCALE;
2613
                if (weapon_box_fade_values[wt] <= 0) {
2614
                        weapon_box_states[wt] = WS_FADING_IN;
2615
                        old_weapon[wt] = weapon_num;
2616
                        old_laser_level = player_info.laser_level;
2617
                        weapon_box_fade_values[wt] = 0;
2618
                }
2619
        }
2620
        else if (weapon_box_states[wt] == WS_FADING_IN) {
2621
                if (weapon_num != old_weapon[wt]) {
2622
                        weapon_box_states[wt] = WS_FADING_OUT;
2623
                }
2624
                else {
2625
                        draw_weapon_info(hudctx, player_info, weapon_num, player_info.laser_level, wt);
2626
                        weapon_box_fade_values[wt] += FrameTime * FADE_SCALE;
2627
                        if (weapon_box_fade_values[wt] >= i2f(GR_FADE_LEVELS-1)) {
2628
                                weapon_box_states[wt] = WS_SET;
2629
                                old_weapon[wt] = -1;
2630
                        }
2631
                }
2632
        } else
2633
        {
2634
                draw_weapon_info(hudctx, player_info, weapon_num, player_info.laser_level, wt);
2635
                old_weapon[wt] = weapon_num;
2636
                old_laser_level = player_info.laser_level;
2637
        }
2638
 
2639
        if (weapon_box_states[wt] != WS_SET)            //fade gauge
2640
        {
2641
                int fade_value = f2i(weapon_box_fade_values[wt]);
2642
                int boxofs = (PlayerCfg.CockpitMode[1]==CM_STATUS_BAR)?SB_PRIMARY_BOX:COCKPIT_PRIMARY_BOX;
2643
 
2644
                gr_settransblend(canvas, fade_value, gr_blend::normal);
2645
                auto &g = gauge_boxes[boxofs + wt];
2646
                auto &canvas = hudctx.canvas;
2647
                gr_rect(canvas, hudctx.xscale(g.left), hudctx.yscale(g.top), hudctx.xscale(g.right), hudctx.yscale(g.bot), 0);
2648
 
2649
                gr_settransblend(canvas, GR_FADE_OFF, gr_blend::normal);
2650
        }
2651
}
2652
 
2653
#if defined(DXX_BUILD_DESCENT_II)
2654
static std::array<fix, 2> static_time;
2655
 
2656
static void draw_static(const d_vclip_array &Vclip, const hud_draw_context_hs_mr hudctx, const unsigned win)
2657
{
2658
        const vclip *const vc = &Vclip[VCLIP_MONITOR_STATIC];
2659
        int framenum;
2660
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2661
        int boxofs = (PlayerCfg.CockpitMode[1]==CM_STATUS_BAR)?SB_PRIMARY_BOX:COCKPIT_PRIMARY_BOX;
2662
#if !DXX_USE_OGL
2663
        int x,y;
2664
#endif
2665
 
2666
        static_time[win] += FrameTime;
2667
        if (static_time[win] >= vc->play_time) {
2668
                weapon_box_user[win] = WBU_WEAPON;
2669
                return;
2670
        }
2671
 
2672
        framenum = static_time[win] * vc->num_frames / vc->play_time;
2673
 
2674
        PIGGY_PAGE_IN(vc->frames[framenum]);
2675
 
2676
        auto &bmp = GameBitmaps[vc->frames[framenum].index];
2677
        auto &box = gauge_boxes[boxofs+win];
2678
#if !DXX_USE_OGL
2679
        for (x = box.left; x < box.right; x += bmp.bm_w)
2680
                for (y = box.top; y < box.bot; y += bmp.bm_h)
2681
                        gr_bitmap(hudctx.canvas, x, y, bmp);
2682
#else
2683
        if (multires_gauge_graphic.is_hires())
2684
        {
2685
                const auto scaled_left = hudctx.xscale(box.left);
2686
                const auto scaled_top = hudctx.yscale(box.top);
2687
                const auto scaled_bottom = hudctx.yscale(box.bot - bmp.bm_h);
2688
                hud_bitblt_scaled_xy(hudctx, scaled_left, scaled_top, bmp);
2689
                hud_bitblt_scaled_xy(hudctx, scaled_left, scaled_bottom, bmp);
2690
                const auto scaled_right = hudctx.xscale(box.right - bmp.bm_w);
2691
                hud_bitblt_scaled_xy(hudctx, scaled_right, scaled_top, bmp);
2692
                hud_bitblt_scaled_xy(hudctx, scaled_right, scaled_bottom, bmp);
2693
        }
2694
#endif
2695
}
2696
#endif
2697
 
2698
namespace dsx {
2699
 
2700
static void draw_weapon_box0(const hud_draw_context_hs_mr hudctx, const player_info &player_info)
2701
{
2702
#if defined(DXX_BUILD_DESCENT_II)
2703
        if (weapon_box_user[0] == WBU_WEAPON)
2704
#endif
2705
        {
2706
                const auto Primary_weapon = player_info.Primary_weapon;
2707
                draw_weapon_box(hudctx, player_info, Primary_weapon, weapon_type::primary);
2708
 
2709
                if (weapon_box_states[0] == WS_SET) {
2710
                        unsigned nd_ammo;
2711
                        unsigned ammo_count;
2712
                        if (weapon_index_uses_vulcan_ammo(Primary_weapon))
2713
                        {
2714
                                nd_ammo = player_info.vulcan_ammo;
2715
                                ammo_count = vulcan_ammo_scale(nd_ammo);
2716
                        }
2717
#if defined(DXX_BUILD_DESCENT_II)
2718
                        else if (Primary_weapon == primary_weapon_index_t::OMEGA_INDEX)
2719
                        {
2720
                                auto &Omega_charge = player_info.Omega_charge;
2721
                                nd_ammo = Omega_charge;
2722
                                ammo_count = Omega_charge * 100/MAX_OMEGA_CHARGE;
2723
                        }
2724
#endif
2725
                        else
2726
                                return;
2727
                        if (Newdemo_state == ND_STATE_RECORDING)
2728
                                newdemo_record_primary_ammo(nd_ammo);
2729
                        draw_primary_ammo_info(hudctx, ammo_count);
2730
                }
2731
        }
2732
#if defined(DXX_BUILD_DESCENT_II)
2733
        else if (weapon_box_user[0] == WBU_STATIC)
2734
                draw_static(Vclip, hudctx, 0);
2735
#endif
2736
}
2737
 
2738
static void draw_weapon_box1(const hud_draw_context_hs_mr hudctx, const player_info &player_info)
2739
{
2740
#if defined(DXX_BUILD_DESCENT_II)
2741
        if (weapon_box_user[1] == WBU_WEAPON)
2742
#endif
2743
        {
2744
                auto &Secondary_weapon = player_info.Secondary_weapon;
2745
                draw_weapon_box(hudctx, player_info, Secondary_weapon, weapon_type::secondary);
2746
                if (weapon_box_states[1] == WS_SET)
2747
                {
2748
                        const auto ammo = player_info.secondary_ammo[Secondary_weapon];
2749
                        if (Newdemo_state == ND_STATE_RECORDING)
2750
                                newdemo_record_secondary_ammo(ammo);
2751
                        draw_secondary_ammo_info(hudctx, ammo);
2752
                }
2753
        }
2754
#if defined(DXX_BUILD_DESCENT_II)
2755
        else if (weapon_box_user[1] == WBU_STATIC)
2756
                draw_static(Vclip, hudctx, 1);
2757
#endif
2758
}
2759
 
2760
static void draw_weapon_boxes(const hud_draw_context_hs_mr hudctx, const player_info &player_info)
2761
{
2762
        draw_weapon_box0(hudctx, player_info);
2763
        draw_weapon_box1(hudctx, player_info);
2764
}
2765
 
2766
static void sb_draw_energy_bar(const hud_draw_context_hs_mr hudctx, const unsigned energy)
2767
{
2768
        int ew;
2769
 
2770
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2771
        hud_gauge_bitblt(hudctx, SB_ENERGY_GAUGE_X, SB_ENERGY_GAUGE_Y, SB_GAUGE_ENERGY);
2772
 
2773
        auto &canvas = hudctx.canvas;
2774
        if (energy <= 100)
2775
        {
2776
                const auto color = 0;
2777
                const int erase_x0 = i2f(hudctx.xscale(SB_ENERGY_GAUGE_X));
2778
                const int erase_x1 = i2f(hudctx.xscale(SB_ENERGY_GAUGE_X + SB_ENERGY_GAUGE_W));
2779
                const int erase_y_base = hudctx.yscale(SB_ENERGY_GAUGE_Y);
2780
                for (int i = hudctx.yscale((100 - energy) * SB_ENERGY_GAUGE_H / 100); i-- > 0;)
2781
                {
2782
                const int erase_y = i2f(erase_y_base + i);
2783
                gr_uline(canvas, erase_x0, erase_y, erase_x1, erase_y, color);
2784
                }
2785
        }
2786
 
2787
        //draw numbers
2788
        gr_set_fontcolor(canvas, BM_XRGB(25, 18, 6), -1);
2789
        gr_get_string_size(*canvas.cv_font, get_gauge_width_string(energy), &ew, nullptr, nullptr);
2790
#if defined(DXX_BUILD_DESCENT_I)
2791
        unsigned y = SB_ENERGY_NUM_Y;
2792
#elif defined(DXX_BUILD_DESCENT_II)
2793
        unsigned y = SB_ENERGY_GAUGE_Y + SB_ENERGY_GAUGE_H - GAME_FONT->ft_h - (GAME_FONT->ft_h / 4);
2794
#endif
2795
        gr_printf(canvas, *canvas.cv_font, (grd_curscreen->get_screen_width() / 3) - (ew / 2), hudctx.yscale(y), "%d", energy);
2796
}
2797
 
2798
}
2799
 
2800
#if defined(DXX_BUILD_DESCENT_II)
2801
static void sb_draw_afterburner(const hud_draw_context_hs_mr hudctx, const player_info &player_info)
2802
{
2803
        auto &ab_str = "AB";
2804
 
2805
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2806
        hud_gauge_bitblt(hudctx, SB_AFTERBURNER_GAUGE_X, SB_AFTERBURNER_GAUGE_Y, SB_GAUGE_AFTERBURNER);
2807
 
2808
        const auto color = 0;
2809
        const int erase_x0 = i2f(hudctx.xscale(SB_AFTERBURNER_GAUGE_X));
2810
        const int erase_x1 = i2f(hudctx.xscale(SB_AFTERBURNER_GAUGE_X + (SB_AFTERBURNER_GAUGE_W)));
2811
        const int erase_y_base = hudctx.yscale(SB_AFTERBURNER_GAUGE_Y);
2812
        for (int i = hudctx.yscale(fixmul((f1_0 - Afterburner_charge), SB_AFTERBURNER_GAUGE_H)); i-- > 0;)
2813
        {
2814
                const int erase_y = i2f(erase_y_base + i);
2815
                gr_uline(hudctx.canvas, erase_x0, erase_y, erase_x1, erase_y, color);
2816
        }
2817
 
2818
        //draw legend
2819
        unsigned r, g, b;
2820
        if (player_info.powerup_flags & PLAYER_FLAGS_AFTERBURNER)
2821
                r = 90, g = b = 0;
2822
        else
2823
                r = g = b = 24;
2824
        auto &canvas = hudctx.canvas;
2825
        gr_set_fontcolor(canvas, gr_find_closest_color(r, g, b), -1);
2826
 
2827
        int w, h;
2828
        gr_get_string_size(*canvas.cv_font, ab_str, &w, &h, nullptr);
2829
        gr_string(canvas, *canvas.cv_font, hudctx.xscale(SB_AFTERBURNER_GAUGE_X + (SB_AFTERBURNER_GAUGE_W + 1) / 2) - (w / 2), hudctx.yscale(SB_AFTERBURNER_GAUGE_Y + (SB_AFTERBURNER_GAUGE_H - GAME_FONT->ft_h - (GAME_FONT->ft_h / 4))), ab_str, w, h);
2830
}
2831
#endif
2832
 
2833
static void sb_draw_shield_num(const hud_draw_context_hs_mr hudctx, const unsigned shield)
2834
{
2835
        //draw numbers
2836
        int sw;
2837
 
2838
        auto &canvas = hudctx.canvas;
2839
        gr_set_fontcolor(canvas, BM_XRGB(14, 14, 23), -1);
2840
 
2841
        auto &game_font = *GAME_FONT;
2842
        gr_get_string_size(game_font, get_gauge_width_string(shield), &sw, nullptr, nullptr);
2843
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2844
        gr_printf(canvas, game_font, (grd_curscreen->get_screen_width() / 2.266) - (sw / 2), hudctx.yscale(SB_SHIELD_NUM_Y), "%d", shield);
2845
}
2846
 
2847
static void sb_draw_shield_bar(const hud_draw_context_hs_mr hudctx, const int shield)
2848
{
2849
        int bm_num = shield>=100?9:(shield / 10);
2850
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2851
        hud_gauge_bitblt(hudctx, SB_SHIELD_GAUGE_X, SB_SHIELD_GAUGE_Y, GAUGE_SHIELDS+9-bm_num);
2852
}
2853
 
2854
void draw_keys_state::draw_all_statusbar_keys()
2855
{
2856
        auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2857
        draw_one_key(SB_GAUGE_KEYS_X, SB_GAUGE_BLUE_KEY_Y, SB_GAUGE_BLUE_KEY, PLAYER_FLAGS_BLUE_KEY);
2858
        draw_one_key(SB_GAUGE_KEYS_X, SB_GAUGE_GOLD_KEY_Y, SB_GAUGE_GOLD_KEY, PLAYER_FLAGS_GOLD_KEY);
2859
        draw_one_key(SB_GAUGE_KEYS_X, SB_GAUGE_RED_KEY_Y, SB_GAUGE_RED_KEY, PLAYER_FLAGS_RED_KEY);
2860
}
2861
 
2862
//      Draws invulnerable ship, or maybe the flashing ship, depending on invulnerability time left.
2863
static void draw_invulnerable_ship(const hud_draw_context_hs_mr hudctx, const object &plrobj)
2864
{
2865
        auto &player_info = plrobj.ctype.player_info;
2866
        const auto cmmode = PlayerCfg.CockpitMode[1];
2867
        const auto t = player_info.invulnerable_time;
2868
        if (t + INVULNERABLE_TIME_MAX - GameTime64 > F1_0*4 || GameTime64 & 0x8000)
2869
        {
2870
                static fix time;
2871
                auto ltime = time + FrameTime;
2872
                const auto old_invulnerable_frame = invulnerable_frame;
2873
                while (ltime > INV_FRAME_TIME)
2874
                {
2875
                        ltime -= INV_FRAME_TIME;
2876
                        if (++invulnerable_frame == N_INVULNERABLE_FRAMES)
2877
                                invulnerable_frame=0;
2878
                }
2879
                time = ltime;
2880
                unsigned x, y;
2881
                auto &multires_gauge_graphic = hudctx.multires_gauge_graphic;
2882
                if (cmmode == CM_STATUS_BAR)
2883
                {
2884
                        x = SB_SHIELD_GAUGE_X;
2885
                        y = SB_SHIELD_GAUGE_Y;
2886
                }
2887
                else
2888
                {
2889
                        x = SHIELD_GAUGE_X;
2890
                        y = SHIELD_GAUGE_Y;
2891
                }
2892
                hud_gauge_bitblt(hudctx, x, y, GAUGE_INVULNERABLE + old_invulnerable_frame);
2893
 
2894
                // Show Invulnerability Timer if enabled
2895
                if (show_cloak_invul_timer())
2896
                        show_cockpit_cloak_invul_timer(hudctx.canvas, t + INVULNERABLE_TIME_MAX - GameTime64, hudctx.yscale(y));
2897
        }
2898
        else
2899
        {
2900
                const auto shields_ir = f2ir(plrobj.shields);
2901
                if (cmmode == CM_STATUS_BAR)
2902
                        sb_draw_shield_bar(hudctx, shields_ir);
2903
                else
2904
                        draw_shield_bar(hudctx, shields_ir);
2905
        }
2906
}
2907
 
2908
const rgb_array_t player_rgb_normal{{
2909
                                                        {15,15,23},
2910
                                                        {27,0,0},
2911
                                                        {0,23,0},
2912
                                                        {30,11,31},
2913
                                                        {31,16,0},
2914
                                                        {24,17,6},
2915
                                                        {14,21,12},
2916
                                                        {29,29,0},
2917
}};
2918
 
2919
struct xy {
2920
        sbyte x, y;
2921
};
2922
 
2923
//offsets for reticle parts: high-big  high-sml  low-big  low-sml
2924
const std::array<xy, 4> cross_offsets{{
2925
        {-8,-5},
2926
        {-4,-2},
2927
        {-4,-2},
2928
        {-2,-1}
2929
}},
2930
        primary_offsets{{
2931
        {-30,14},
2932
        {-16,6},
2933
        {-15,6},
2934
        {-8, 2}
2935
}},
2936
        secondary_offsets{{
2937
        {-24,2},
2938
        {-12,0},
2939
        {-12,1},
2940
        {-6,-2}
2941
}};
2942
 
2943
//draw the reticle
2944
namespace dsx {
2945
void show_reticle(grs_canvas &canvas, const player_info &player_info, int reticle_type, int secondary_display)
2946
{
2947
        int x,y,size;
2948
        int laser_ready,missile_ready;
2949
        int cross_bm_num,primary_bm_num,secondary_bm_num;
2950
        int gauge_index;
2951
 
2952
#if defined(DXX_BUILD_DESCENT_II)
2953
        if (Newdemo_state==ND_STATE_PLAYBACK && Viewer->type != OBJ_PLAYER)
2954
                 return;
2955
#endif
2956
 
2957
        x = canvas.cv_bitmap.bm_w/2;
2958
        y = canvas.cv_bitmap.bm_h/2;
2959
        size = (canvas.cv_bitmap.bm_h / (32-(PlayerCfg.ReticleSize*4)));
2960
 
2961
        laser_ready = allowed_to_fire_laser(player_info);
2962
 
2963
        missile_ready = allowed_to_fire_missile(player_info);
2964
        auto &Primary_weapon = player_info.Primary_weapon;
2965
        primary_bm_num = (laser_ready && player_has_primary_weapon(player_info, Primary_weapon).has_all());
2966
        auto &Secondary_weapon = player_info.Secondary_weapon;
2967
        secondary_bm_num = (missile_ready && player_has_secondary_weapon(player_info, Secondary_weapon).has_all());
2968
 
2969
        if (primary_bm_num && Primary_weapon == primary_weapon_index_t::LASER_INDEX && (player_info.powerup_flags & PLAYER_FLAGS_QUAD_LASERS))
2970
                primary_bm_num++;
2971
 
2972
        if (Secondary_weapon_to_gun_num[Secondary_weapon]==7)
2973
                secondary_bm_num += 3;          //now value is 0,1 or 3,4
2974
        else if (secondary_bm_num && !(player_info.missile_gun & 1))
2975
                        secondary_bm_num++;
2976
 
2977
        cross_bm_num = ((primary_bm_num > 0) || (secondary_bm_num > 0));
2978
 
2979
        Assert(primary_bm_num <= 2);
2980
        Assert(secondary_bm_num <= 4);
2981
        Assert(cross_bm_num <= 1);
2982
 
2983
        const auto color = BM_XRGB(PlayerCfg.ReticleRGBA[0],PlayerCfg.ReticleRGBA[1],PlayerCfg.ReticleRGBA[2]);
2984
        gr_settransblend(canvas, PlayerCfg.ReticleRGBA[3], gr_blend::normal);
2985
 
2986
        [&]{
2987
                int x0, x1, y0, y1;
2988
        switch (reticle_type)
2989
        {
2990
                case RET_TYPE_CLASSIC:
2991
                {
2992
                        const local_multires_gauge_graphic multires_gauge_graphic{};
2993
                        const hud_draw_context_hs_mr hudctx(canvas, grd_curscreen->get_screen_width(), grd_curscreen->get_screen_height(), multires_gauge_graphic);
2994
                        const auto use_hires_reticle = multires_gauge_graphic.is_hires();
2995
                        const unsigned ofs = (use_hires_reticle ? 0 : 2);
2996
                        gauge_index = RETICLE_CROSS + cross_bm_num;
2997
                        PAGE_IN_GAUGE(gauge_index, multires_gauge_graphic);
2998
                        auto &cross = GameBitmaps[GET_GAUGE_INDEX(gauge_index)];
2999
                        const auto &&hud_scale_ar = HUD_SCALE_AR(hudctx.xscale, hudctx.yscale);
3000
                        hud_bitblt_free(canvas, x + hud_scale_ar(cross_offsets[ofs].x), y + hud_scale_ar(cross_offsets[ofs].y), hud_scale_ar(cross.bm_w), hud_scale_ar(cross.bm_h), cross);
3001
 
3002
                        gauge_index = RETICLE_PRIMARY + primary_bm_num;
3003
                        PAGE_IN_GAUGE(gauge_index, multires_gauge_graphic);
3004
                        auto &primary = GameBitmaps[GET_GAUGE_INDEX(gauge_index)];
3005
                        hud_bitblt_free(canvas, x + hud_scale_ar(primary_offsets[ofs].x), y + hud_scale_ar(primary_offsets[ofs].y), hud_scale_ar(primary.bm_w), hud_scale_ar(primary.bm_h), primary);
3006
 
3007
                        gauge_index = RETICLE_SECONDARY + secondary_bm_num;
3008
                        PAGE_IN_GAUGE(gauge_index, multires_gauge_graphic);
3009
                        auto &secondary = GameBitmaps[GET_GAUGE_INDEX(gauge_index)];
3010
                        hud_bitblt_free(canvas, x + hud_scale_ar(secondary_offsets[ofs].x), y + hud_scale_ar(secondary_offsets[ofs].y), hud_scale_ar(secondary.bm_w), hud_scale_ar(secondary.bm_h), secondary);
3011
                        return;
3012
                }
3013
                case RET_TYPE_CLASSIC_REBOOT:
3014
#if DXX_USE_OGL
3015
                        ogl_draw_vertex_reticle(cross_bm_num,primary_bm_num,secondary_bm_num,BM_XRGB(PlayerCfg.ReticleRGBA[0],PlayerCfg.ReticleRGBA[1],PlayerCfg.ReticleRGBA[2]),PlayerCfg.ReticleRGBA[3],PlayerCfg.ReticleSize);
3016
#endif
3017
                        return;
3018
                case RET_TYPE_X:
3019
                        {
3020
                        gr_uline(canvas, i2f(x-(size/2)), i2f(y-(size/2)), i2f(x-(size/5)), i2f(y-(size/5)), color); // top-left
3021
                        gr_uline(canvas, i2f(x+(size/2)), i2f(y-(size/2)), i2f(x+(size/5)), i2f(y-(size/5)), color); // top-right
3022
                        gr_uline(canvas, i2f(x-(size/2)), i2f(y+(size/2)), i2f(x-(size/5)), i2f(y+(size/5)), color); // bottom-left
3023
                        gr_uline(canvas, i2f(x+(size/2)), i2f(y+(size/2)), i2f(x+(size/5)), i2f(y+(size/5)), color); // bottom-right
3024
                        if (secondary_display && secondary_bm_num == 1)
3025
                                x0 = i2f(x-(size/2)-(size/5)), y0 = i2f(y-(size/2)), x1 = i2f(x-(size/5)-(size/5)), y1 = i2f(y-(size/5));
3026
                        else if (secondary_display && secondary_bm_num == 2)
3027
                                x0 = i2f(x+(size/2)+(size/5)), y0 = i2f(y-(size/2)), x1 = i2f(x+(size/5)+(size/5)), y1 = i2f(y-(size/5));
3028
                        else if (secondary_display && secondary_bm_num == 4)
3029
                                x0 = i2f(x+(size/2)), y0 = i2f(y+(size/2)+(size/5)), x1 = i2f(x+(size/5)), y1 = i2f(y+(size/5)+(size/5));
3030
                        else
3031
                                return;
3032
                        }
3033
                        break;
3034
                case RET_TYPE_DOT:
3035
                        {
3036
                                gr_disk(canvas, i2f(x), i2f(y), i2f(size/5), color);
3037
                        if (secondary_display && secondary_bm_num == 1)
3038
                                x0 = i2f(x-(size/2)-(size/5)), y0 = i2f(y-(size/2)), x1 = i2f(x-(size/5)-(size/5)), y1 = i2f(y-(size/5));
3039
                        else if (secondary_display && secondary_bm_num == 2)
3040
                                x0 = i2f(x+(size/2)+(size/5)), y0 = i2f(y-(size/2)), x1 = i2f(x+(size/5)+(size/5)), y1 = i2f(y-(size/5));
3041
                        else if (secondary_display && secondary_bm_num == 4)
3042
                                x0 = i2f(x), y0 = i2f(y+(size/2)+(size/5)), x1 = i2f(x), y1 = i2f(y+(size/5)+(size/5));
3043
                        else
3044
                                return;
3045
                        }
3046
                        break;
3047
                case RET_TYPE_CIRCLE:
3048
                        {
3049
                                gr_ucircle(canvas, i2f(x), i2f(y), i2f(size/4), color);
3050
                        if (secondary_display && secondary_bm_num == 1)
3051
                                x0 = i2f(x-(size/2)-(size/5)), y0 = i2f(y-(size/2)), x1 = i2f(x-(size/5)-(size/5)), y1 = i2f(y-(size/5));
3052
                        else if (secondary_display && secondary_bm_num == 2)
3053
                                x0 = i2f(x+(size/2)+(size/5)), y0 = i2f(y-(size/2)), x1 = i2f(x+(size/5)+(size/5)), y1 = i2f(y-(size/5));
3054
                        else if (secondary_display && secondary_bm_num == 4)
3055
                                x0 = i2f(x), y0 = i2f(y+(size/2)+(size/5)), x1 = i2f(x), y1 = i2f(y+(size/5)+(size/5));
3056
                        else
3057
                                return;
3058
                        }
3059
                        break;
3060
                case RET_TYPE_CROSS_V1:
3061
                        {
3062
                        gr_uline(canvas, i2f(x),i2f(y-(size/2)),i2f(x),i2f(y+(size/2)+1), color); // horiz
3063
                        gr_uline(canvas, i2f(x-(size/2)),i2f(y),i2f(x+(size/2)+1),i2f(y), color); // vert
3064
                        if (secondary_display && secondary_bm_num == 1)
3065
                                x0 = i2f(x-(size/2)), y0 = i2f(y-(size/2)), x1 = i2f(x-(size/5)), y1 = i2f(y-(size/5));
3066
                        else if (secondary_display && secondary_bm_num == 2)
3067
                                x0 = i2f(x+(size/2)), y0 = i2f(y-(size/2)), x1 = i2f(x+(size/5)), y1 = i2f(y-(size/5));
3068
                        else if (secondary_display && secondary_bm_num == 4)
3069
                                x0 = i2f(x-(size/2)), y0 = i2f(y+(size/2)), x1 = i2f(x-(size/5)), y1 = i2f(y+(size/5));
3070
                        else
3071
                                return;
3072
                        }
3073
                        break;
3074
                case RET_TYPE_CROSS_V2:
3075
                        {
3076
                        gr_uline(canvas, i2f(x), i2f(y-(size/2)), i2f(x), i2f(y-(size/6)), color); // vert-top
3077
                        gr_uline(canvas, i2f(x), i2f(y+(size/2)), i2f(x), i2f(y+(size/6)), color); // vert-bottom
3078
                        gr_uline(canvas, i2f(x-(size/2)), i2f(y), i2f(x-(size/6)), i2f(y), color); // horiz-left
3079
                        gr_uline(canvas, i2f(x+(size/2)), i2f(y), i2f(x+(size/6)), i2f(y), color); // horiz-right
3080
                        if (secondary_display && secondary_bm_num == 1)
3081
                                x0 = i2f(x-(size/2)), y0 = i2f(y-(size/2)), x1 = i2f(x-(size/5)), y1 = i2f(y-(size/5));
3082
                        else if (secondary_display && secondary_bm_num == 2)
3083
                                x0 = i2f(x+(size/2)), y0 = i2f(y-(size/2)), x1 = i2f(x+(size/5)), y1 = i2f(y-(size/5));
3084
                        else if (secondary_display && secondary_bm_num == 4)
3085
                                x0 = i2f(x-(size/2)), y0 = i2f(y+(size/2)), x1 = i2f(x-(size/5)), y1 = i2f(y+(size/5));
3086
                        else
3087
                                return;
3088
                        }
3089
                        break;
3090
                case RET_TYPE_ANGLE:
3091
                        {
3092
                        gr_uline(canvas, i2f(x),i2f(y),i2f(x),i2f(y+(size/2)), color); // vert
3093
                        gr_uline(canvas, i2f(x),i2f(y),i2f(x+(size/2)),i2f(y), color); // horiz
3094
                        if (secondary_display && secondary_bm_num == 1)
3095
                                x0 = i2f(x-(size/2)), y0 = i2f(y-(size/2)), x1 = i2f(x-(size/5)), y1 = i2f(y-(size/5));
3096
                        else if (secondary_display && secondary_bm_num == 2)
3097
                                x0 = i2f(x+(size/2)), y0 = i2f(y-(size/2)), x1 = i2f(x+(size/5)), y1 = i2f(y-(size/5));
3098
                        else if (secondary_display && secondary_bm_num == 4)
3099
                                x0 = i2f(x-(size/2)), y0 = i2f(y+(size/2)), x1 = i2f(x-(size/5)), y1 = i2f(y+(size/5));
3100
                        else
3101
                                return;
3102
                        }
3103
                        break;
3104
                case RET_TYPE_NONE:
3105
                default:
3106
                        return;
3107
        }
3108
        gr_uline(canvas, x0, y0, x1, y1, color);
3109
        }();
3110
        gr_settransblend(canvas, GR_FADE_OFF, gr_blend::normal);
3111
}
3112
}
3113
 
3114
void show_mousefs_indicator(grs_canvas &canvas, int mx, int my, int mz, int x, int y, int size)
3115
{
3116
        int axscale = (MOUSEFS_DELTA_RANGE*2)/size, xaxpos = x+(mx/axscale), yaxpos = y+(my/axscale), zaxpos = y+(mz/axscale);
3117
 
3118
        gr_settransblend(canvas, PlayerCfg.ReticleRGBA[3], gr_blend::normal);
3119
        auto &rgba = PlayerCfg.ReticleRGBA;
3120
        const auto color = BM_XRGB(rgba[0], rgba[1], rgba[2]);
3121
        gr_uline(canvas, i2f(xaxpos), i2f(y-(size/2)), i2f(xaxpos), i2f(y-(size/4)), color);
3122
        gr_uline(canvas, i2f(xaxpos), i2f(y+(size/2)), i2f(xaxpos), i2f(y+(size/4)), color);
3123
        gr_uline(canvas, i2f(x-(size/2)), i2f(yaxpos), i2f(x-(size/4)), i2f(yaxpos), color);
3124
        gr_uline(canvas, i2f(x+(size/2)), i2f(yaxpos), i2f(x+(size/4)), i2f(yaxpos), color);
3125
        const local_multires_gauge_graphic multires_gauge_graphic{};
3126
        auto &&hud_scale_ar = HUD_SCALE_AR(grd_curscreen->get_screen_width(), grd_curscreen->get_screen_height(), multires_gauge_graphic);
3127
        auto &&hud_scale_ar2 = hud_scale_ar(2);
3128
        gr_uline(canvas, i2f(x + (size / 2) + hud_scale_ar2), i2f(y), i2f(x + (size / 2) + hud_scale_ar2), i2f(zaxpos), color);
3129
        gr_settransblend(canvas, GR_FADE_OFF, gr_blend::normal);
3130
}
3131
 
3132
static void hud_show_kill_list(fvcobjptr &vcobjptr, grs_canvas &canvas)
3133
{
3134
        playernum_t n_players;
3135
        playernum_array_t player_list;
3136
        int n_left,i,x0,x1,x2,y,save_y;
3137
 
3138
        if (Show_kill_list_timer > 0)
3139
        {
3140
                Show_kill_list_timer -= FrameTime;
3141
                if (Show_kill_list_timer < 0)
3142
                        Show_kill_list = 0;
3143
        }
3144
 
3145
        n_players = multi_get_kill_list(player_list);
3146
 
3147
        if (Show_kill_list == 3)
3148
                n_players = 2;
3149
 
3150
        if (n_players <= 4)
3151
                n_left = n_players;
3152
        else
3153
                n_left = (n_players+1)/2;
3154
 
3155
        const auto &&fspacx = FSPACX();
3156
        const auto &&fspacx43 = fspacx(43);
3157
 
3158
        x1 = fspacx43;
3159
 
3160
        const auto is_multiplayer_cooperative = Game_mode & GM_MULTI_COOP;
3161
        if (is_multiplayer_cooperative)
3162
                x1 = fspacx(31);
3163
 
3164
        auto &game_font = *GAME_FONT;
3165
        const auto &&line_spacing = LINE_SPACING(game_font, game_font);
3166
        save_y = y = canvas.cv_bitmap.bm_h - n_left * line_spacing;
3167
 
3168
        if (PlayerCfg.CockpitMode[1] == CM_FULL_COCKPIT) {
3169
                save_y = y -= fspacx(6);
3170
                if (is_multiplayer_cooperative)
3171
                        x1 = fspacx(33);
3172
        }
3173
 
3174
        const auto bm_w = canvas.cv_bitmap.bm_w;
3175
        const auto &&bmw_x0_cockpit = bm_w - fspacx(PlayerCfg.CockpitMode[1] == CM_FULL_COCKPIT ? 53 : 60);
3176
        // Right edge of name, change this for width problems
3177
        const auto &&bmw_x1_multi = bm_w - fspacx(is_multiplayer_cooperative ? 27 : 15);
3178
        const auto &&fspacx1 = fspacx(1);
3179
        const auto &&fspacx2 = fspacx(2);
3180
        const auto &&fspacx18 = fspacx(18);
3181
        const auto &&fspacx35 = fspacx(35);
3182
        const auto &&fspacx64 = fspacx(64);
3183
        x0 = fspacx1;
3184
        for (i=0;i<n_players;i++) {
3185
                playernum_t player_num;
3186
                callsign_t name;
3187
 
3188
                if (i>=n_left) {
3189
                        x0 = bmw_x0_cockpit;
3190
                        x1 = bmw_x1_multi;
3191
                        if (PlayerCfg.MultiPingHud)
3192
                        {
3193
                                x0 -= fspacx35;
3194
                                x1 -= fspacx35;
3195
                        }
3196
                        if (i==n_left)
3197
                                y = save_y;
3198
 
3199
                        if (Netgame.KillGoal || Netgame.PlayTimeAllowed.count())
3200
                                x1 -= fspacx18;
3201
                }
3202
                else  if (Netgame.KillGoal || Netgame.PlayTimeAllowed.count())
3203
                {
3204
                        x1 = fspacx43;
3205
                        x1 -= fspacx18;
3206
                }
3207
 
3208
                if (Show_kill_list == 3)
3209
                        player_num = i;
3210
                else
3211
                        player_num = player_list[i];
3212
                auto &p = *vcplayerptr(player_num);
3213
 
3214
                color_t fontcolor;
3215
                rgb color;
3216
                if (Show_kill_list == 1 || Show_kill_list==2)
3217
                {
3218
                        if (vcplayerptr(player_num)->connected != CONNECT_PLAYING)
3219
                                color.r = color.g = color.b = 12;
3220
                        else {
3221
                                color = player_rgb[get_player_or_team_color(player_num)];
3222
                        }
3223
                }
3224
                else
3225
                {
3226
                        color = player_rgb_normal[player_num];
3227
                }
3228
                fontcolor = BM_XRGB(color.r, color.g, color.b);
3229
                gr_set_fontcolor(canvas, fontcolor, -1);
3230
 
3231
                if (Show_kill_list == 3)
3232
                        name = Netgame.team_name[i];
3233
                else if (Game_mode & GM_BOUNTY && player_num == Bounty_target && GameTime64&0x10000)
3234
                {
3235
                        name = "[TARGET]";
3236
                }
3237
                else
3238
                        name = vcplayerptr(player_num)->callsign;       // Note link to above if!!
3239
                int sw, sh;
3240
                gr_get_string_size(game_font, static_cast<const char *>(name), &sw, &sh, nullptr);
3241
                {
3242
                        const auto b = x1 - x0 - fspacx2;
3243
                        if (sw > b)
3244
                                for (char *e = &name.buffer()[strlen(name)];;)
3245
                                {
3246
                                         *--e = 0;
3247
                                         gr_get_string_size(game_font, name, &sw, nullptr, nullptr);
3248
                                         if (!(sw > b))
3249
                                                 break;
3250
                                }
3251
                }
3252
                gr_string(canvas, game_font, x0, y, name, sw, sh);
3253
 
3254
                auto &player_info = vcobjptr(p.objnum)->ctype.player_info;
3255
                if (Show_kill_list==2)
3256
                {
3257
                        const int eff = (player_info.net_killed_total + player_info.net_kills_total <= 0)
3258
                                ? 0
3259
                                : static_cast<int>(
3260
                                        static_cast<float>(player_info.net_kills_total) / (
3261
                                                static_cast<float>(player_info.net_killed_total) + static_cast<float>(player_info.net_kills_total)
3262
                                        ) * 100.0
3263
                                );
3264
                        gr_printf(canvas, game_font, x1, y, "%i%%", eff <= 0 ? 0 : eff);
3265
                }
3266
                else if (Show_kill_list == 3)
3267
                        gr_printf(canvas, game_font, x1, y, "%3d", team_kills[i]);
3268
                else if (is_multiplayer_cooperative)
3269
                        gr_printf(canvas, game_font, x1, y, "%-6d", player_info.mission.score);
3270
                else if (Netgame.KillGoal || Netgame.PlayTimeAllowed.count())
3271
                        gr_printf(canvas, game_font, x1, y, "%3d(%d)", player_info.net_kills_total, player_info.KillGoalCount);
3272
                else
3273
                        gr_printf(canvas, game_font, x1, y, "%3d", player_info.net_kills_total);
3274
 
3275
                if (PlayerCfg.MultiPingHud && Show_kill_list != 3)
3276
                {
3277
                                        if (is_multiplayer_cooperative)
3278
                                x2 = SWIDTH - (fspacx64/2);
3279
                        else
3280
                                x2 = x0 + fspacx64;
3281
                        gr_printf(canvas, game_font, x2, y, "%4dms", Netgame.players[player_num].ping);
3282
                }
3283
 
3284
                y += line_spacing;
3285
        }
3286
}
3287
 
3288
//returns true if viewer can see object
3289
static int see_object(fvcobjptridx &vcobjptridx, const vcobjptridx_t objnum)
3290
{
3291
        fvi_query fq;
3292
        int hit_type;
3293
        fvi_info hit_data;
3294
 
3295
        //see if we can see this player
3296
 
3297
        fq.p0                                   = &Viewer->pos;
3298
        fq.p1                                   = &objnum->pos;
3299
        fq.rad                                  = 0;
3300
        fq.thisobjnum                   = vcobjptridx(Viewer);
3301
        fq.flags                                = FQ_TRANSWALL | FQ_CHECK_OBJS | FQ_GET_SEGLIST;
3302
        fq.startseg                             = Viewer->segnum;
3303
        fq.ignore_obj_list.first = nullptr;
3304
 
3305
        hit_type = find_vector_intersection(fq, hit_data);
3306
 
3307
        return (hit_type == HIT_OBJECT && hit_data.hit_object == objnum);
3308
}
3309
 
3310
//show names of teammates & players carrying flags
3311
namespace dsx {
3312
 
3313
void show_HUD_names(grs_canvas &canvas)
3314
{
3315
        auto &Objects = LevelUniqueObjectState.Objects;
3316
        auto &vcobjptr = Objects.vcptr;
3317
        auto &vcobjptridx = Objects.vcptridx;
3318
        for (playernum_t pnum = 0;pnum < N_players; ++pnum)
3319
        {
3320
                if (pnum == Player_num || vcplayerptr(pnum)->connected != CONNECT_PLAYING)
3321
                        continue;
3322
                // ridiculusly complex to check if we want to show something... but this is readable at least.
3323
 
3324
                objnum_t objnum;
3325
                if (Newdemo_state == ND_STATE_PLAYBACK) {
3326
                        //if this is a demo, the objnum in the player struct is wrong, so we search the object list for the objnum
3327
                        for (objnum=0;objnum<=Highest_object_index;objnum++)
3328
                        {
3329
                                auto &objp = *vcobjptr(objnum);
3330
                                if (objp.type == OBJ_PLAYER && get_player_id(objp) == pnum)
3331
                                        break;
3332
                        }
3333
                        if (objnum > Highest_object_index)      //not in list, thus not visible
3334
                                continue;                       //..so don't show name
3335
                }
3336
                else
3337
                        objnum = vcplayerptr(pnum)->objnum;
3338
 
3339
                const auto &&objp = vcobjptridx(objnum);
3340
                const auto &pl_flags = objp->ctype.player_info.powerup_flags;
3341
                const auto is_friend = (Game_mode & GM_MULTI_COOP || (Game_mode & GM_TEAM && get_team(pnum) == get_team(Player_num)));
3342
                const auto show_friend_name = Show_reticle_name;
3343
                const auto is_cloaked = pl_flags & PLAYER_FLAGS_CLOAKED;
3344
                const auto show_enemy_name = Show_reticle_name && Netgame.ShowEnemyNames && !is_cloaked;
3345
                const auto show_name = is_friend ? show_friend_name : show_enemy_name;
3346
                const auto show_typing = is_friend || !is_cloaked;
3347
                const auto is_bounty_target = (Game_mode & GM_BOUNTY) && pnum == Bounty_target;
3348
                const auto show_indi = (is_friend || !is_cloaked) &&
3349
#if defined(DXX_BUILD_DESCENT_I)
3350
                        is_bounty_target;
3351
#elif defined(DXX_BUILD_DESCENT_II)
3352
                        (is_bounty_target || ((game_mode_capture_flag() || game_mode_hoard()) && (pl_flags & PLAYER_FLAGS_FLAG)));
3353
#endif
3354
 
3355
                if ((show_name || show_typing || show_indi) && see_object(vcobjptridx, objp))
3356
                {
3357
                        auto player_point = g3_rotate_point(objp->pos);
3358
                        if (player_point.p3_codes == 0) //on screen
3359
                        {
3360
                                g3_project_point(player_point);
3361
                                if (!(player_point.p3_flags & PF_OVERFLOW))
3362
                                {
3363
                                        fix x,y,dx,dy;
3364
                                        char s[CALLSIGN_LEN+10];
3365
                                        int x1, y1;
3366
 
3367
                                        x = player_point.p3_sx;
3368
                                        y = player_point.p3_sy;
3369
                                        dy = -fixmuldiv(fixmul(objp->size, Matrix_scale.y), i2f(canvas.cv_bitmap.bm_h) / 2, player_point.p3_z);
3370
                                        dx = fixmul(dy,grd_curscreen->sc_aspect);
3371
                                        /* Set the text to show */
3372
                                        const char *name = NULL;
3373
                                        if(is_bounty_target)
3374
                                                name = "Target";
3375
                                        else if (show_name)
3376
                                                name = static_cast<const char *>(vcplayerptr(pnum)->callsign);
3377
                                        const char *trailer = NULL;
3378
                                        if (show_typing)
3379
                                        {
3380
                                                if (multi_sending_message[pnum] == msgsend_typing)
3381
                                                        trailer = "Typing";
3382
                                                else if (multi_sending_message[pnum] == msgsend_automap)
3383
                                                        trailer = "Map";
3384
                                        }
3385
                                        int written = snprintf(s, sizeof(s), "%s%s%s", name ? name : "", name && trailer ? ", " : "", trailer ? trailer : "");
3386
                                        if (written)
3387
                                        {
3388
                                                int w, h;
3389
                                                gr_get_string_size(*canvas.cv_font, s, &w, &h, nullptr);
3390
                                                const auto color = get_player_or_team_color(pnum);
3391
                                                gr_set_fontcolor(canvas, BM_XRGB(player_rgb[color].r, player_rgb[color].g, player_rgb[color].b), -1);
3392
                                                x1 = f2i(x)-w/2;
3393
                                                y1 = f2i(y-dy)+FSPACY(1);
3394
                                                gr_string(canvas, *canvas.cv_font, x1, y1, s, w, h);
3395
                                        }
3396
 
3397
                                        /* Draw box on HUD */
3398
                                        if (show_indi)
3399
                                        {
3400
                                                fix w,h;
3401
 
3402
                                                w = dx/4;
3403
                                                h = dy/4;
3404
 
3405
                                                        struct {
3406
                                                                int r, g, b;
3407
                                                        } c{};
3408
#if defined(DXX_BUILD_DESCENT_II)
3409
                                                if (game_mode_capture_flag())
3410
                                                                ((get_team(pnum) == TEAM_BLUE) ? c.r : c.b) = 31;
3411
                                                else if (game_mode_hoard())
3412
                                                {
3413
                                                                ((Game_mode & GM_TEAM)
3414
                                                                        ? ((get_team(pnum) == TEAM_RED) ? c.r : c.b)
3415
                                                                        : c.g
3416
                                                                ) = 31;
3417
                                                }
3418
                                                else
3419
#endif
3420
                                                        {
3421
                                                                auto &color = player_rgb[get_player_color(pnum)];
3422
                                                                c = {color.r, color.g, color.b};
3423
                                                        }
3424
                                                const uint8_t color = BM_XRGB(c.r, c.g, c.b);
3425
 
3426
                                                gr_line(canvas, x + dx - w, y - dy, x + dx, y - dy, color);
3427
                                                gr_line(canvas, x + dx, y - dy, x + dx, y - dy + h, color);
3428
                                                gr_line(canvas, x - dx, y - dy, x - dx + w, y - dy, color);
3429
                                                gr_line(canvas, x - dx, y - dy, x - dx, y - dy + h, color);
3430
                                                gr_line(canvas, x + dx - w, y + dy, x + dx, y + dy, color);
3431
                                                gr_line(canvas, x + dx, y + dy, x + dx, y + dy - h, color);
3432
                                                gr_line(canvas, x - dx, y + dy, x - dx + w, y + dy, color);
3433
                                                gr_line(canvas, x - dx, y + dy, x - dx, y + dy - h, color);
3434
                                        }
3435
                                }
3436
                        }
3437
                }
3438
        }
3439
}
3440
 
3441
//draw all the things on the HUD
3442
void draw_hud(grs_canvas &canvas, const object &plrobj)
3443
{
3444
        auto &Objects = LevelUniqueObjectState.Objects;
3445
        auto &vcobjptr = Objects.vcptr;
3446
        auto &player_info = plrobj.ctype.player_info;
3447
        if (Newdemo_state == ND_STATE_RECORDING)
3448
        {
3449
                int ammo;
3450
                auto &Primary_weapon = player_info.Primary_weapon;
3451
                if ((Primary_weapon == primary_weapon_index_t::VULCAN_INDEX && (ammo = player_info.vulcan_ammo, true))
3452
#if defined(DXX_BUILD_DESCENT_II)
3453
                        ||
3454
                        (Primary_weapon == primary_weapon_index_t::OMEGA_INDEX && (ammo = player_info.Omega_charge, true))
3455
#endif
3456
                )
3457
                        newdemo_record_primary_ammo(ammo);
3458
                newdemo_record_secondary_ammo(player_info.secondary_ammo[player_info.Secondary_weapon]);
3459
        }
3460
        if (PlayerCfg.HudMode == HudType::Hidden) // no hud, "immersion mode"
3461
                return;
3462
 
3463
        // Cruise speed
3464
        if (Viewer->type == OBJ_PLAYER && get_player_id(vcobjptr(Viewer)) == Player_num && PlayerCfg.CockpitMode[1] != CM_REAR_VIEW)
3465
        {
3466
                int     x = FSPACX(1);
3467
                int     y = canvas.cv_bitmap.bm_h;
3468
 
3469
                gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
3470
                if (Cruise_speed > 0) {
3471
                        auto &game_font = *GAME_FONT;
3472
                        const auto &&line_spacing = LINE_SPACING(game_font, game_font);
3473
                        if (PlayerCfg.CockpitMode[1]==CM_FULL_SCREEN) {
3474
                                if (Game_mode & GM_MULTI)
3475
                                        y -= line_spacing * 10;
3476
                                else
3477
                                        y -= line_spacing * 6;
3478
                        } else if (PlayerCfg.CockpitMode[1] == CM_STATUS_BAR) {
3479
                                if (Game_mode & GM_MULTI)
3480
                                        y -= line_spacing * 6;
3481
                                else
3482
                                        y -= line_spacing * 1;
3483
                        } else {
3484
                                if (Game_mode & GM_MULTI)
3485
                                        y -= line_spacing * 7;
3486
                                else
3487
                                        y -= line_spacing * 2;
3488
                        }
3489
 
3490
                        gr_printf(canvas, game_font, x, y, "%s %2d%%", TXT_CRUISE, f2i(Cruise_speed) );
3491
                }
3492
        }
3493
 
3494
        //      Show score so long as not in rearview
3495
        if ( !Rear_view && PlayerCfg.CockpitMode[1]!=CM_REAR_VIEW && PlayerCfg.CockpitMode[1]!=CM_STATUS_BAR) {
3496
                hud_show_score(canvas, player_info);
3497
                if (score_time)
3498
                        hud_show_score_added(canvas);
3499
        }
3500
 
3501
        if ( !Rear_view && PlayerCfg.CockpitMode[1]!=CM_REAR_VIEW)
3502
                hud_show_timer_count(canvas);
3503
 
3504
        //      Show other stuff if not in rearview or letterbox.
3505
        if (!Rear_view && PlayerCfg.CockpitMode[1]!=CM_REAR_VIEW)
3506
        {
3507
                show_HUD_names(canvas);
3508
 
3509
                if (PlayerCfg.CockpitMode[1]==CM_STATUS_BAR || PlayerCfg.CockpitMode[1]==CM_FULL_SCREEN)
3510
                        hud_show_homing_warning(canvas, player_info.homing_object_dist);
3511
 
3512
                const local_multires_gauge_graphic multires_gauge_graphic = {};
3513
                const hud_draw_context_hs_mr hudctx(canvas, grd_curscreen->get_screen_width(), grd_curscreen->get_screen_height(), multires_gauge_graphic);
3514
                if (PlayerCfg.CockpitMode[1]==CM_FULL_SCREEN) {
3515
 
3516
                        auto &game_font = *GAME_FONT;
3517
                        const auto &&line_spacing = LINE_SPACING(game_font, game_font);
3518
                        const unsigned base_y = canvas.cv_bitmap.bm_h - ((Game_mode & GM_MULTI) ? (line_spacing * (5 + (N_players > 3))) : line_spacing);
3519
                        unsigned current_y = base_y;
3520
                        hud_show_energy(canvas, player_info, game_font, current_y);
3521
                        current_y -= line_spacing;
3522
                        hud_show_shield(canvas, plrobj, game_font, current_y);
3523
                        current_y -= line_spacing;
3524
#if defined(DXX_BUILD_DESCENT_II)
3525
                        hud_show_afterburner(canvas, player_info, game_font, current_y);
3526
                        current_y -= line_spacing;
3527
#endif
3528
                        hud_show_weapons(canvas, plrobj, game_font);
3529
#if defined(DXX_BUILD_DESCENT_I)
3530
                        if (!PCSharePig)
3531
#endif
3532
                        hud_show_keys(hudctx, HUD_SCALE_AR(hudctx.xscale, hudctx.yscale), player_info);
3533
                        hud_show_cloak_invuln(canvas, player_info.powerup_flags, player_info.cloak_time, player_info.invulnerable_time, current_y);
3534
 
3535
                        if (Newdemo_state==ND_STATE_RECORDING)
3536
                                newdemo_record_player_flags(player_info.powerup_flags.get_player_flags());
3537
                }
3538
 
3539
#ifndef RELEASE
3540
                if (!(Game_mode&GM_MULTI && Show_kill_list))
3541
                        show_time(canvas);
3542
#endif
3543
 
3544
#if defined(DXX_BUILD_DESCENT_II)
3545
                if (PlayerCfg.CockpitMode[1] != CM_LETTERBOX && PlayerCfg.CockpitMode[1] != CM_REAR_VIEW)
3546
                {
3547
                        hud_show_flag(canvas, player_info, multires_gauge_graphic);
3548
                        hud_show_orbs(canvas, player_info, multires_gauge_graphic);
3549
                }
3550
#endif
3551
                HUD_render_message_frame(canvas);
3552
 
3553
                if (PlayerCfg.CockpitMode[1]!=CM_STATUS_BAR)
3554
                        hud_show_lives(hudctx, HUD_SCALE_AR(grd_curscreen->get_screen_width(), grd_curscreen->get_screen_height(), multires_gauge_graphic), player_info);
3555
                if (Game_mode&GM_MULTI && Show_kill_list)
3556
                        hud_show_kill_list(vcobjptr, canvas);
3557
                if (PlayerCfg.CockpitMode[1] != CM_LETTERBOX)
3558
                        show_reticle(canvas, player_info, PlayerCfg.ReticleType, 1);
3559
                if (PlayerCfg.CockpitMode[1] != CM_LETTERBOX && Newdemo_state != ND_STATE_PLAYBACK && PlayerCfg.MouseFlightSim && PlayerCfg.MouseFSIndicator)
3560
                {
3561
                        const auto gwidth = canvas.cv_bitmap.bm_w;
3562
                        const auto gheight = canvas.cv_bitmap.bm_h;
3563
                        auto &raw_mouse_axis = Controls.raw_mouse_axis;
3564
                        show_mousefs_indicator(canvas, raw_mouse_axis[0], raw_mouse_axis[1], raw_mouse_axis[2], gwidth / 2, gheight / 2, gheight / 4);
3565
                }
3566
        }
3567
 
3568
        if (Rear_view && PlayerCfg.CockpitMode[1]!=CM_REAR_VIEW) {
3569
                HUD_render_message_frame(canvas);
3570
                gr_set_fontcolor(canvas, BM_XRGB(0, 31, 0), -1);
3571
                auto &game_font = *GAME_FONT;
3572
                gr_string(canvas, game_font, 0x8000, canvas.cv_bitmap.bm_h - LINE_SPACING(game_font, game_font), TXT_REAR_VIEW);
3573
        }
3574
}
3575
 
3576
//print out some player statistics
3577
void render_gauges()
3578
{
3579
        auto &Objects = LevelUniqueObjectState.Objects;
3580
        auto &vmobjptr = Objects.vmptr;
3581
        auto &plrobj = get_local_plrobj();
3582
        auto &player_info = plrobj.ctype.player_info;
3583
        const auto energy = f2ir(player_info.energy);
3584
        auto &pl_flags = player_info.powerup_flags;
3585
        const auto cloak = (pl_flags & PLAYER_FLAGS_CLOAKED);
3586
 
3587
        Assert(PlayerCfg.CockpitMode[1]==CM_FULL_COCKPIT || PlayerCfg.CockpitMode[1]==CM_STATUS_BAR);
3588
 
3589
        auto shields = f2ir(plrobj.shields);
3590
        if (shields < 0 ) shields = 0;
3591
 
3592
        gr_set_default_canvas();
3593
        gr_set_curfont(*grd_curcanv, GAME_FONT);
3594
 
3595
        if (Newdemo_state == ND_STATE_RECORDING)
3596
        {
3597
                const auto homing_object_dist = player_info.homing_object_dist;
3598
                if (homing_object_dist >= 0)
3599
                        newdemo_record_homing_distance(homing_object_dist);
3600
        }
3601
 
3602
        const local_multires_gauge_graphic multires_gauge_graphic{};
3603
        const hud_draw_context_hs_mr hudctx(*grd_curcanv, grd_curscreen->get_screen_width(), grd_curscreen->get_screen_height(), multires_gauge_graphic);
3604
        draw_weapon_boxes(hudctx, player_info);
3605
        if (PlayerCfg.CockpitMode[1] == CM_FULL_COCKPIT) {
3606
                if (Newdemo_state == ND_STATE_RECORDING)
3607
                        newdemo_record_player_energy(energy);
3608
                draw_energy_bar(hudctx, energy);
3609
#if defined(DXX_BUILD_DESCENT_I)
3610
                if (PlayerCfg.HudMode == HudType::Standard)
3611
#elif defined(DXX_BUILD_DESCENT_II)
3612
                if (Newdemo_state==ND_STATE_RECORDING )
3613
                        newdemo_record_player_afterburner(Afterburner_charge);
3614
                draw_afterburner_bar(hudctx, Afterburner_charge);
3615
#endif
3616
                show_bomb_count(hudctx.canvas, player_info, hudctx.xscale(BOMB_COUNT_X), hudctx.yscale(BOMB_COUNT_Y), gr_find_closest_color(0, 0, 0), 0, 0);
3617
                draw_player_ship(hudctx, player_info, cloak, SHIP_GAUGE_X, SHIP_GAUGE_Y);
3618
 
3619
                if (player_info.powerup_flags & PLAYER_FLAGS_INVULNERABLE)
3620
                        draw_invulnerable_ship(hudctx, plrobj);
3621
                else
3622
                        draw_shield_bar(hudctx, shields);
3623
                draw_numerical_display(hudctx, shields, energy);
3624
 
3625
                if (Newdemo_state==ND_STATE_RECORDING)
3626
                {
3627
                        newdemo_record_player_shields(shields);
3628
                        newdemo_record_player_flags(player_info.powerup_flags.get_player_flags());
3629
                }
3630
                draw_keys_state(hudctx, player_info.powerup_flags).draw_all_cockpit_keys();
3631
 
3632
                show_homing_warning(hudctx, player_info.homing_object_dist);
3633
                draw_wbu_overlay(hudctx);
3634
 
3635
        } else if (PlayerCfg.CockpitMode[1] == CM_STATUS_BAR) {
3636
 
3637
                if (Newdemo_state == ND_STATE_RECORDING)
3638
                        newdemo_record_player_energy(energy);
3639
                sb_draw_energy_bar(hudctx, energy);
3640
#if defined(DXX_BUILD_DESCENT_I)
3641
                if (PlayerCfg.HudMode == HudType::Standard)
3642
#elif defined(DXX_BUILD_DESCENT_II)
3643
                if (Newdemo_state==ND_STATE_RECORDING )
3644
                        newdemo_record_player_afterburner(Afterburner_charge);
3645
                sb_draw_afterburner(hudctx, player_info);
3646
                if (PlayerCfg.HudMode == HudType::Standard && weapon_box_user[1] == WBU_WEAPON)
3647
#endif
3648
                        show_bomb_count(hudctx.canvas, player_info, hudctx.xscale(SB_BOMB_COUNT_X), hudctx.yscale(SB_BOMB_COUNT_Y), gr_find_closest_color(0, 0, 0), 0, 0);
3649
 
3650
                draw_player_ship(hudctx, player_info, cloak, SB_SHIP_GAUGE_X, SB_SHIP_GAUGE_Y);
3651
 
3652
                if (player_info.powerup_flags & PLAYER_FLAGS_INVULNERABLE)
3653
                        draw_invulnerable_ship(hudctx, plrobj);
3654
                else
3655
                        sb_draw_shield_bar(hudctx, shields);
3656
                sb_draw_shield_num(hudctx, shields);
3657
 
3658
                if (Newdemo_state==ND_STATE_RECORDING)
3659
                {
3660
                        newdemo_record_player_shields(shields);
3661
                        newdemo_record_player_flags(player_info.powerup_flags.get_player_flags());
3662
                }
3663
                draw_keys_state(hudctx, player_info.powerup_flags).draw_all_statusbar_keys();
3664
 
3665
                sb_show_lives(hudctx, HUD_SCALE_AR(grd_curscreen->get_screen_width(), grd_curscreen->get_screen_height(), hudctx.multires_gauge_graphic), player_info);
3666
                sb_show_score(hudctx, player_info);
3667
 
3668
                if ((Game_mode&GM_MULTI) && !(Game_mode & GM_MULTI_COOP))
3669
                {
3670
                }
3671
                else
3672
                {
3673
                        sb_show_score_added(hudctx);
3674
                }
3675
        }
3676
#if defined(DXX_BUILD_DESCENT_I)
3677
        else
3678
                draw_player_ship(hudctx, player_info, cloak, SB_SHIP_GAUGE_X, SB_SHIP_GAUGE_Y);
3679
#endif
3680
}
3681
 
3682
//      ---------------------------------------------------------------------------------------------------------
3683
//      Call when picked up a quad laser powerup.
3684
//      If laser is active, set old_weapon[0] to -1 to force redraw.
3685
void update_laser_weapon_info(void)
3686
{
3687
        if (old_weapon[0] == 0)
3688
                        old_weapon[0] = -1;
3689
}
3690
 
3691
#if defined(DXX_BUILD_DESCENT_II)
3692
static std::array<int, 2> overlap_dirty;
3693
 
3694
//draws a 3d view into one of the cockpit windows.  win is 0 for left,
3695
//1 for right.  viewer is object.  NULL object means give up window
3696
//user is one of the WBU_ constants.  If rear_view_flag is set, show a
3697
//rear view.  If label is non-NULL, print the label at the top of the
3698
//window.
3699
void do_cockpit_window_view(int win,int user)
3700
{
3701
        Assert(user == WBU_WEAPON || user == WBU_STATIC);
3702
        if (user == WBU_STATIC && weapon_box_user[win] != WBU_STATIC)
3703
                static_time[win] = 0;
3704
        if (weapon_box_user[win] == WBU_WEAPON || weapon_box_user[win] == WBU_STATIC)
3705
                return;         //already set
3706
        weapon_box_user[win] = user;
3707
        if (overlap_dirty[win]) {
3708
                gr_set_default_canvas();
3709
                overlap_dirty[win] = 0;
3710
        }
3711
}
3712
 
3713
void do_cockpit_window_view(const int win, const object &viewer, const int rear_view_flag, const int user, const char *const label, const player_info *const player_info)
3714
{
3715
        grs_canvas window_canv;
3716
        static grs_canvas overlap_canv;
3717
        const auto viewer_save = Viewer;
3718
        int boxnum;
3719
        static int window_x,window_y;
3720
        const gauge_box *box;
3721
        int rear_view_save = Rear_view;
3722
 
3723
        box = NULL;
3724
 
3725
        window_rendered_data window;
3726
        update_rendered_data(window, viewer, rear_view_flag);
3727
 
3728
        weapon_box_user[win] = user;                                            //say who's using window
3729
 
3730
        Viewer = &viewer;
3731
        Rear_view = rear_view_flag;
3732
 
3733
        const local_multires_gauge_graphic multires_gauge_graphic{};
3734
        const hud_draw_context_hs_mr hudctx(window_canv, grd_curscreen->get_screen_width(), grd_curscreen->get_screen_height(), multires_gauge_graphic);
3735
        if (PlayerCfg.CockpitMode[1] == CM_FULL_SCREEN)
3736
        {
3737
                const unsigned w = HUD_SCALE_AR(hudctx.xscale, hudctx.yscale)(multires_gauge_graphic.get(106, 44));
3738
                const unsigned h = w;
3739
 
3740
                const int dx = (win == 0) ? -(w + (w / 10)) : (w / 10);
3741
 
3742
                window_x = grd_curscreen->get_screen_width() / 2 + dx;
3743
                window_y = grd_curscreen->get_screen_height() - h - (SHEIGHT / 15);
3744
 
3745
                gr_init_sub_canvas(window_canv, grd_curscreen->sc_canvas, window_x, window_y, w, h);
3746
        }
3747
        else {
3748
                if (PlayerCfg.CockpitMode[1] == CM_FULL_COCKPIT)
3749
                        boxnum = (COCKPIT_PRIMARY_BOX)+win;
3750
                else if (PlayerCfg.CockpitMode[1] == CM_STATUS_BAR)
3751
                        boxnum = (SB_PRIMARY_BOX)+win;
3752
                else
3753
                        goto abort;
3754
 
3755
                box = &gauge_boxes[boxnum];
3756
                gr_init_sub_canvas(window_canv, grd_curscreen->sc_canvas, hudctx.xscale(box->left), hudctx.yscale(box->top), hudctx.xscale(box->right - box->left + 1), hudctx.yscale(box->bot - box->top + 1));
3757
        }
3758
 
3759
        gr_set_current_canvas(window_canv);
3760
 
3761
        render_frame(*grd_curcanv, 0, window);
3762
 
3763
        //      HACK! If guided missile, wake up robots as necessary.
3764
        if (viewer.type == OBJ_WEAPON) {
3765
                // -- Used to require to be GUIDED -- if (viewer->id == GUIDEDMISS_ID)
3766
                wake_up_rendered_objects(viewer, window);
3767
        }
3768
 
3769
        if (label) {
3770
                if (Color_0_31_0 == -1)
3771
                        Color_0_31_0 = BM_XRGB(0,31,0);
3772
                gr_set_fontcolor(*grd_curcanv, Color_0_31_0, -1);
3773
                auto &game_font = *GAME_FONT;
3774
                gr_string(*grd_curcanv, game_font, 0x8000, FSPACY(1), label);
3775
        }
3776
 
3777
        if (player_info)        // only non-nullptr for WBU_GUIDED
3778
        {
3779
                show_reticle(*grd_curcanv, *player_info, RET_TYPE_CROSS_V1, 0);
3780
        }
3781
 
3782
        if (PlayerCfg.CockpitMode[1] == CM_FULL_SCREEN) {
3783
                int small_window_bottom,big_window_bottom,extra_part_h;
3784
 
3785
                gr_ubox(*grd_curcanv, 0, 0, grd_curcanv->cv_bitmap.bm_w, grd_curcanv->cv_bitmap.bm_h, BM_XRGB(0,0,32));
3786
 
3787
                //if the window only partially overlaps the big 3d window, copy
3788
                //the extra part to the visible screen
3789
 
3790
                big_window_bottom = SHEIGHT - 1;
3791
 
3792
                if (window_y > big_window_bottom) {
3793
 
3794
                        //the small window is completely outside the big 3d window, so
3795
                        //copy it to the visible screen
3796
 
3797
                        gr_set_default_canvas();
3798
 
3799
                        gr_bitmap(*grd_curcanv, window_x, window_y, window_canv.cv_bitmap);
3800
 
3801
                        overlap_dirty[win] = 1;
3802
                }
3803
                else {
3804
 
3805
                        small_window_bottom = window_y + window_canv.cv_bitmap.bm_h - 1;
3806
 
3807
                        extra_part_h = small_window_bottom - big_window_bottom;
3808
 
3809
                        if (extra_part_h > 0) {
3810
                                gr_init_sub_canvas(overlap_canv, window_canv, 0, window_canv.cv_bitmap.bm_h-extra_part_h, window_canv.cv_bitmap.bm_w, extra_part_h);
3811
                                gr_set_default_canvas();
3812
                                gr_bitmap(*grd_curcanv, window_x, big_window_bottom + 1, overlap_canv.cv_bitmap);
3813
                                overlap_dirty[win] = 1;
3814
                        }
3815
                }
3816
        }
3817
        else if (PlayerCfg.CockpitMode[1] == CM_FULL_COCKPIT)
3818
        {
3819
                /* `draw_wbu_overlay` has hard-coded x/y coordinates with their
3820
                 * origin at the root of the screen canvas, not the window
3821
                 * canvas.  Reset to screen canvas so that the coordinates are
3822
                 * interpreted properly.
3823
                 */
3824
                gr_set_default_canvas();
3825
                draw_wbu_overlay(hud_draw_context_hs_mr(*grd_curcanv, grd_curscreen->get_screen_width(), grd_curscreen->get_screen_height(), multires_gauge_graphic));
3826
        }
3827
 
3828
        //force redraw when done
3829
        old_weapon[win] = -1;
3830
 
3831
abort:;
3832
 
3833
        Viewer = viewer_save;
3834
 
3835
        Rear_view = rear_view_save;
3836
        /* grd_curcanv may point to `window_canv`; if so, grd_curcanv
3837
         * would become a dangling pointer when `window_canv` goes out of
3838
         * scope at the end of the block.  Redirect it to the default screen
3839
         * to avoid pointing to freed memory.  Setting grd_curcanv to
3840
         * nullptr would be better, but some code assumes that grd_curcanv
3841
         * is never nullptr, so instead set it to the default canvas.
3842
         * Eventually, grd_curcanv will be removed entirely.
3843
         */
3844
        gr_set_default_canvas();
3845
}
3846
#endif
3847
}