1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: st_stuff.c 1452 2019-08-03 07:03:27Z wesleyjohnson $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Portions Copyright (C) 1998-2000 by DooM Legacy Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 //
20 // $Log: st_stuff.c,v $
21 // Revision 1.22  2003/08/11 13:50:00  hurdler
22 // go final + translucent HUD + fix spawn in net game
23 //
24 // Revision 1.21  2001/08/20 21:37:35  hurdler
25 // fix palette in splitscreen + hardware mode
26 //
27 // Revision 1.20  2001/08/20 20:40:39  metzgermeister
28 //
29 // Revision 1.19  2001/08/08 20:34:43  hurdler
30 // Big TANDL update
31 //
32 // Revision 1.18  2001/05/16 21:21:14  bpereira
33 // Revision 1.17  2001/04/01 17:35:07  bpereira
34 // Revision 1.16  2001/03/03 06:17:34  bpereira
35 // Revision 1.15  2001/02/24 13:35:21  bpereira
36 // Revision 1.14  2001/02/10 13:05:45  hurdler
37 //
38 // Revision 1.13  2001/01/31 17:14:07  hurdler
39 // Add cv_scalestatusbar in hardware mode
40 //
41 // Revision 1.12  2001/01/25 22:15:44  bpereira
42 // added heretic support
43 //
44 // Revision 1.11  2000/11/02 19:49:37  bpereira
45 //
46 // Revision 1.10  2000/10/04 16:34:51  hurdler
47 // Change a little the presentation of monsters/secrets numbers
48 //
49 // Revision 1.9  2000/10/02 18:25:45  bpereira
50 // Revision 1.8  2000/10/01 10:18:19  bpereira
51 //
52 // Revision 1.7  2000/10/01 01:12:00  hurdler
53 // Add number of monsters and secrets in overlay
54 //
55 // Revision 1.6  2000/09/28 20:57:18  bpereira
56 //
57 // Revision 1.5  2000/09/25 19:28:15  hurdler
58 // Enable Direct3D support as OpenGL
59 //
60 // Revision 1.4  2000/09/21 16:45:09  bpereira
61 // Revision 1.3  2000/08/31 14:30:56  bpereira
62 // Revision 1.2  2000/02/27 00:42:11  hurdler
63 // Revision 1.1.1.1  2000/02/22 20:32:33  hurdler
64 // Initial import into CVS (v1.29 pr3)
65 //
66 //
67 // DESCRIPTION:
68 //      Status bar code.
69 //      Does the face/direction indicator animatin.
70 //      Does palette indicators as well (red pain/berserk, bright pickup)
71 //
72 //-----------------------------------------------------------------------------
73 
74 #include "doomincl.h"
75 
76 #include "am_map.h"
77 
78 #include "g_game.h"
79 #include "m_cheat.h"
80 
81 #include "screen.h"
82 #include "r_local.h"
83 #include "p_local.h"
84 #include "p_inter.h"
85 #include "m_random.h"
86 
87 #include "st_stuff.h"
88 #include "st_lib.h"
89 #include "i_video.h"
90 #include "v_video.h"
91 
92 #include "keys.h"
93 
94 #include "z_zone.h"
95 #include "hu_stuff.h"
96 #include "d_main.h"
97 
98 #ifdef HWRENDER
99 #include "hardware/hw_drv.h"
100 #include "hardware/hw_main.h"
101 #endif
102 
103 // Doom and Heretic use ST_Drawer.
104 // Almost everything else that is ST_  is Doom Only.
105 // Heretic has its own status bar drawer that uses SB_ functions in sb_bar.c.
106 // There is very little shared code, but some sharing attempts are being made.
107 
108 //protos
109 static void ST_Create_Widgets(void);
110 
111 
112 #define FLASH_COLOR  0x72
113 
114 //
115 // STATUS BAR DATA
116 //
117 
118 // Palette indices.
119 // For damage/bonus red-/gold-shifts
120 #define STARTREDPALS            1
121 #define STARTBONUSPALS          9
122 #define NUMREDPALS              8
123 #define NUMBONUSPALS            4
124 // Radiation suit, green shift.
125 #define RADIATIONPAL            13
126 
127 // N/256*100% probability
128 //  that the normal face state will change
129 #define ST_FACEPROBABILITY              96
130 
131 // For Responder
132 #define ST_TOGGLECHAT           KEY_ENTER
133 
134 // Location of status bar
135   //added:08-01-98:status bar position changes according to resolution.
136 #define ST_FX                     143
137 // This is now dynamic
138 // #define ST_Y                    stbar_y
139 
140 // Number of status faces.
141 #define ST_NUMPAINFACES         5
142 #define ST_NUMSTRAIGHTFACES     3
143 #define ST_NUMTURNFACES         2
144 #define ST_NUMSPECIALFACES      3
145 
146 #define ST_FACESTRIDE \
147           (ST_NUMSTRAIGHTFACES+ST_NUMTURNFACES+ST_NUMSPECIALFACES)
148 
149 #define ST_NUMEXTRAFACES        2
150 
151 #define ST_NUMFACES \
152           (ST_FACESTRIDE*ST_NUMPAINFACES+ST_NUMEXTRAFACES)
153 
154 #define ST_TURNOFFSET           (ST_NUMSTRAIGHTFACES)
155 #define ST_OUCHOFFSET           (ST_TURNOFFSET + ST_NUMTURNFACES)
156 #define ST_EVILGRINOFFSET       (ST_OUCHOFFSET + 1)
157 #define ST_RAMPAGEOFFSET        (ST_EVILGRINOFFSET + 1)
158 #define ST_GODFACE              (ST_NUMPAINFACES*ST_FACESTRIDE)
159 #define ST_DEADFACE             (ST_GODFACE+1)
160 
161 #define ST_FACESX               143
162 #define ST_FACESY               0
163 
164 #define ST_EVILGRINCOUNT        (2*TICRATE)
165 #define ST_STRAIGHTFACECOUNT    (TICRATE/2)
166 #define ST_TURNCOUNT            (1*TICRATE)
167 #define ST_OUCHCOUNT            (1*TICRATE)
168 #define ST_RAMPAGEDELAY         (2*TICRATE)
169 
170 #define ST_MUCHPAIN             20
171 
172 
173 // Location and size of statistics,
174 //  justified according to widget type.
175 // Problem is, within which space? STbar? Screen?
176 // Note: this could be read in by a lump.
177 //       Problem is, is the stuff rendered
178 //       into a buffer,
179 //       or into the frame buffer?
180 
181 // AMMO number pos.
182 #define ST_AMMOWIDTH            3
183 #define ST_AMMOX                44
184 #define ST_AMMOY                3
185 
186 // HEALTH number pos.
187 #define ST_HEALTHWIDTH          3
188 #define ST_HEALTHX              90
189 #define ST_HEALTHY              3
190 
191 // Weapon pos.
192 #define ST_ARMSX                111
193 #define ST_ARMSY                4
194 #define ST_ARMSBGX              104
195 #define ST_ARMSBGY              0
196 #define ST_ARMSXSPACE           12
197 #define ST_ARMSYSPACE           10
198 
199 // Frags pos.
200 #define ST_FRAGSX               138
201 #define ST_FRAGSY               3
202 #define ST_FRAGSWIDTH           2
203 
204 // ARMOR number pos.
205 #define ST_ARMORWIDTH           3
206 #define ST_ARMORX               221
207 #define ST_ARMORY               3
208 
209 // Key icon positions.
210 #define ST_KEYSBOX_X   236
211 #define ST_KEYSBOX_Y   2
212 #define ST_KEYSBOX_W   13
213 #define ST_KEYSBOX_H   30
214 // They appear in a vertical column, so share x positions.
215 #define ST_KEY_WIDTH   6
216 #define ST_KEY_HEIGHT  5
217 #define ST_KEYX        239
218 #define ST_KEYDX       2
219 #define ST_KEYDY       2
220 static uint8_t  keybox_y[6] = { 3, 13, 23, 3, 13, 23 };
221 static uint8_t  keybox_dual_x[6] =
222  { ST_KEYX-ST_KEYDX, ST_KEYX-ST_KEYDX, ST_KEYX-ST_KEYDX,
223    ST_KEYX+ST_KEYDX, ST_KEYX+ST_KEYDX, ST_KEYX+ST_KEYDX };
224 static uint8_t  keybox_dual_y[6] =
225  {  3-ST_KEYDY, 13-ST_KEYDY, 23-ST_KEYDY,
226     3+ST_KEYDY, 13+ST_KEYDY, 23+ST_KEYDY };
227 
228 // Ammunition counter, and max ammo, in two columns in status bar.
229 // Max ammo changes when backpack aquired.
230 #define ST_AMMOS_WIDTH          3
231 #define ST_MAXAMMOS_WIDTH       3
232 #define ST_AMMOSX               288
233 #define ST_MAXAMMOSX            314
234 static uint8_t  ammobox_y[4] = { 5, 11, 23, 17 };
235 
236 
237 //faB: unused stuff from the Doom alpha version ?
238 // pistol
239 //#define ST_WEAPON0X           110
240 //#define ST_WEAPON0Y           4
241 // shotgun
242 //#define ST_WEAPON1X           122
243 //#define ST_WEAPON1Y           4
244 // chain gun
245 //#define ST_WEAPON2X           134
246 //#define ST_WEAPON2Y           4
247 // missile launcher
248 //#define ST_WEAPON3X           110
249 //#define ST_WEAPON3Y           13
250 // plasma gun
251 //#define ST_WEAPON4X           122
252 //#define ST_WEAPON4Y           13
253 // bfg
254 //#define ST_WEAPON5X           134
255 //#define ST_WEAPON5Y           13
256 
257 // WPNS title
258 //#define ST_WPNSX              109
259 //#define ST_WPNSY              23
260 
261  // DETH title
262 //#define ST_DETHX              109
263 //#define ST_DETHY              23
264 
265 //Incoming messages window location
266 // #define ST_MSGTEXTX     (viewwindowx)
267 // #define ST_MSGTEXTY     (viewwindowy+viewheight-18)
268 //#define ST_MSGTEXTX             0
269 //#define ST_MSGTEXTY             0     //added:08-01-98:unused
270 // Dimensions given in characters.
271 #define ST_MSGWIDTH             52
272 // Or shall I say, in lines?
273 #define ST_MSGHEIGHT            1
274 
275 #define ST_OUTTEXTX             0
276 #define ST_OUTTEXTY             6
277 
278 // Width, in characters again.
279 #define ST_OUTWIDTH             52
280  // Height, in lines.
281 #define ST_OUTHEIGHT            1
282 
283 #if 0
284 // UNUSED
285 #define ST_MAPWIDTH     \
286     (strlen(mapnames[(gameepisode-1)*9+(gamemap-1)]))
287 
288 //added:24-01-98:unused ?
289 //#define ST_MAPTITLEX  (vid.width - ST_MAPWIDTH * ST_CHATFONTWIDTH)
290 #endif
291 
292 #define ST_MAPTITLEY            0
293 #define ST_MAPHEIGHT            1
294 
295 
296 int stbar_height = ST_HEIGHT;
297 int stbar_y = BASEVIDHEIGHT - ST_HEIGHT;
298 int stbar_x = 0;
299 int stbar_scalex, stbar_scaley;
300 int stbar_fg = FG | V_TRANSLUCENTPATCH;
301 
302 
303 //added:02-02-98: set true if widgets coords need to be recalculated
304 // Set by functions that change the window or status bar size.
305 boolean     stbar_recalc;
306 
307 // ST_Start() has just been called
308 boolean     st_force_refresh;
309 
310 // facegraphics loaded
311 static byte  facegraphics_loaded = false;
312 
313 // used for timing
314 static unsigned int     st_clock;
315 
316 // used for making messages go away
317 static int              st_msgcounter=0;
318 
319 // used when in chat
320 static st_chatstateenum_t       st_chatstate;
321 
322 // whether left-side main status bar is active
323 boolean                 stbar_on;
324 
325 // whether status bar chat is active
326 static boolean          st_chat;
327 
328 // value of st_chat before message popped up
329 static boolean          st_oldchat;
330 
331 // whether chat window has the cursor on
332 static boolean          st_cursor_on;
333 
334 // !deathmatch
335 static boolean          st_not_deathmatch;
336 
337 // main bar left
338 static patch_t*         sbar = NULL;
339 
340 // 0-9, tall numbers, minus at [10], percent at [11].
341 static patch_t*         tallnum[12];
342 
343 // 0-9, short, yellow (,different!) numbers
344 static patch_t*         shortnum[12];
345 
346 // 3 key-cards, 3 skulls
347 static patch_t*         keys[NUMCARDS];
348 
349 // face status patches
350 static patch_t*         faces[ST_NUMFACES];
351 
352 // face background
353 static patch_t*         faceback;
354 
355  // main bar right
356 static patch_t*         armsbg;
357 
358 // weapon ownership patches
359 static patch_t*         arms[6][2];
360 
361 // ready-weapon widget
362 static st_number_t      w_ready;
363 
364  // in deathmatch only, summary of frags stats
365 static st_number_t      w_frags;
366 
367 // health widget
368 static st_number_t      w_health;
369 
370 // arms background
371 static st_binicon_t     w_armsbg;
372 
373 // weapon ownership widgets
374 static st_multicon_t    w_arms[6];
375 
376 // face status widget
377 static st_multicon_t    w_faces;
378 
379 // keycard widgets
380 static st_multicon_t    w_keyboxes[6];
381 
382 // armor widget
383 static st_number_t      w_armor;
384 
385 // ammo widgets
386 static st_number_t      w_ammo[4];
387 
388 // max ammo widgets
389 static st_number_t      w_maxammo[4];
390 
391 
392 // ------------------------------------------
393 // Status bar state.
394 // Doom only.
395 // Single player only (can handle only one status bar).
396 // Splitscreen must use status overlay, which does not have state.
397 
398 // Status bar player, also used by Heretic status display
399 player_t*  st_plyr = NULL;
400 
401 // number of frags so far in deathmatch
402 static int      st_fragscount;
403 
404 // used to use appopriately pained face
405 static int      st_oldhealth = -1;
406 
407 // Doom only, but has room for Heretic weapons.
408 // used for evil grin
409 static boolean  oldweaponsowned[NUMWEAPONS];
410 
411  // count until face changes
412 static int      st_facecount = 0;
413 
414 // current face index, used by w_faces
415 static int      st_faceindex = 0;
416 
417 // holds key-type for each key box on bar
418 byte     st_card;  // card state displayed  (Doom, Heretic)
419 static byte     st_num_keyboxes;
420 static int      keyboxes[6];  // 0..3 keycards skulls, 4..6 dual display
421 
422 // a random number per tick
423 static int      st_randomnumber;
424 
425 
426 // ------------------------------------------
427 //        Doom status overlay variables
428 // ------------------------------------------
429 
430 // Doom only.
431 // Icons for status bar.
432 static lumpnum_t  sbo_health, sbo_frags, sbo_armor;
433 static lumpnum_t  sbo_ammo[NUMWEAPONS];
434 
435 
436 // ------------------------------------------
437 //        Doom status bar
438 // ------------------------------------------
439 
440 //
441 // STATUS BAR CODE
442 //
443 
444 
445 // Single player only, when stbar_on.
446 //  Global : st_plyr
ST_Refresh_Background(void)447 static void ST_Refresh_Background( void )
448 {
449     byte * colormap;
450 
451     // Draw background, with status bar flag settings
452     V_SetupDraw( BG | (stbar_fg & ~V_SCREENMASK) );
453 
454     // software mode copies patch to BG buffer,
455     // hardware modes directly draw the statusbar to the screen
456     V_DrawScaledPatch(stbar_x, stbar_y, sbar);
457 
458     // draw the faceback for the statusbarplayer
459     colormap = (st_plyr->skincolor) ?
460              SKIN_TO_SKINMAP( st_plyr->skincolor )
461            : & reg_colormaps[0]; // default green skin
462 
463     V_DrawMappedPatch (stbar_x+ST_FX, stbar_y, faceback, colormap);
464 
465     // copy the statusbar buffer to the screen
466     if( rendermode == render_soft )
467         V_CopyRect(0, vid.height-stbar_height, BG, vid.width, stbar_height, 0, vid.height-stbar_height, FG);
468 }
469 
470 
471 // Respond to keyboard input events,
472 //  intercept cheats.
ST_Responder(event_t * ev)473 boolean ST_Responder (event_t* ev)
474 {
475 
476   if (ev->type == ev_keyup)
477   {
478     // Filter automap on/off : activates the statusbar while automap is active
479     if( (ev->data1 & 0xffff0000) == AM_MSGHEADER )
480     {
481         switch(ev->data1)
482         {
483           case AM_MSGENTERED:
484             st_force_refresh = true;        // force refresh of status bar
485             break;
486 
487           case AM_MSGEXITED:
488             break;
489         }
490     }
491 
492   }
493   return false;
494 }
495 
496 // Global : st_plyr
ST_calcPainOffset(void)497 static int ST_calcPainOffset(void)
498 {
499     // [WDJ] FIXME : This thrashes when splitplayer, but it is correct.
500     static int  st_pain;
501     static int  oldhealth = -1;
502 
503     int  health = st_plyr->health > 100 ? 100 : st_plyr->health;
504     if (health != oldhealth)
505     {
506         st_pain = ST_FACESTRIDE * (((100 - health) * ST_NUMPAINFACES) / 101);
507         oldhealth = health;
508     }
509     return st_pain;
510 }
511 
512 
513 //
514 // This is a not-very-pretty routine which handles
515 //  the face states and their timing.
516 // the precedence of expressions is:
517 //  dead > evil grin > turned head > straight ahead
518 //
519 // Doom only.
520 // Single Player status only.
521 // Global : st_plyr
ST_updateFaceWidget(void)522 static void ST_updateFaceWidget(void)
523 {
524     static int  fw_last_attackdown_cnt = -1;
525     static int  fw_priority = 0;  // priority of face effects
526 
527     player_t *  plyr = st_plyr;
528     int         i;
529     angle_t     badguyangle;
530     angle_t     diffang;
531     boolean     doevilgrin;
532 
533     // Highest priority first
534     if (fw_priority < 10)
535     {
536         if (!plyr->health)
537         {
538             // dead
539             fw_priority = 9;
540             st_faceindex = ST_DEADFACE;
541             st_facecount = 1;
542         }
543     }
544 
545     if (fw_priority < 9)
546     {
547         if (plyr->bonuscount)
548         {
549             // picking up bonus
550             doevilgrin = false;
551 
552             // Doom only.
553             for (i=0;i<NUMWEAPONS;i++)
554             {
555                 if (oldweaponsowned[i] != plyr->weaponowned[i])
556                 {
557                     doevilgrin = true;
558                     oldweaponsowned[i] = plyr->weaponowned[i];
559                 }
560             }
561             if (doevilgrin)
562             {
563                 // evil grin if just picked up weapon
564                 fw_priority = 8;  // allow retrigger evil grin
565                 st_facecount = ST_EVILGRINCOUNT;
566                 st_faceindex = ST_calcPainOffset() + ST_EVILGRINOFFSET;
567             }
568         }
569 
570     }
571 
572     if (fw_priority < 8)
573     {
574         if (plyr->damagecount
575             && plyr->attacker
576             && plyr->attacker != plyr->mo)
577         {
578             // being attacked
579             fw_priority = 7;  // allow attack test retrigger
580 
581             // [WDJ] Ouch-face when damage>20, fix from DoomWiki, same as prboom
582 //            if (plyr->health - st_oldhealth > ST_MUCHPAIN) // orig bug
583             if (st_oldhealth - plyr->health > ST_MUCHPAIN)
584             {
585                 st_facecount = ST_TURNCOUNT;
586                 st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET;
587                 fw_priority = 8; // [WDJ] Necessary to keep ouchface visible
588             }
589             else
590             {
591                 badguyangle = R_PointToAngle2(plyr->mo->x,
592                                               plyr->mo->y,
593                                               plyr->attacker->x,
594                                               plyr->attacker->y);
595 
596                 if (badguyangle > plyr->mo->angle)
597                 {
598                     // whether right or left
599                     diffang = badguyangle - plyr->mo->angle;
600                     i = diffang > ANG180;
601                 }
602                 else
603                 {
604                     // whether left or right
605                     diffang = plyr->mo->angle - badguyangle;
606                     i = diffang <= ANG180;
607                 } // confusing, aint it?
608 
609 
610                 st_facecount = ST_TURNCOUNT;
611                 st_faceindex = ST_calcPainOffset();
612 
613                 if (diffang < ANG45)
614                 {
615                     // head-on
616                     st_faceindex += ST_RAMPAGEOFFSET;
617                 }
618                 else if (i)
619                 {
620                     // turn face right
621                     st_faceindex += ST_TURNOFFSET;
622                 }
623                 else
624                 {
625                     // turn face left
626                     st_faceindex += ST_TURNOFFSET+1;
627                 }
628             }
629         }
630     }
631 
632     if (fw_priority < 7)
633     {
634         // getting hurt because of your own damn stupidity
635         if (plyr->damagecount)
636         {
637             // [WDJ] Ouch-face when damage>20, fix from DoomWiki, same as prboom
638 //            if (plyr->health - st_oldhealth > ST_MUCHPAIN)
639             if (st_oldhealth - plyr->health > ST_MUCHPAIN)
640             {
641                 fw_priority = 7;  // no pain retrigger
642                 st_facecount = ST_TURNCOUNT;
643                 st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET;
644             }
645             else
646             {
647                 fw_priority = 6;  // allow pain test retrigger
648                 st_facecount = ST_TURNCOUNT;
649                 st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET;
650             }
651         }
652     }
653 
654     if (fw_priority < 6)
655     {
656         // rapid firing
657         if (plyr->attackdown)
658         {
659             if (fw_last_attackdown_cnt < 0)
660                 fw_last_attackdown_cnt = ST_RAMPAGEDELAY;
661             else if (--fw_last_attackdown_cnt == 0)
662             {
663                 fw_last_attackdown_cnt = 1;
664                 fw_priority = 5;  // continual retrigger, no timer
665                 st_facecount = 1;
666                 st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET;
667             }
668         }
669         else
670             fw_last_attackdown_cnt = -1;
671     }
672 
673     if (fw_priority < 5)
674     {
675         // invulnerability
676         if ((plyr->cheats & CF_GODMODE)
677             || plyr->powers[pw_invulnerability])
678         {
679             fw_priority = 4;  // continual retrigger, no timer
680             st_facecount = 1;
681             st_faceindex = ST_GODFACE;
682         }
683     }
684 
685     // look left or look right if the facecount has timed out
686     if (st_facecount == 0)
687     {
688         fw_priority = 0;  // clear
689         st_faceindex = ST_calcPainOffset() + (st_randomnumber % 3);
690         st_facecount = ST_STRAIGHTFACECOUNT;
691     }
692 
693     st_facecount--;
694 }
695 
ST_SameTeam(player_t * a,player_t * b)696 boolean ST_SameTeam(player_t *a,player_t *b)
697 {
698     switch( cv_teamplay.EV ) {
699        case 0 : return false;
700        case 1 : return (a->skincolor == b->skincolor);
701        case 2 : return (a->skin == b->skin);
702     }
703     return false;
704 }
705 
706 // count the frags of the playernum player
707 //Fab: made as a tiny routine so ST_overlayDrawer() can use it
708 //Boris: rename ST_countFrags in to ST_PlayerFrags for use anytime
709 //       when we need the frags
ST_PlayerFrags(int playernum)710 int ST_PlayerFrags (int playernum)
711 {
712     player_t * player = &players[playernum];
713     int    i,frags;
714 
715     frags = player->addfrags;
716     for (i=0 ; i<MAXPLAYERS ; i++)
717     {
718         if( ((cv_teamplay.EV == 0) && i != playernum)
719             || (cv_teamplay.EV && !ST_SameTeam(&players[i], player)) )
720             frags += player->frags[i];
721         else
722             frags -= player->frags[i];
723     }
724 
725     return frags;
726 }
727 
728 
729 // Doom only.
730 // Single player status bar only.
731 //  Global : st_plyr
732 // Called by: ST_Ticker
ST_updateWidgets(void)733 static void ST_updateWidgets(void)
734 {
735     static int  largeammo = NON_NUMBER; // means "n/a"
736 
737     int         i;
738     player_t *  plyr = st_plyr;
739 
740 #ifdef PARANOIA
741     if(!plyr)  return;  // not likely, but have soft fail
742 #endif
743     // must redirect the pointer if the ready weapon has changed.
744     //  if (w_ready.data != plyr->readyweapon)
745     //  {
746     if (plyr->weaponinfo[plyr->readyweapon].ammo == am_noammo)
747         w_ready.num = &largeammo;
748     else
749         w_ready.num = &plyr->ammo[plyr->weaponinfo[plyr->readyweapon].ammo];
750     //{
751     // static int tic=0;
752     // static int dir=-1;
753     // if (!(tic&15))
754     //   plyr->ammo[weaponinfo[plyr->readyweapon].ammo]+=dir;
755     // if (plyr->ammo[weaponinfo[plyr->readyweapon].ammo] == -100)
756     //   dir = 1;
757     // tic++;
758     // }
759 //    w_ready.data = plyr->readyweapon;
760 
761     // if (*w_ready.on)
762     //  STlib_updateNum(&w_ready, true);
763     // refresh weapon change
764     //  }
765 
766     // update keycard multiple widgets
767     if( plyr->cards != st_card )
768     {
769         // fraggle script can take a key, so keyboxes must not be sticky.
770         if( ~plyr->cards & st_card )
771         {
772              st_force_refresh = true;  // a card was taken
773              st_card = 0;
774         }
775         if( (plyr->cards & 0x07) && (plyr->cards & 0x38) )
776         {
777             // Have both keycards and skulls.
778             if(((st_card & 0x07) == 0) || ((st_card & 0x38) == 0))
779             {
780                 // Enable display of both skulls and keycards.
781                 st_num_keyboxes = 6;
782                 // Shift keybox[ 0..6 ] positions, into two vertical columns
783                 // Skulls will now be in [3..6]
784                 for( i=0; i<6; i++ )
785                 {
786                     keyboxes[i] = -1;  // clear previous recorded skulls
787                     w_keyboxes[i].x = stbar_x + keybox_dual_x[i],
788                     w_keyboxes[i].y = stbar_y + keybox_dual_y[i],
789                     w_keyboxes[i].command = STLIB_REFRESH;   // to clear old card positions
790                 }
791             }
792         }
793         st_card = plyr->cards;
794 
795         for (i=0;i<3;i++)
796         {
797             // keycards
798             keyboxes[i] = ((st_card >> i) & 0x01) ? i : -1;
799 
800             // skull keys
801             if ((st_card >> i) & 0x08)
802             {
803                 keyboxes[i+3] = i+3;  // dual display
804                 if( st_num_keyboxes == 3 )
805                     keyboxes[i] = i+3;  // only skull display
806             }
807             else
808             {
809                 keyboxes[i+3] = -1;  // skull off in dual display
810             }
811         }
812     }
813 
814     // refresh everything if this is him coming back to life
815     ST_updateFaceWidget();
816     st_oldhealth = plyr->health;
817 
818     // used by the w_armsbg widget
819     st_not_deathmatch = ! deathmatch;
820 
821     st_fragscount = ST_PlayerFrags(statusbarplayer);
822 
823     // get rid of chat window if up because of message
824     if (!--st_msgcounter)
825         st_chat = st_oldchat;
826 }
827 
828 static boolean  st_stopped = true;
829 
830 //  Global : st_plyr
ST_Ticker(void)831 void ST_Ticker (void)
832 {
833     if( st_stopped )
834         return;
835 
836     if( EN_heretic )
837     {
838         SB_Heretic_Ticker();
839         return;
840     }
841 
842     // Doom only.
843     st_clock++;
844     st_randomnumber = M_Random();
845 
846     // Update immediately upon display changes.
847     if((cv_viewsize.value<11) || automapactive )
848         ST_updateWidgets();
849 }
850 
851 // These are used by Heretic too.
852 int st_palette = 0;
853 byte pickupflash_table[ 4 ] = { 6, 5, 4, 3 }; // Vanilla=[3]=3
854 
855 // Single and SplitPlayer, Software and Hardware Render.
856 // Called by: R_SetupFrame, from R_RenderPlayerView, HWR_RenderPlayerView
ST_doPaletteStuff(player_t * plyr)857 void ST_doPaletteStuff( player_t * plyr )
858 {
859     int  palette;
860     int  red_cnt;
861 
862     red_cnt = plyr->damagecount;
863 
864     if (plyr->powers[pw_strength])
865     {
866         // slowly fade the berzerk out
867         int bzc = 12 - (plyr->powers[pw_strength]>>6);
868 
869         if (bzc > red_cnt)
870             red_cnt = bzc;
871     }
872 
873     if (red_cnt)
874     {
875         palette = STARTREDPALS + ((red_cnt+7)>>3);
876 
877         if (palette >= (STARTREDPALS+NUMREDPALS))
878             palette = STARTREDPALS+NUMREDPALS-1;
879     }
880     else
881     if (plyr->bonuscount && (cv_pickupflash.EV>=2))
882     {
883         // Pickup object palette flash.
884         palette = STARTBONUSPALS
885            + ((plyr->bonuscount+7)>>(pickupflash_table[cv_pickupflash.EV]));
886 
887         if (palette >= (STARTBONUSPALS+NUMBONUSPALS))
888             palette = STARTBONUSPALS+NUMBONUSPALS-1;
889     }
890     else
891     if ( plyr->powers[pw_ironfeet] > BLINKTHRESHOLD
892          || plyr->powers[pw_ironfeet]&0x08)  // blink rate
893         palette = RADIATIONPAL;
894     else
895         palette = 0;
896 
897 
898     //added:28-02-98:quick hack underwater palette
899     /*if (plyr->mo &&
900         (plyr->mo->z + (((unsigned int)cv_viewheight.EV)<<FRACBITS) < plyr->mo->waterz) )
901         palette = RADIATIONPAL;*/
902 
903     if (palette != st_palette)
904     {
905         st_palette = palette;
906 
907 #ifdef HWRENDER
908         if( EN_HWR_flashpalette )  // some hardware draw can flash palette
909         {
910             // Imitate the palette flash
911             //debug_Printf("palette: %d\n", palette);
912             HWR_SetFlashPalette( palette );
913         }
914         else
915 #endif
916         {
917             // Splitscreen cannot use palette effects when 8bit palette draw,
918             // but other draw modes can.
919             if( ((cv_splitscreen.EV == 0) || (vid.drawmode != DRAW8PAL))
920                 || !palette )
921                 V_SetPalette (palette);
922         }
923     }
924 }
925 
926 // Set status palette 0 for camera.
ST_Palette0(void)927 void ST_Palette0( void )
928 {
929     if (st_palette)
930     {
931 #ifdef HWRENDER
932         if ( EN_HWR_flashpalette )
933         {
934             // Imitate the palette flash
935             HWR_SetFlashPalette( 0 );
936         }
937         else
938 #endif
939         {
940             V_SetPalette(0);
941         }
942 
943         // Record it as the status palette.
944         st_palette = 0;
945     }
946 }
947 
948 
949 // Single player only, when stbar_on.
950 // Called by: ST_Drawer, when stbar_on.
951 // STlib refresh enable is now setup by caller.
952 // Only called when stbar_on == true, so more tests are pointless.
ST_Draw_Widgets(void)953 static void ST_Draw_Widgets( void )
954 {
955     int  i;
956     player_t * plyr;
957 
958     // Draw stbar_fg, screen0 status bar
959     V_SetupDraw( stbar_fg );  // for all STlib
960 
961     if( cv_pickupflash.EV == 1 )
962     {
963         plyr = st_plyr;
964         // Pickup flash on the status bar.
965         if( plyr->ammo_pickup )
966         {
967             w_ready.command = STLIB_FLASH;
968         }
969         if( plyr->armor_pickup )
970         {
971             w_armor.command = STLIB_FLASH;
972         }
973         if( plyr->health_pickup )
974         {
975             w_health.command = STLIB_FLASH;
976         }
977 
978         if( plyr->key_pickup )
979         {
980             // Flash entire box.
981             // Do not know which one was picked up.
982             V_DrawScaledFill(stbar_x + ST_KEYSBOX_X, stbar_y + ST_KEYSBOX_Y,
983                        ST_KEYSBOX_W, ST_KEYSBOX_H, FLASH_COLOR);
984             // Prevent the key icons from performing background refresh.
985             for (i=0;i<st_num_keyboxes;i++)
986                 w_keyboxes[i].command = STLIB_FLASH;
987         }
988         else if( w_keyboxes[0].command == STLIB_FLASH_CLEAR
989                  && ( rendermode == render_soft ) )
990         {
991             // Restore the background
992             V_CopyRect(stbar_x + ST_KEYSBOX_X, stbar_y + ST_KEYSBOX_Y, BG,
993                        ST_KEYSBOX_W, ST_KEYSBOX_H,
994                        stbar_x + ST_KEYSBOX_X, stbar_y + ST_KEYSBOX_Y, stbar_fg);
995             // Refresh the key icons.
996             for (i=0;i<st_num_keyboxes;i++)
997                 w_keyboxes[i].command = STLIB_REFRESH;
998         }
999     }
1000 
1001     STlib_updateNum(&w_ready);  // current weapon ammo
1002 
1003     for (i=0;i<4;i++)
1004     {
1005         STlib_updateNum(&w_ammo[i]);
1006         STlib_updateNum(&w_maxammo[i]);
1007     }
1008 
1009     STlib_updatePercent(&w_health);
1010     STlib_updatePercent(&w_armor);
1011 
1012     STlib_updateBinIcon(&w_armsbg);
1013 
1014     if( deathmatch )
1015     {
1016         // frags on
1017         STlib_updateNum(&w_frags);
1018     }
1019     else
1020     {
1021         // arms on
1022         for (i=0;i<6;i++)
1023             STlib_updateMultIcon(&w_arms[i]);
1024     }
1025 
1026     STlib_updateMultIcon(&w_faces);
1027 
1028     for (i=0;i<st_num_keyboxes;i++)
1029         STlib_updateMultIcon(&w_keyboxes[i]);
1030 }
1031 
1032 
ST_Invalidate(void)1033 void ST_Invalidate(void)
1034 {
1035     st_force_refresh = true;
1036     st_card = 0;
1037 }
1038 
1039 static void ST_overlayDrawer ( byte status_position, player_t * plyr );
1040 
1041 // Doom and Heretic.
1042 // For player, and both splitscreen players.
1043 // Called by D_Display
ST_Drawer(boolean refresh)1044 void ST_Drawer ( boolean refresh )
1045 {
1046     // Respond to these changes immediately, so cannot be in any setup.
1047     stbar_on = (cv_viewsize.value<11) || automapactive;
1048 
1049     if( EN_heretic )
1050     {
1051         SB_Heretic_Drawer( refresh );
1052         return;
1053     }
1054 
1055     // Doom Only.
1056     //added:30-01-98:force a set of the palette by doPaletteStuff()
1057     if (vid.recalc)
1058         st_palette = -1;
1059 
1060     // Player status palette interactions moved to R_SetupFrame
1061     // so that Splitplayer can be handled.
1062 
1063     // Splitplayer restricted to overlay or status bar off.
1064     if( stbar_on )
1065     {
1066         // Single player only (st_plyr), keeping state.
1067         if (st_force_refresh || refresh || stbar_recalc )
1068         {
1069             // after ST_Start(), screen refresh needed, or vid mode change
1070             if (stbar_recalc)  //recalc widget coords after vid mode change
1071             {
1072                 ST_Create_Widgets ();
1073                 stbar_recalc = false;
1074             }
1075             st_force_refresh = false;
1076             st_card = 0;
1077 
1078             // This is not executed as frequently as drawing, so it is more
1079             // complicated, in order to keep ST_Draw_Widgets simpler.
1080 
1081             // Draw status bar background to BG buffer
1082             ST_Refresh_Background();   // st_plyr
1083 
1084             stlib_enable_erase = (rendermode == render_soft);
1085             stlib_force_refresh = true;  // stlib refreshes from BG buffer.
1086         }
1087         else
1088         {
1089             // Otherwise, update as little as possible
1090             stlib_force_refresh = false;
1091         }
1092         // Update all widgets using stlib.
1093         ST_Draw_Widgets();
1094     }
1095     else if( st_overlay_on )
1096     {
1097         // Overlay status over screen.
1098         // Any minimal state kept, must be per splitscreen (see hardware).
1099         // Does not use stlib.
1100         if( cv_splitscreen.EV )
1101         {
1102             if((vid.drawmode != DRAW8PAL) && st_palette != 0 )
1103                 ST_Palette0();
1104 
1105             // player 1 is upper
1106             ST_overlayDrawer ( 1, displayplayer_ptr );
1107             if( displayplayer2_ptr )
1108             {
1109                 // player 2 is lower
1110                 ST_overlayDrawer( 0, displayplayer2_ptr );
1111             }
1112         }
1113         else if( !playerdeadview )
1114         {
1115             ST_overlayDrawer( 0, displayplayer_ptr );
1116         }
1117     }
1118 }
1119 
1120 
1121 byte st_patches_loaded = 0;
1122 load_patch_t  st_patches[13] =
1123 {
1124   { &armsbg, "STARMS" }, // arms background
1125   { &sbar, "STBAR" },    // status bar background bits
1126   { NULL, NULL }
1127 };
1128 
1129 
1130 // Called by ST_Init, SCR_SetMode
ST_Load_Graphics(void)1131 void ST_Load_Graphics(void)
1132 {
1133     int         i;
1134     // [Stylinski] Compiler complains of possible buffer overrun, requires [10].
1135     char        namebuf[12];
1136     // [WDJ] all ST graphics are loaded endian fixed
1137     // [WDJ] Lock the status bar graphics against other texture users.
1138 
1139     if( EN_heretic )
1140     {
1141         SB_Heretic_Load_Graphics();
1142         return;
1143     }
1144 
1145     st_patches_loaded = 1;
1146     load_patch_list( st_patches );
1147 
1148     // Load the numbers, tall and short
1149     for (i=0;i<10;i++)
1150     {
1151         sprintf(namebuf, "STTNUM%d", i);
1152         tallnum[i] = W_CachePatchName(namebuf, PU_LOCK_SB);
1153 
1154         sprintf(namebuf, "STYSNUM%d", i);
1155         shortnum[i] = W_CachePatchName(namebuf, PU_LOCK_SB);
1156     }
1157     tallnum[10] = W_CachePatchName("STTMINUS", PU_LOCK_SB);
1158     tallnum[11] = W_CachePatchName("STTPRCNT", PU_LOCK_SB);
1159     shortnum[10] = NULL; // has no minus
1160     shortnum[11] = NULL; // has no percent
1161 
1162 
1163     // key cards
1164     // FreeDoom and DoomII have STKEYS 0..5.
1165     for (i=0;i<NUMCARDS;i++)
1166     {
1167         sprintf(namebuf, "STKEYS%d", i);
1168         keys[i] = W_CachePatchName(namebuf, PU_LOCK_SB);
1169     }
1170 
1171     // arms ownership widgets
1172     for (i=0;i<6;i++)
1173     {
1174         sprintf(namebuf, "STGNUM%d", i+2);
1175 
1176         // gray #
1177         arms[i][0] = W_CachePatchName(namebuf, PU_LOCK_SB);
1178 
1179         // yellow #
1180         arms[i][1] = shortnum[i+2];  // shared patch
1181     }
1182 
1183     // the original Doom uses 'STF' as base name for all face graphics
1184     ST_Load_FaceGraphics ("STF");
1185 }
1186 
1187 
1188 // made separate so that skins code can reload custom face graphics
ST_Load_FaceGraphics(const char * facestr)1189 void ST_Load_FaceGraphics (const char *facestr)
1190 {
1191     lumpnum_t ln;
1192     int   i,j;
1193     int   facenum;
1194     char  namelump[9];
1195     char* namebuf;
1196     // [WDJ] all ST graphics are loaded endian fixed
1197 
1198     //hack: make sure base face name is no more than 3 chars
1199     // bug: core dump fixed 19990220 by Kin
1200     // [WDJ] Cannot modify facestr.
1201     strncpy (namelump, facestr, 3);  // copy base name
1202     namelump[3] = '\0';
1203     // namebuf points after base face name, for appending to base name
1204     namebuf = namelump;
1205     while (*namebuf>' ') namebuf++;
1206 
1207     // face states
1208     facenum = 0;
1209     for (i=0;i<ST_NUMPAINFACES;i++)
1210     {
1211         for (j=0;j<ST_NUMSTRAIGHTFACES;j++)
1212         {
1213             sprintf(namebuf, "ST%d%d", i, j);
1214             faces[facenum++] = W_CachePatchName(namelump, PU_LOCK_SB);
1215         }
1216         sprintf(namebuf, "TR%d0", i);        // turn right
1217         faces[facenum++] = W_CachePatchName(namelump, PU_LOCK_SB);
1218         sprintf(namebuf, "TL%d0", i);        // turn left
1219         faces[facenum++] = W_CachePatchName(namelump, PU_LOCK_SB);
1220         sprintf(namebuf, "OUCH%d", i);       // ouch!
1221         faces[facenum++] = W_CachePatchName(namelump, PU_LOCK_SB);
1222         sprintf(namebuf, "EVL%d", i);        // evil grin ;)
1223         faces[facenum++] = W_CachePatchName(namelump, PU_LOCK_SB);
1224         sprintf(namebuf, "KILL%d", i);       // pissed off
1225         faces[facenum++] = W_CachePatchName(namelump, PU_LOCK_SB);
1226     }
1227     strcpy (namebuf, "GOD0");
1228     faces[facenum++] = W_CachePatchName(namelump, PU_LOCK_SB);
1229     strcpy (namebuf, "DEAD0");
1230     faces[facenum++] = W_CachePatchName(namelump, PU_LOCK_SB);
1231 
1232     // face backgrounds for different player colors
1233     //added:08-02-98: uses only STFB0, which is remapped to the right
1234     //                colors using the player translation tables, so if
1235     //                you add new player colors, it is automatically
1236     //                used for the statusbar.
1237     strcpy (namebuf, "B0");
1238     ln = W_CheckNumForName(namelump);
1239     if( VALID_LUMP( ln ) )
1240         faceback = W_CachePatchNum( ln, PU_LOCK_SB );
1241     else
1242         faceback = W_CachePatchName("STFB0", PU_LOCK_SB);
1243 
1244     ST_Invalidate();
1245     facegraphics_loaded = true;
1246 }
1247 
1248 
ST_Release_Graphics(void)1249 void ST_Release_Graphics(void)
1250 {
1251     int i;
1252 
1253     if( EN_heretic )
1254     {
1255         SB_Heretic_Release_Graphics();
1256         return;
1257     }
1258 
1259     //faB: GlidePatch_t are always purgeable
1260     if( st_patches_loaded )
1261     {
1262         st_patches_loaded = 0;
1263         release_patch_list( st_patches );
1264 
1265         // unload the numbers, tall and short
1266         release_patch_array( tallnum, 12 );
1267         release_patch_array( shortnum, 10 );
1268 
1269         // unload gray #'s
1270         for (i=0;i<6;i++)
1271             W_release_patch( arms[i][0] );
1272 
1273         // unload the key cards
1274         release_patch_array( keys, NUMCARDS );
1275     }
1276 
1277     ST_Release_FaceGraphics ();
1278 }
1279 
1280 // made separate so that skins code can reload custom face graphics
1281 // Called by SetPlayerSkin, ST_Release_Graphics
ST_Release_FaceGraphics(void)1282 void ST_Release_FaceGraphics (void)
1283 {
1284     //faB: MipPatch_t are always purgeable
1285     if( facegraphics_loaded )
1286     {
1287         facegraphics_loaded = false;
1288         release_patch_array( faces, ST_NUMFACES );
1289 
1290         // face background
1291         W_release_patch( faceback );
1292     }
1293 }
1294 
1295 
1296 
1297 // Doom only.
ST_init_stbar(void)1298 static void ST_init_stbar(void)
1299 {
1300 
1301     int         i;
1302 
1303     st_force_refresh = true;
1304 
1305     //added:16-01-98:'link' the statusbar display to a player, which could be
1306     //               another player than consoleplayer, for example, when you
1307     //               change the view in a multiplayer demo with F12.
1308     if (singledemo)
1309         statusbarplayer = displayplayer;
1310     else
1311         statusbarplayer = consoleplayer;
1312 
1313     // Single player status state init.
1314     st_plyr = &players[statusbarplayer];
1315 
1316     st_clock = 0;
1317     st_chatstate = StartChatState;
1318 
1319     stbar_on = true;  // ST_Drawer clears it for Splitscreen
1320     st_oldchat = st_chat = false;
1321     st_cursor_on = false;
1322 
1323     st_faceindex = 0;
1324     st_palette = -1;
1325 
1326     st_oldhealth = -1;
1327 
1328     // Doom only.
1329     for (i=0;i<NUMWEAPONS;i++)
1330         oldweaponsowned[i] = st_plyr->weaponowned[i];
1331 
1332     st_card = 0;  // no keys
1333     st_num_keyboxes = 3;
1334     for (i=0;i<6;i++)
1335         keyboxes[i] = -1;
1336 
1337 }
1338 
1339 
ST_CalcPos(void)1340 void ST_CalcPos(void)
1341 {
1342     if( cv_scalestatusbar.EV || cv_viewsize.value>=11 )
1343     {
1344         // large scaled status bar
1345         stbar_fg = FG | V_SCALEPATCH | V_SCALESTART | V_TRANSLUCENTPATCH;
1346         stbar_scalex = vid.dupx;
1347         stbar_scaley = vid.dupy;
1348 
1349 #ifdef HWRENDER
1350         if( rendermode != render_soft )
1351         {
1352             stbar_x = 0;
1353             stbar_y = BASEVIDHEIGHT - stbar_height/vid.fdupy;
1354         }
1355         else
1356 #endif
1357 
1358         {
1359             stbar_x = ((vid.width - ST_WIDTH*vid.dupx)>>1)/vid.dupx;
1360             stbar_y = (vid.height - stbar_height)/vid.dupy;
1361         }
1362     }
1363     else
1364     {
1365         // smaller unscaled status bar in center
1366         stbar_fg = FG | V_NOSCALE | V_TRANSLUCENTPATCH;
1367         stbar_scalex = stbar_scaley = 1;
1368         stbar_x = (vid.width - ST_WIDTH)>>1;  // center
1369         stbar_y = vid.height - stbar_height;
1370     }
1371 }
1372 
1373 // Single player init.
1374 // Also can be called at init of Splitscreen game.
1375 //added:30-01-98: NOTE: this is called at any level start, view change,
1376 //                      and after vid mode change.
1377 static
ST_Create_Widgets(void)1378 void ST_Create_Widgets(void)
1379 {
1380     int i;
1381 
1382     ST_CalcPos();
1383 
1384     // ready weapon ammo
1385     STlib_initNum(&w_ready,
1386                   stbar_x + ST_AMMOX,
1387                   stbar_y + ST_AMMOY,
1388                   tallnum,
1389                   &st_plyr->ammo[st_plyr->weaponinfo[st_plyr->readyweapon].ammo],
1390                   ST_AMMOWIDTH );
1391 
1392     // the last weapon type
1393 //    w_ready.data = st_plyr->readyweapon;
1394 
1395     // health percentage
1396     STlib_initNum(&w_health,
1397                   stbar_x + ST_HEALTHX,
1398                   stbar_y + ST_HEALTHY,
1399                   tallnum,
1400                   &st_plyr->health,
1401                   3 );
1402 
1403     // arms background
1404     STlib_initBinIcon(&w_armsbg,
1405                       stbar_x + ST_ARMSBGX,
1406                       stbar_y + ST_ARMSBGY,
1407                       armsbg,
1408                       &st_not_deathmatch );
1409 
1410     // weapons owned, draw enabled by ! deathmatch
1411     for(i=0;i<6;i++)
1412     {
1413         STlib_initMultIcon(&w_arms[i],
1414                       stbar_x + ST_ARMSX + (i%3)*ST_ARMSXSPACE,
1415                       stbar_y + ST_ARMSY + (i/3)*ST_ARMSYSPACE,
1416                       arms[i], (int *) &st_plyr->weaponowned[i+1] );
1417     }
1418 
1419     // frags sum, draw enabled by deathmatch
1420     STlib_initNum(&w_frags,
1421                   stbar_x + ST_FRAGSX,
1422                   stbar_y + ST_FRAGSY,
1423                   tallnum,
1424                   &st_fragscount,
1425                   ST_FRAGSWIDTH);
1426 
1427     // faces
1428     STlib_initMultIcon(&w_faces,
1429                        stbar_x + ST_FACESX,
1430                        stbar_y + ST_FACESY,
1431                        faces,
1432                        &st_faceindex );
1433 
1434     // armor percentage - should be colored later
1435     STlib_initNum(&w_armor,
1436                   stbar_x + ST_ARMORX,
1437                   stbar_y + ST_ARMORY,
1438                   tallnum,
1439                   &st_plyr->armorpoints,
1440                   3 );
1441 
1442     // keyboxes 0-6, in vertical column
1443     for( i=0; i<6; i++ )
1444     {
1445         STlib_initMultIcon(&w_keyboxes[i],
1446                        stbar_x + ST_KEYX,
1447                        stbar_y + keybox_y[i],
1448                        keys,  // patches
1449                        &keyboxes[i] );
1450     }
1451 
1452     for( i=0; i<4; i++ )
1453     {
1454         // In vertical column.
1455         // ammo count (all four kinds)
1456         STlib_initNum(&w_ammo[i],
1457                   stbar_x + ST_AMMOSX,
1458                   stbar_y + ammobox_y[i],
1459                   shortnum,
1460                   &st_plyr->ammo[i],
1461                   ST_AMMOS_WIDTH);
1462         // max ammo count (all four kinds)
1463         STlib_initNum(&w_maxammo[i],
1464                   stbar_x + ST_MAXAMMOSX,
1465                   stbar_y + ammobox_y[i],
1466                   shortnum,
1467                   &st_plyr->maxammo[i],
1468                   ST_MAXAMMOS_WIDTH);
1469     }
1470 }
1471 
ST_Stop(void)1472 static void ST_Stop (void)
1473 {
1474     if (st_stopped)
1475         return;
1476 
1477 //    V_SetPalette (0);
1478     ST_Palette0();
1479 
1480     st_stopped = true;
1481 }
1482 
1483 // Doom or Heretic.
1484 // Single and SplitPlayer.
1485 // Called by G_DoLoadLevel, P_SpawnPlayer, P_AddWadFile
1486 // Called by ST_Change_DemoView
ST_Start(void)1487 void ST_Start (void)
1488 {
1489     // Doom and Heretic common.
1490     st_plyr = &players[statusbarplayer];
1491 
1492     if( EN_heretic )
1493     {
1494         st_stopped = false;
1495         return;
1496     }
1497 
1498     // Doom only.
1499     if (!st_stopped)
1500         ST_Stop();
1501 
1502     // Init as if Single player.
1503     // When AutoMap displayed, shows Status bar for player1.
1504     ST_init_stbar();
1505     ST_Create_Widgets();
1506     st_stopped = false;
1507     stbar_recalc = false;  //added:02-02-98: widgets coords have been setup
1508                            // see ST_drawer()
1509 }
1510 
1511 //
1512 //  Initializes the status bar,
1513 //  sets the defaults border patch for the window borders.
1514 //
1515 
1516 //faB: used by Glide mode, holds lumpnum of flat used to fill space around the viewwindow
1517 lumpnum_t  st_borderflat_num;  // extern in r_draw.h
1518 
ST_Init(void)1519 void ST_Init (void)
1520 {
1521     int     i;
1522 
1523     if(dedicated)
1524         return;
1525 
1526     //added:26-01-98:screens[4] is allocated at videomode setup, and
1527     //               set at V_Init(), the first time being at SCR_Recalc()
1528 
1529     // choose and cache the default border patch
1530     switch(gamemode) {
1531         case doom2_commercial :
1532             // DOOM II border patch, original was GRNROCK
1533             st_borderflat_num = W_GetNumForName ("GRNROCK");
1534             break;
1535         case heretic :
1536             if( ! VALID_LUMP( W_CheckNumForName("e2m1") ) )
1537             {
1538                 // GDESC_heretic_shareware
1539                 st_borderflat_num = W_GetNumForName ("FLOOR04");
1540             }
1541             else
1542                 st_borderflat_num = W_GetNumForName ("FLAT513");
1543             break;
1544         case hexen :
1545             st_borderflat_num = W_GetNumForName ("F_022");
1546             break;
1547         default :
1548             // DOOM border patch.
1549             st_borderflat_num = W_GetNumForName ("FLOOR7_2");
1550     }
1551     // [WDJ] Lock against other users of same patch releasing it!.
1552     scr_borderflat = W_CacheLumpNum (st_borderflat_num, PU_LOCK_SB);
1553 
1554     ST_Load_Graphics();  // Doom and Heretic
1555 
1556     if( EN_heretic )
1557         return;
1558 
1559     // Doom only
1560     //
1561     // cache the status bar overlay icons  (fullscreen mode)
1562     //
1563     sbo_health = W_GetNumForName ("SBOHEALT");
1564     sbo_frags  = W_GetNumForName ("SBOFRAGS");
1565     sbo_armor  = W_GetNumForName ("SBOARMOR");
1566 
1567     // With Heretic, NUMWEAPONS = 18.
1568     // Doom weapons are 0..8, chainsaw = 7.
1569     for (i=0;i<NUMWEAPONS;i++)
1570     {
1571         sbo_ammo[i] = (i>0 && i!=7 && i<=8)?
1572             W_GetNumForName (va("SBOAMMO%c",'0'+i))
1573             : 0;
1574     }
1575 }
1576 
1577 //added:16-01-98: change the status bar too, when pressing F12 while viewing
1578 //                 a demo.
ST_Change_DemoView(void)1579 void ST_Change_DemoView (void)
1580 {
1581     //the same routine is called at multiplayer deathmatch spawn
1582     // so it can be called multiple times
1583     ST_Start();
1584 }
1585 
1586 
1587 // =========================================================================
1588 //                         STATUS BAR OVERLAY
1589 // =========================================================================
1590 
1591 consvar_t cv_stbaroverlay = {"overlay","kahmf",CV_SAVE,NULL};
1592 
1593 boolean   st_overlay_on;  // status overlay for Doom and Heretic
1594 
1595 
ST_Register_Commands(void)1596 void ST_Register_Commands (void)
1597 {
1598     CV_RegisterVar (&cv_stbaroverlay);
1599 }
1600 
1601 
1602 //  Draw a number, scaled, over the view
1603 //  Always draw the number completely since it's overlay
1604 //
1605 //   x, y: scaled position, right border!
1606 static
ST_drawOverlayNum(int x,int y,int num,patch_t ** numpat,patch_t * percent,byte pickup_flash)1607 void ST_drawOverlayNum (int x, int y,
1608                         int       num,
1609                         patch_t** numpat,
1610                         patch_t*  percent,
1611                         byte      pickup_flash )
1612 {
1613     // Hardware or software draw.
1614     patch_t * pf = V_patch( numpat[0] );
1615     int  hf = pf->height;
1616     int  wf = pf->width;
1617     int  wfv = wf * vid.dupx;
1618     boolean   neg;
1619 
1620     V_SetupDraw( FG | V_NOSCALE | V_SCALEPATCH | V_TRANSLUCENTPATCH );
1621 
1622     if( pickup_flash && (cv_pickupflash.EV == 1))
1623     {
1624         // Assume 3 digits  0..200
1625         V_DrawVidFill(x - (wfv*3), y, wfv*3, hf*vid.dupy, FLASH_COLOR);
1626     }
1627 
1628     // in the special case of 0, you draw 0
1629     if (num == 0)
1630     {
1631         V_DrawScaledPatch(x - wfv, y, numpat[ 0 ]);
1632         return;
1633     }
1634 
1635     neg = num < 0;
1636 
1637     if (neg)
1638         num = -num;
1639 
1640     // draw the number
1641     while (num)
1642     {
1643         x -= wfv;
1644         V_DrawScaledPatch(x, y, numpat[ num % 10 ]);
1645         num /= 10;
1646     }
1647 
1648     // draw a minus sign if necessary, minus is at [10] in the number font
1649     if (neg && numpat[10])
1650         V_DrawScaledPatch(x - (8*vid.dupx), y, numpat[10]);
1651 }
1652 
1653 //  y : status position normally
1654 //  y0 : status base position as modified for splitscreen
SCY(int y,int y0)1655 static inline int SCY( int y, int y0 )
1656 {
1657     //31/10/99: fixed by Hurdler so it _works_ also in hardware mode
1658     // do not scale to resolution for hardware accelerated
1659     // because these modes always scale by default
1660     y = (int)( y * vid.fdupy );     // scale to resolution
1661     if( cv_splitscreen.EV ) {
1662         y >>= 1; // half sized screens
1663         y += y0; // base position of upper or lower screen
1664     }
1665     return y;
1666 }
1667 
1668 
SCX(int x)1669 static inline int SCX( int x )
1670 {
1671     return x * vid.fdupx;
1672 }
1673 
1674 static
ST_drawOverlayKeys(int x,int y,player_t * plyr)1675 void  ST_drawOverlayKeys( int x, int y, player_t * plyr )
1676 {
1677     int  i, yh, xinc, yinc;
1678     byte cards = plyr->cards;
1679 
1680     xinc = (int)((ST_KEY_WIDTH + 1) * vid.fdupx);
1681     yinc = (int)((ST_KEY_HEIGHT + 1) * vid.fdupy);
1682     yh = y;  // upper row is same as lower row when no skull keys
1683     // if both skull and cards, then move cards up a row
1684     if( cards & 0x38 )
1685         yh -= yinc;
1686 
1687     if( plyr->key_pickup && (cv_pickupflash.EV == 1))
1688     {
1689         V_DrawVidFill(x - (xinc*3), yh, (xinc*3), y - yh + yinc, FLASH_COLOR);
1690     }
1691 
1692     for (i=0;i<3;i++)
1693     {
1694         x -= xinc;
1695         if( (cards >> i) & 0x08 ) // skull
1696         {
1697             V_DrawScaledPatch(x, y, keys[i+3]);  // skull graphic lower row
1698         }
1699         if( (cards >> i) & 0x01 ) // card
1700         {
1701             V_DrawScaledPatch(x, yh, keys[i]);  // keycard graphic upper row
1702         }
1703     }
1704 }
1705 
1706 
1707 //  Draw the status bar overlay, customisable : the user choose which
1708 //  kind of information to overlay
1709 //
1710 //   status_position : 0=lower, 1=upper
1711 static
ST_overlayDrawer(byte status_position,player_t * plyr)1712 void ST_overlayDrawer ( byte status_position, player_t * plyr )
1713 {
1714     const char *  cmds;
1715     char   c;
1716     int    i;
1717     // [WDJ] 8/2012 fix opengl overlay position to use fdupy
1718     float  sf_dupy = (rendermode == render_soft)? vid.dupy : vid.fdupy ;
1719     int  y0 = status_position ? 0 : vid.height / 2;
1720     int  lowerbar_y = SCY(198,y0) - (int)( 16 * sf_dupy );
1721 
1722     // Draw screen0, scaled, abs position
1723     V_SetupDraw( FG | V_NOSCALE | V_SCALEPATCH );
1724     // x, y are already scaled.
1725 
1726     cmds = cv_stbaroverlay.string;
1727 
1728     while ((c=*cmds++))
1729     {
1730        if (c>='A' && c<='Z')
1731            c = c + 'a' - 'A';
1732        switch (c)
1733        {
1734          case 'h': // draw health
1735            ST_drawOverlayNum(SCX(50), lowerbar_y,
1736                              plyr->health,
1737                              tallnum, NULL, plyr->health_pickup);
1738 
1739            V_DrawScalePic_Num (SCX(52), lowerbar_y, sbo_health);
1740            break;
1741 
1742          case 'f': // draw frags
1743            st_fragscount = ST_PlayerFrags(plyr-players);
1744 
1745            if( deathmatch )
1746            {
1747                ST_drawOverlayNum(SCX(300), SCY(2,y0),
1748                                  st_fragscount,
1749                                  tallnum, NULL, 0);
1750 
1751                V_DrawScalePic_Num (SCX(302), SCY(2,y0), sbo_frags);
1752            }
1753            break;
1754 
1755          case 'a': // draw ammo
1756            i = sbo_ammo[plyr->readyweapon];
1757            if (i)
1758            {
1759                ST_drawOverlayNum(SCX(234), lowerbar_y,
1760                                  plyr->ammo[plyr->weaponinfo[plyr->readyweapon].ammo],
1761                                  tallnum, NULL, plyr->ammo_pickup);
1762 
1763                V_DrawScalePic_Num (SCX(236), lowerbar_y, i);
1764            }
1765            break;
1766 
1767          case 'k': // draw keys
1768            ST_drawOverlayKeys( SCX(318), lowerbar_y - (8 * sf_dupy), plyr );
1769            break;
1770 
1771          case 'm': // draw armor
1772            ST_drawOverlayNum(SCX(300), lowerbar_y,
1773                              plyr->armorpoints,
1774                              tallnum, NULL, plyr->armor_pickup);
1775 
1776            V_DrawScalePic_Num (SCX(302), lowerbar_y, sbo_armor);
1777            break;
1778 
1779          // added by Hurdler for single player only (or coop netplay)
1780          case 'e': // number of monster killed
1781            if( (! deathmatch) && (!cv_splitscreen.EV) )
1782            {
1783                char buf[16];
1784                sprintf(buf, "%d/%d", plyr->killcount, totalkills);
1785                V_DrawString(SCX(318-V_StringWidth(buf)), SCY(1,y0), 0, buf);
1786            }
1787            break;
1788 
1789          case 's': // number of secrets found
1790            if( (! deathmatch) && (!cv_splitscreen.EV) )
1791            {
1792                char buf[16];
1793                sprintf(buf, "%d/%d", plyr->secretcount, totalsecret);
1794                V_DrawString(SCX(318-V_StringWidth(buf)), SCY(11,y0), 0, buf);
1795            }
1796            break;
1797 
1798            /* //TODO
1799          case 'r': // current frame rate
1800            {
1801                char buf[8];
1802                int framerate = 35;
1803                sprintf(buf, "%d FPS", framerate);
1804                V_DrawString(SCX(2), SCY(4,y0), 0, buf);
1805            }
1806            break;
1807            */
1808        }
1809     }
1810 }
1811