Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

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