1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: hu_stuff.c 1543 2020-08-22 02:36:35Z wesleyjohnson $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 1998-2010 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: hu_stuff.c,v $
21 // Revision 1.18  2003/11/22 00:22:09  darkwolf95
22 // get rid of FS hud pics on level exit and new game, also added exl's fix
23 // for clearing hub variables on new game
24 //
25 // Revision 1.17  2003/07/14 12:39:12  darkwolf95
26 // Made rankings screen smaller for splitscreen.  Ditched the graphical title
27 // and limited max rankings to four.
28 //
29 // Revision 1.16  2002/07/23 15:07:10  mysterial
30 // Messages to second player appear on his half of the screen
31 //
32 // Revision 1.15  2001/12/15 18:41:35  hurdler
33 // small commit, mainly splitscreen fix
34 //
35 // Revision 1.14  2001/08/20 20:40:39  metzgermeister
36 //
37 // Revision 1.13  2001/07/16 22:35:40  bpereira
38 // - fixed crash of e3m8 in heretic
39 // - fixed crosshair not drawed bug
40 //
41 // Revision 1.12  2001/05/16 21:21:14  bpereira
42 // Revision 1.11  2001/04/01 17:35:06  bpereira
43 // Revision 1.10  2001/02/24 13:35:20  bpereira
44 //
45 // Revision 1.9  2001/02/19 17:40:34  hurdler
46 // Fix a bug with "chat on" in hw mode
47 //
48 // Revision 1.8  2001/01/25 22:15:42  bpereira
49 // added heretic support
50 //
51 // Revision 1.7  2000/11/04 16:23:43  bpereira
52 //
53 // Revision 1.6  2000/11/02 17:50:06  stroggonmeth
54 // Big 3Dfloors & FraggleScript commit!!
55 //
56 // Revision 1.5  2000/09/28 20:57:15  bpereira
57 // Revision 1.4  2000/08/31 14:30:55  bpereira
58 // Revision 1.3  2000/08/03 17:57:42  bpereira
59 // Revision 1.2  2000/02/27 00:42:10  hurdler
60 // Revision 1.1.1.1  2000/02/22 20:32:33  hurdler
61 // Initial import into CVS (v1.29 pr3)
62 //
63 //
64 // DESCRIPTION:
65 //      heads up displays, cleaned up (hasta la vista hu_lib)
66 //      because a lot of code was unnecessary now
67 //
68 //-----------------------------------------------------------------------------
69 
70 
71 #include "doomincl.h"
72 #include "hu_stuff.h"
73 
74 #include "d_netcmd.h"
75 #include "d_clisrv.h"
76 
77 #include "g_game.h"
78 #include "g_input.h"
79 
80 #include "i_video.h"
81 
82 // Data.
83 #include "dstrings.h"
84 #include "st_stuff.h"
85   //added:05-02-98: ST_HEIGHT
86 #include "r_local.h"
87 #include "wi_stuff.h"
88   // for drawrankings
89 #include "p_info.h"
90 #include "p_inter.h"
91   // P_SetMessage
92 
93 #include "keys.h"
94 #include "v_video.h"
95 
96 #include "w_wad.h"
97 #include "z_zone.h"
98 
99 #include "console.h"
100 #include "am_map.h"
101 #include "d_main.h"
102 
103 #ifdef HWRENDER
104 #include "hardware/hw_main.h"
105 #endif
106 
107 
108 // coords are scaled
109 #define HU_INPUTX       0
110 #define HU_INPUTY       0
111 
112 //-------------------------------------------
113 //              heads up font
114 //-------------------------------------------
115 patch_t*                hu_font[HU_FONTSIZE];
116 
117 
118 static player_t*        plr;
119 boolean                 chat_on;
120 
121 static boolean          headsup_active = false;
122 
123 boolean                 hu_showscores;        // draw deathmatch rankings
124 
125 static char             hu_tick;
126 
127 //-------------------------------------------
128 //              misc vars
129 //-------------------------------------------
130 
131 consvar_t*   chat_macros[10];
132 
133 static
134 CV_PossibleValue_t crosshair_cons_t[] = {{0,"Off"},{1,"Cross"},{2,"Angle"},{3,"Point"},{0,NULL}};
135 consvar_t cv_crosshair[2] = {
136   {"crosshair"   ,"0",CV_SAVE,crosshair_cons_t},
137   {"crosshair2"   ,"0",CV_SAVE,crosshair_cons_t}
138 };
139 //consvar_t cv_crosshairscale   = {"crosshairscale","0",CV_SAVE,CV_YesNo};
140 
141 // maximum 9
142 #define HU_CROSSHAIRS   3
143 //added:16-02-98: crosshair 0=off, 1=cross, 2=angle, 3=point, see m_menu.c
144 static patch_t * crosshair_patch[HU_CROSSHAIRS];     //3 precached crosshair graphics
145 
146 byte  hu_fonts_loaded = 0; // 1=partially loaded, 2=fully loaded
147 
148 #ifdef HWRENDER
149 // The settings of HWR_patchstore by SCR_SetMode seem to be adequate.
150 // This only would be needed if there were additional problems.
151 // It is more expensive.
152 //#define HU_HWR_PATCHSTORE_SAVE
153 #ifdef HU_HWR_PATCHSTORE_SAVE
154 static byte  hu_HWR_patchstore;  // the HWR_patchstore setting used by fonts.
155 #endif
156 #endif
157 
158 
159 // -------
160 // protos.
161 // -------
162 static void  HU_Draw_DeathmatchRankings (void);
163 static void  HU_Draw_Crosshair (void);
164 static void  HU_Draw_Tip();
165 
166 
167 
168 //======================================================================
169 //                          HEADS UP INIT
170 //======================================================================
171 
HU_Stop(void)172 void HU_Stop(void)
173 {
174     headsup_active = false;
175 }
176 
177 //
178 // Reset Heads up when consoleplayer spawns
179 //
HU_Start(void)180 void HU_Start(void)
181 {
182     if (headsup_active)
183         HU_Stop();
184 
185     plr = consoleplayer_ptr;
186     chat_on = false;
187 
188     headsup_active = true;
189 }
190 
HU_Load_Graphics(void)191 void HU_Load_Graphics( void )
192 {
193     int         i, j;
194     char        buffer[9];
195 
196     hu_fonts_loaded = 2;
197     // cache the heads-up font
198     // Patches are endian fixed when loaded.
199     j = (EN_heretic)? 1 : HU_FONTSTART;
200     for (i=0; i<HU_FONTSIZE; i++)
201     {
202         if( EN_heretic_hexen )
203             sprintf(buffer, "FONTA%.2d", ((j>59)? 59 : j));
204         else
205             sprintf(buffer, "STCFN%.3d", j);
206 
207         j++;
208         if( ! VALID_LUMP( W_CheckNumForName( buffer ) ) )
209         {
210             // font not found
211             hu_font[i] = NULL;
212             hu_fonts_loaded = 1;  // partially loaded
213             continue;
214         }
215         hu_font[i] = W_CachePatchName(buffer, PU_STATIC);
216     }
217 
218     // cache the crosshairs, dont bother to know which one is being used,
219     // just cache them 3 all, they're so small anyway.
220     for(i=0; i<HU_CROSSHAIRS; i++)
221     {
222        sprintf(buffer, "CROSHAI%c", '1'+i);
223        crosshair_patch[i] = W_CachePatchName(buffer, PU_STATIC);
224     }
225 
226 #ifdef HU_HWR_PATCHSTORE_SAVE
227     hu_HWR_patchstore = HWR_patchstore;
228 #endif
229 }
230 
HU_Release_Graphics(void)231 void HU_Release_Graphics( void )
232 {
233 #ifdef HU_HWR_PATCHSTORE_SAVE
234     byte saved_HWR_patchstore = HWR_patchstore;
235 #endif
236 
237     if( hu_fonts_loaded )
238     {
239         hu_fonts_loaded = 0;
240 
241 #ifdef HU_HWR_PATCHSTORE_SAVE
242         // Must use setting from when fonts were saved.
243         // This is necessary because releasing the font is delayed until the last second.
244         HWR_patchstore = hu_HWR_patchstore;
245 #endif
246 
247         // Has protection against individual NULL ptr in array.
248         release_patch_array( hu_font, HU_FONTSIZE );
249         release_patch_array( crosshair_patch, HU_CROSSHAIRS );
250 
251 #ifdef HU_HWR_PATCHSTORE_SAVE
252         HWR_patchstore = saved_HWR_patchstore;
253 #endif
254     }
255 }
256 
257 
258 //======================================================================
259 //                            EXECUTION
260 //======================================================================
261 
262 
263 // SAY: Broadcast to all players.
Command_Say_f(void)264 void Command_Say_f (void)
265 {
266     char buf[255];
267     int i,j;
268 
269     if((j=COM_Argc())<2)
270     {
271         CONS_Printf ("say <message> : send a message\n");
272         return;
273     }
274 
275     buf[0]=255;  // broadcast
276     strcpy(&buf[1],COM_Argv(1));
277     for(i=2; i<j; i++)
278     {
279         strcat(&buf[1]," ");
280         strcat(&buf[1],COM_Argv(i));
281     }
282     // as mainplayer
283     Send_NetXCmd(XD_SAY, buf, strlen(buf+1)+2);
284        // +2 because 1 for buf[0] and the other for null terminated string
285 }
286 
287 // SAYTO: Send to a player.
Command_Sayto_f(void)288 void Command_Sayto_f (void)
289 {
290     byte playernum;
291     int i,j;
292     char buf[255];
293 
294     if((j=COM_Argc())<3)
295     {
296         CONS_Printf ("sayto <playername|playernum> <message> : send a message to a player\n");
297         return;
298     }
299 
300     // Players 0..(MAXPLAYERS-1) are known as Player 1 to MAXPLAYERS to user.
301     playernum = player_name_to_num(COM_Argv(1));
302     if(playernum > MAXPLAYERS)
303         return;  // not found
304 
305     buf[0] = playernum;    // 0..127
306     strcpy(&buf[1],COM_Argv(2));
307     for(i=3; i<j; i++)
308     {
309         strcat(&buf[1]," ");
310         strcat(&buf[1],COM_Argv(i));
311     }
312     // as mainplayer
313     Send_NetXCmd(XD_SAY, buf, strlen(buf+1)+2);
314 }
315 
316 // SAYTEAM: To all team members of this player.
Command_Sayteam_f(void)317 void Command_Sayteam_f (void)
318 {
319     char buf[255];
320     int i,j;
321 
322     if((j=COM_Argc())<2)
323     {
324         CONS_Printf ("sayteam <message> : send a message to your team\n");
325         return;
326     }
327 
328     // Players 0..(MAXPLAYERS-1) are known as Player 1 to MAXPLAYERS to user.
329     buf[0] = consoleplayer & 0x80;  // 128..254
330     strcpy(&buf[1],COM_Argv(1));
331     for(i=2; i<j; i++)
332     {
333         strcat(&buf[1]," ");
334         strcat(&buf[1],COM_Argv(i));
335     }
336     // as mainplayer
337     Send_NetXCmd(XD_SAY, buf, strlen(buf+1)+2);
338         // +2 because 1 for buf[0] and the other for null terminated string
339 }
340 
341 // [WDJ] Previous Say/Sayto/Sayteam system was broken.
342 // New as of DoomLegacy 1.46:
343 //  to: 0..127 player
344 //      0x80 & team number, team= 0..126
345 //      255 broadcast
346 
Got_NetXCmd_Saycmd(xcmd_t * xc)347 void Got_NetXCmd_Saycmd( xcmd_t * xc )
348 {
349     // Command: ( byte: to_player_id, string0: message )
350     // XCmd buffer has forced 0 term to protect against malicious message.
351     const char * tostr = "";
352     char * fromstr;
353     byte * p = xc->curpos;
354     byte to = *(p++);
355     byte pn = (to & 0x7F); // to player num 0..126
356 
357     if( xc->playernum >= MAXPLAYERS )  goto done;  // cannot index player_names
358     fromstr = player_names[xc->playernum];  // never NULL
359 
360     if( to==255 )
361     {
362         // Broadcast
363         tostr = " All";
364     }
365     else
366     {
367         if( pn >= MAXPLAYERS )  goto done;  // cannot index tables
368         if( to & 0x80 )
369         {
370             // To Team
371             tostr = " Team";
372         }
373     }
374 
375     if(xc->playernum == consoleplayer
376        || to==255 // broadcast
377        || ( (to < MAXPLAYERS) && pn==consoleplayer )
378        || ( (to & 0x80) // Team broadcast from pn
379             && ST_SameTeam(consoleplayer_ptr,&players[pn])) )
380     {
381         GenPrintf( EMSG_playmsg, "\3%s%s: %s\n", fromstr, tostr, p);
382     }
383 
384     if(  displayplayer2_ptr )
385     {
386         // Splitscreen
387         if(xc->playernum == displayplayer2
388            || to==255 // broadcast
389            || ( (to < MAXPLAYERS) && pn==displayplayer2 )
390            || ( (to & 0x80) // Team broadcast from pn
391                 && ST_SameTeam(displayplayer2_ptr,&players[pn])) )
392         {
393             GenPrintf( EMSG_playmsg2, "\3%s%s: %s\n", fromstr, tostr, p);
394         }
395     }
396 
397 done:
398     p += strlen((char*)p) + 1;  // incl term 0
399     xc->curpos = p;
400 }
401 
402 
403 
404 
405 //
406 //
HU_Ticker(void)407 void HU_Ticker(void)
408 {
409     player_t    *pl;
410 
411     if(dedicated)
412         return;
413 
414     hu_tick++;
415     hu_tick &= 7;        //currently only to blink chat input cursor
416 
417     // display message if necessary
418     // (display the viewplayer's messages)
419     pl = displayplayer_ptr;
420     if ( pl->message )
421     {
422         // Player message blocking is handled by P_SetMessage.
423         GenPrintf(EMSG_playmsg, "%s\n", pl->message);
424         pl->message = NULL;
425         pl->msglevel = 0;
426     }
427 
428     // In splitscreen, display second player's messages
429     if (cv_splitscreen.value && displayplayer2_ptr )
430     {
431         pl = displayplayer2_ptr;
432         if ( pl->message )
433         {
434             // Player message blocking is handled by P_SetMessage.
435             GenPrintf(EMSG_playmsg2, "%s\n", pl->message);
436             pl->message = NULL;
437             pl->msglevel = 0;
438         }
439     }
440 
441     //deathmatch rankings overlay if press key or while in death view
442     if( deathmatch )
443     {
444         if (gamekeydown[gamecontrol[gc_scores][0]] ||
445             gamekeydown[gamecontrol[gc_scores][1]] )
446             hu_showscores = !chat_on;
447         else
448             hu_showscores = playerdeadview; //hack from P_DeathThink()
449     }
450     else
451         hu_showscores = false;
452 }
453 
454 
455 
456 // [smite] there's no reason to use a queue here, a normal buffer will do
457 static char     w_chat[HU_MAXMSGLEN+1]; // always NUL-terminated
458 static unsigned tail = 0; // first free cell, should contain NUL
459 
460 // simplified stl::vector implementation
HU_Chat_push_back(char c)461 static boolean HU_Chat_push_back(char c)
462 {
463   if (tail >= HU_MAXMSGLEN)
464     return false;
465 
466   w_chat[tail++] = c;
467   w_chat[tail] = '\0';
468   return true;
469 }
470 
HU_Chat_pop_back()471 static boolean HU_Chat_pop_back()
472 {
473   if (tail == 0)
474     return false;
475 
476   tail--;
477   w_chat[tail] = '\0';
478   return true;
479 }
480 
HU_Chat_clear()481 static void HU_Chat_clear()
482 {
483   tail = 0;
484   w_chat[tail] = '\0';
485 }
486 
HU_Chat_empty()487 static boolean HU_Chat_empty()
488 {
489   return tail == 0;
490 }
491 
HU_Chat_send()492 static void HU_Chat_send()
493 {
494   COM_BufInsertText(va("say %s", w_chat));
495 }
496 
497 
498 
499 //
500 //  Returns true if key eaten
501 //
HU_Responder(event_t * ev)502 boolean HU_Responder (event_t *ev)
503 {
504   if (ev->type != ev_keydown)
505     return false;
506 
507   // only KeyDown events now...
508   int key = ev->data1;
509 
510   if (!chat_on)
511   {
512       // enter chat mode
513       if (key == gamecontrol[gc_talkkey][0] || key == gamecontrol[gc_talkkey][1])
514       {
515           chat_on = true;
516           HU_Chat_clear();
517           return true;
518       }
519   }
520   else
521   {
522       int c = ev->data2; // ASCII character
523 
524       // send a macro
525       if (altdown)
526       {
527           c = c - '0';
528           if (c > 9 || c < 0)
529             return false;
530 
531           // current message stays unchanged
532 
533           // send the macro message
534           COM_BufInsertText(va("say %s", chat_macros[c]->string));
535 
536           // if there is no unfinished message, leave chat mode and notify that it was sent
537           if (HU_Chat_empty())
538             chat_on = false;
539       }
540       else
541       {
542           // chat input
543           if (key == KEY_ESCAPE)
544           {
545               // close chat
546               chat_on = false;
547           }
548           else if (key == KEY_ENTER)
549           {
550               // send the message
551               if (tail > 1)
552                 HU_Chat_send(w_chat);
553 
554               HU_Chat_clear();
555               chat_on = false;
556           }
557           else if (key == KEY_BACKSPACE)
558           {
559               // erase a char
560               HU_Chat_pop_back();
561           }
562           else if (c >= ' ' && c <= '~')
563           {
564               // add a char
565               if (!HU_Chat_push_back(c))
566                 P_SetMessage( plr, HUSTR_MSGU, 63);  // out of space
567           }
568           else
569             return false; // let the event go
570       }
571 
572       return true; // ate the key
573   }
574 
575   return false; // let the event go
576 }
577 
578 
579 //======================================================================
580 //                         HEADS UP DRAWING
581 //======================================================================
582 
583 //  Draw chat input
584 //
HU_Draw_Chat(void)585 static void HU_Draw_Chat (void)
586 {
587     // vid : from video setup
588     int  i,x,y;
589 
590     V_SetupFont( cv_msg_fontsize.value, NULL, V_NOSCALE );
591 
592     i=0;
593     x=HU_INPUTX;
594     y=HU_INPUTY;
595     while (w_chat[i])
596     {
597 #if 1
598         // Proportional Font
599         x += V_DrawCharacter( x, y, w_chat[i++] | 0x80 );  // white
600 #else
601         // Fixed width Font
602         V_DrawCharacter( x, y, w_chat[i++] | 0x80 );  // white
603         x += drawfont.xinc;
604 #endif
605 
606         if (x >= vid.width)
607         {
608             x = HU_INPUTX;
609             y += drawfont.yinc;
610         }
611     }
612 
613     // Cursor blink
614     if (hu_tick<4)
615         V_DrawCharacter( x, y, '_' | 0x80 );  // white
616 }
617 
618 
619 extern consvar_t cv_chasecam;
620 
621 //  Heads up displays drawer, call each frame
622 //
HU_Drawer(void)623 void HU_Drawer(void)
624 {
625     // draw chat string plus cursor
626     if (chat_on)
627         HU_Draw_Chat ();
628 
629     // draw deathmatch rankings
630     if (hu_showscores)
631         HU_Draw_DeathmatchRankings ();
632 
633     // draw the crosshair, not when viewing demos nor with chasecam
634     if (!automapactive && !demoplayback && !cv_chasecam.value)
635         HU_Draw_Crosshair ();
636 
637     HU_Draw_Tip();
638     HU_Draw_FSPics();
639 }
640 
641 //======================================================================
642 //                          PLAYER TIPS
643 //======================================================================
644 #define MAXTIPLINES 20
645 char    *tiplines[MAXTIPLINES];
646 int     numtiplines = 0;
647 int     tiptime = 0;
648 int     largestline = 0;
649 
650 
651 
HU_SetTip(char * tip,int displaytics)652 void HU_SetTip(char *tip, int displaytics)
653 {
654   int    i;
655   char   *rover, *ctipline, *ctipline_p;
656 
657 
658   for(i = 0; i < numtiplines; i++)
659     Z_Free(tiplines[i]);
660 
661 
662   numtiplines = 0;
663 
664   rover = tip;
665   ctipline = ctipline_p = Z_Malloc(128, PU_STATIC, NULL);
666   *ctipline = 0;
667   largestline = 0;
668 
669   while(*rover)
670   {
671     if(*rover == '\n' || strlen(ctipline) + 2 >= 128 || V_StringWidth(ctipline) + 16 >= BASEVIDWIDTH)
672     {
673       if(numtiplines > MAXTIPLINES)
674         break;
675       if(V_StringWidth(ctipline) > largestline)
676         largestline = V_StringWidth(ctipline);
677 
678       tiplines[numtiplines] = ctipline;
679       ctipline = ctipline_p = Z_Malloc(128, PU_STATIC, NULL);
680       *ctipline = 0;
681       numtiplines ++;
682     }
683     else
684     {
685       *ctipline_p = *rover;
686       ctipline_p++;
687       *ctipline_p = 0;
688     }
689     rover++;
690 
691     if(!*rover)
692     {
693       if(V_StringWidth(ctipline) > largestline)
694         largestline = V_StringWidth(ctipline);
695       tiplines[numtiplines] = ctipline;
696       numtiplines ++;
697     }
698   }
699 
700   tiptime = displaytics;
701 }
702 
703 
704 
705 
HU_Draw_Tip()706 static void HU_Draw_Tip()
707 {
708   int    i;
709   if(!numtiplines) return;
710   if(!tiptime)
711   {
712     for(i = 0; i < numtiplines; i++)
713       Z_Free(tiplines[i]);
714     numtiplines = 0;
715     return;
716   }
717   tiptime--;
718 
719 
720   // Draw screen0, scaled
721   V_SetupDraw( 0 | V_SCALESTART | V_SCALEPATCH );
722   for(i = 0; i < numtiplines; i++)
723   {
724     V_DrawString((BASEVIDWIDTH - largestline) / 2,
725                  ((BASEVIDHEIGHT - (numtiplines * 8)) / 2) + ((i + 1) * 8),
726                  0,
727                  tiplines[i]);
728   }
729 }
730 
731 
HU_Clear_Tips()732 void HU_Clear_Tips()
733 {
734   int    i;
735 
736   for(i = 0; i < numtiplines; i++)
737     Z_Free(tiplines[i]);
738   numtiplines = 0;
739 
740   tiptime = 0;
741 }
742 
743 
744 //======================================================================
745 //                           FS HUD Grapics!
746 //======================================================================
747 typedef struct
748 {
749   lumpnum_t lumpnum;
750   int       xpos;
751   int       ypos;
752   patch_t   *data;
753   boolean   draw;
754 } fspic_t;
755 
756 fspic_t*   piclist = NULL;	// realloc, never deallocated
757 int        num_piclist_alloc = 0;
758 
759 
760 // HU_InitFSPics
761 // This function is called when Doom starts and every time the piclist needs
762 // to be expanded.
HU_Init_FSPics()763 void HU_Init_FSPics()
764 {
765   fspic_t * npp;
766   int  newstart, newend, i;
767 
768   newstart = num_piclist_alloc;
769   if( num_piclist_alloc == 0 )
770   {
771     // Initial allocation.
772     newend = 128;
773   }
774   else
775   {
776     // Double current allocation.
777     newend = (num_piclist_alloc * 2);
778   }
779 
780   npp = realloc(piclist, sizeof(fspic_t) * newend);
781   // Check allocation fail [WDJ]
782   if( npp == NULL )
783      return;
784 
785   // Commit to new allocation.
786   piclist = npp;
787   num_piclist_alloc = newend;
788 
789   // Init the added slots to empty.
790   for(i = newstart; i < newend; i++)
791   {
792     piclist[i].lumpnum = NO_LUMP;
793     piclist[i].data = NULL;
794     piclist[i].draw = false;
795     piclist[i].xpos = 0;
796     piclist[i].ypos = 0;
797   }
798 }
799 
800 // Return slot number (handle) for pic, [ 0 .. num_piclist_alloc-1 ].
HU_Get_FSPic(lumpnum_t lumpnum,int xpos,int ypos)801 int  HU_Get_FSPic( lumpnum_t lumpnum, int xpos, int ypos )
802 {
803   int  i;
804 
805   if(!num_piclist_alloc)
806     HU_Init_FSPics();
807 
808 getpic_retry:  // retry
809   for(i = 0; i < num_piclist_alloc; i++)
810   {
811     // Find empty slot
812     if( VALID_LUMP(piclist[i].lumpnum) )
813       continue;
814 
815     piclist[i].lumpnum = lumpnum;
816     piclist[i].xpos = xpos;
817     piclist[i].ypos = ypos;
818     piclist[i].draw = false;
819     return i;
820   }
821 
822   // Did not find an empty slot.
823   HU_Init_FSPics();
824   goto getpic_retry;
825 }
826 
827 
HU_Delete_FSPic(int handle)828 int  HU_Delete_FSPic(int handle)
829 {
830   if(handle < 0 || handle >= num_piclist_alloc)
831     return -1;
832 
833   piclist[handle].lumpnum = NO_LUMP;
834   piclist[handle].data = NULL;
835   return 0;
836 }
837 
838 
HU_Modify_FSPic(int handle,lumpnum_t lumpnum,int xpos,int ypos)839 int  HU_Modify_FSPic( int handle, lumpnum_t lumpnum, int xpos, int ypos )
840 {
841   if(handle < 0 || handle >= num_piclist_alloc)
842     return -1;
843 
844   if( ! VALID_LUMP(piclist[handle].lumpnum) )
845     return -1;
846 
847   piclist[handle].lumpnum = lumpnum;
848   piclist[handle].xpos = xpos;
849   piclist[handle].ypos = ypos;
850   piclist[handle].data = NULL;
851   return 0;
852 }
853 
854 
855 // Enable or disable the drawing of a Pic.
HU_FS_Display(int handle,boolean enable_draw)856 int  HU_FS_Display(int handle, boolean enable_draw)
857 {
858   if(handle < 0 || handle >= num_piclist_alloc)
859     return -1;
860   if( ! VALID_LUMP(piclist[handle].lumpnum) )
861     return -1;
862 
863   piclist[handle].draw = enable_draw;
864   return 0;
865 }
866 
867 
HU_Draw_FSPics()868 void HU_Draw_FSPics()
869 {
870   // vid : from video setup
871   int  i;
872 
873   // [WDJ] Fragglescript overlays must be centered.
874   // Needed for Chexquest-newmaps scope with crosshairs.
875   // Draw screen0, scaled, menu centering.
876   V_SetupDraw( 0 | V_SCALEPATCH | V_SCALESTART | V_CENTERMENU );
877 
878   for(i = 0; i < num_piclist_alloc; i++)
879   {
880     if( ! VALID_LUMP(piclist[i].lumpnum) )
881       continue;
882 
883     if( ! piclist[i].draw )
884       continue;  // not enabled
885 
886     if(piclist[i].xpos >= vid.width || piclist[i].ypos >= vid.height)
887       continue;  // off screen right
888 
889     if(!piclist[i].data)
890       piclist[i].data = (patch_t *) W_CachePatchNum(piclist[i].lumpnum, PU_STATIC); // endian fix
891 
892     if((piclist[i].xpos + piclist[i].data->width) < 0
893        || (piclist[i].ypos + piclist[i].data->height) < 0)
894       continue;  // off screen left
895 
896     V_DrawScaledPatch(piclist[i].xpos, piclist[i].ypos, piclist[i].data);
897   }
898   // restore
899   //V_SetupDraw( drawinfo.prev_screenflags );
900 }
901 
HU_Clear_FSPics()902 void HU_Clear_FSPics()
903 {
904         piclist = NULL;
905         num_piclist_alloc = 0;
906 
907         HU_Init_FSPics();
908 }
909 
910 //======================================================================
911 //                 HUD MESSAGES CLEARING FROM SCREEN
912 //======================================================================
913 
914 //  Clear old messages from the borders around the view window
915 //  (only for reduced view, refresh the borders when needed)
916 //
917 //  startline  : y coord to start clear,
918 //  clearlines : how many lines to clear.
919 //
920 static int     oldclearlines;
921 
HU_Erase(void)922 void HU_Erase (void)
923 {
924     static  int     secondframelines;
925 
926     // vid : from video setup
927     int topline;
928     int bottomline;
929     int y,yoffset;
930 
931     //faB: clear hud msgs on double buffer (Glide mode)
932     boolean secondframe;
933 
934     if (con_clearlines==oldclearlines && !con_hudupdate && !chat_on)
935         return;
936 
937     // clear the other frame in double-buffer modes
938     secondframe = (con_clearlines!=oldclearlines);
939     if (secondframe)
940         secondframelines = oldclearlines;
941 
942     // clear the message lines that go away, so use _oldclearlines_
943     bottomline = oldclearlines;
944     oldclearlines = con_clearlines;
945     if( chat_on )
946         if( bottomline < 8 )
947             bottomline=8;
948 
949     if (automapactive || viewwindowx==0)   // hud msgs don't need to be cleared
950         return;
951 
952     if( rendermode == render_soft )
953     {
954         // software mode copies view border pattern & beveled edges from the backbuffer
955         topline = 0;
956         for (y=topline,yoffset=y*vid.width; y<bottomline ; y++,yoffset+=vid.width)
957         {
958             if (y < viewwindowy || y >= viewwindowy + rdraw_viewheight)
959                 R_VideoErase(yoffset, vid.width); // erase entire line
960             else
961             {
962                 R_VideoErase(yoffset, viewwindowx); // erase left border
963                 // erase right border
964                 R_VideoErase(yoffset + viewwindowx + rdraw_viewwidth, viewwindowx);
965             }
966         }
967         con_hudupdate = false;      // if it was set..
968     }
969 #ifdef HWRENDER
970     else
971     {
972         // refresh just what is needed from the view borders
973         HWR_DrawViewBorder (secondframelines);
974         con_hudupdate = secondframe;
975     }
976 #endif
977 }
978 
979 
980 
981 //======================================================================
982 //                   IN-LEVEL DEATHMATCH RANKINGS
983 //======================================================================
984 
985 // count frags for each team
HU_Create_TeamFragTbl(fragsort_t * fragtab,int dmtotals[],int fragtbl[MAXPLAYERS][MAXPLAYERS])986 int HU_Create_TeamFragTbl(fragsort_t *fragtab,
987                          int dmtotals[], int fragtbl[MAXPLAYERS][MAXPLAYERS])
988 {
989     int i,j,k,scorelines,team;
990 
991     scorelines = 0;
992     for (i=0; i<MAXPLAYERS; i++)
993     {
994         if (playeringame[i])
995         {
996             team = (cv_teamplay.EV==1) ? players[i].skincolor
997                                        : players[i].skin;
998 
999             for(j=0; j<scorelines; j++)
1000             {
1001                 if (fragtab[j].num == team)
1002                 { // found there team
1003                      if(fragtbl)
1004                      {
1005                          for(k=0; k<MAXPLAYERS; k++)
1006                          {
1007                              if(playeringame[k])
1008                              {
1009                                  int k_indx = (cv_teamplay.EV==1) ?
1010                                      players[k].skincolor : players[k].skin;
1011                                  fragtbl[team][k_indx] += players[i].frags[k];
1012                              }
1013                          }
1014                      }
1015 
1016                      fragtab[j].count += ST_PlayerFrags(i);
1017                      if(dmtotals)
1018                          dmtotals[team]=fragtab[j].count;
1019                      break;
1020                 }
1021             }  // for j
1022 
1023             if (j==scorelines)
1024             {   // team not found, add it
1025 
1026                 if(fragtbl)
1027                 {
1028                     for(k=0; k<MAXPLAYERS; k++)
1029                         fragtbl[team][k] = 0;
1030                 }
1031 
1032                 fragtab[scorelines].count = ST_PlayerFrags(i);
1033                 fragtab[scorelines].num   = team;
1034                 fragtab[scorelines].color = players[i].skincolor;
1035                 fragtab[scorelines].name  = get_team_name(team);
1036 
1037                 if(fragtbl)
1038                 {
1039                     for(k=0; k<MAXPLAYERS; k++)
1040                     {
1041                         if(playeringame[k])
1042                         {
1043                             int k_indx = (cv_teamplay.EV==1) ?
1044                                 players[k].skincolor : players[k].skin;
1045                             fragtbl[team][k_indx] += players[i].frags[k];
1046                         }
1047                     }
1048                 }
1049 
1050                 if(dmtotals)
1051                     dmtotals[team]=fragtab[scorelines].count;
1052 
1053                 scorelines++;
1054             }
1055         }
1056     }
1057     return scorelines;
1058 }
1059 
1060 
1061 //
1062 //  draw Deathmatch Rankings
1063 //
1064 static
HU_Draw_DeathmatchRankings(void)1065 void HU_Draw_DeathmatchRankings (void)
1066 {
1067     fragsort_t   fragtab[MAXPLAYERS];
1068     int          i;
1069     int          scorelines;
1070     int          whiteplayer;
1071     int          y;
1072     char*	 title;
1073     boolean	 large;
1074 
1075     // Draw screen0, scaled, centered
1076     V_SetupDraw( 0 | V_SCALEPATCH | V_SCALESTART | V_CENTERHORZ );
1077 
1078     // draw the ranking title panel
1079     if(!cv_splitscreen.value)
1080     {
1081         patch_t*  p = W_CachePatchName("RANKINGS",PU_CACHE);  // endian fix
1082         V_DrawScaledPatch ((BASEVIDWIDTH-p->width)/2, 5, p);
1083     }
1084 
1085     // count frags for each present player
1086     scorelines = 0;
1087     for (i=0; i<MAXPLAYERS; i++)
1088     {
1089         if (playeringame[i])
1090         {
1091             fragtab[scorelines].count = ST_PlayerFrags(i);
1092             fragtab[scorelines].num   = i;
1093             fragtab[scorelines].color = players[i].skincolor;
1094             fragtab[scorelines].name  = player_names[i];
1095             scorelines++;
1096         }
1097     }
1098 
1099     //Fab:25-04-98: when you play, you quickly see your frags because your
1100     //  name is displayed white, when playback demo, you quickly see who's the
1101     //  view.
1102     whiteplayer = demoplayback ? displayplayer : consoleplayer;
1103 
1104     if (scorelines>9)
1105         scorelines = 9; //dont draw past bottom of screen, show the best only
1106     else if (cv_splitscreen.value && scorelines > 4)
1107         scorelines = 4;
1108 
1109     if(cv_splitscreen.value)
1110     {
1111         y = (100 - (12 * (scorelines + 1) / 2)) + 15;
1112         title = "Rankings";
1113         large = false;
1114     }
1115     else
1116     {
1117         y = 70;
1118         title = NULL;
1119         large = true;
1120     }
1121 
1122     if(cv_teamplay.EV==0)
1123         WI_Draw_Ranking(title, 80, y, fragtab, scorelines, large, whiteplayer, 32);
1124     else
1125     {
1126         // draw the frag to the right
1127 //        WI_Draw_Ranking("Individual",170,70,fragtab,scorelines,true,whiteplayer);
1128 
1129         scorelines = HU_Create_TeamFragTbl(fragtab,NULL,NULL);
1130 
1131         // and the team frag to the left
1132         WI_Draw_Ranking("Teams", 80, y, fragtab, scorelines, large, players[whiteplayer].skincolor, 32);
1133     }
1134 }
1135 
1136 
1137 // draw the Crosshair, at the exact center of the view.
1138 //
1139 // Crosshairs are pre-cached at HU_Init
1140 #ifdef HWRENDER // not win32 only 19990829 by Kin
1141     extern float gr_basewindowcentery;
1142     extern float gr_viewheight;
1143 #endif
1144 
1145 static
HU_Draw_Crosshair(void)1146 void HU_Draw_Crosshair( void )
1147 {
1148     // vid : from video setup
1149     int y, yinc, chv1, chv2;
1150 
1151     chv1 = cv_crosshair[0].value & 3;
1152     chv2 = (cv_splitscreen.value)? (cv_crosshair[1].value & 3) : 0;
1153     if( !chv1 && !chv2 )
1154         return;
1155 
1156 #if 0
1157     if (cv_crosshairscale.value)
1158         V_SetupDraw( 0 | V_SCALEPATCH | V_SCALESTART );
1159     else
1160         V_SetupDraw( 0 | V_SCALEPATCH | V_NOSCALE );
1161 #else
1162     V_SetupDraw( 0 | V_SCALEPATCH | V_NOSCALE );
1163 #endif
1164 
1165     // reduce this to one rendermode test
1166 #ifdef HWRENDER
1167     if( rendermode != render_soft )
1168     {
1169         y = gr_basewindowcentery;
1170         yinc = gr_viewheight;
1171     }
1172     else
1173 #endif
1174     {
1175         y = viewwindowy + (rdraw_viewheight>>1);
1176         yinc = rdraw_viewheight;
1177     }
1178 
1179     if( chv1 )
1180     {
1181         V_DrawTranslucentPatch (vid.width>>1, y, crosshair_patch[chv1-1]);
1182     }
1183 
1184     if( chv2 )
1185     {
1186         y += yinc;
1187         V_DrawTranslucentPatch (vid.width>>1, y, crosshair_patch[chv2-1]);
1188     }
1189     // V_SetupDraw( drawinfo.prev_screenflags );  // restore
1190 }
1191 
1192 
1193 //======================================================================
1194 //                    CHAT MACROS COMMAND & VARS
1195 //======================================================================
1196 
1197 // better do HackChatmacros() because the strings are NULL !!
1198 
1199 consvar_t cv_chatmacro1 = {"_chatmacro1", NULL, CV_SAVE,NULL};
1200 consvar_t cv_chatmacro2 = {"_chatmacro2", NULL, CV_SAVE,NULL};
1201 consvar_t cv_chatmacro3 = {"_chatmacro3", NULL, CV_SAVE,NULL};
1202 consvar_t cv_chatmacro4 = {"_chatmacro4", NULL, CV_SAVE,NULL};
1203 consvar_t cv_chatmacro5 = {"_chatmacro5", NULL, CV_SAVE,NULL};
1204 consvar_t cv_chatmacro6 = {"_chatmacro6", NULL, CV_SAVE,NULL};
1205 consvar_t cv_chatmacro7 = {"_chatmacro7", NULL, CV_SAVE,NULL};
1206 consvar_t cv_chatmacro8 = {"_chatmacro8", NULL, CV_SAVE,NULL};
1207 consvar_t cv_chatmacro9 = {"_chatmacro9", NULL, CV_SAVE,NULL};
1208 consvar_t cv_chatmacro0 = {"_chatmacro0", NULL, CV_SAVE,NULL};
1209 
1210 
1211 // Set the chatmacros original text, before config is executed.
1212 // If a dehacked patch is loaded, it will change the default text strings.
1213 // The config.cfg strings will override these.
1214 //
HU_Init_Chatmacros(void)1215 void HU_Init_Chatmacros (void)
1216 {
1217     int    i;
1218 
1219     // this is the original text, dehacked can modify it as a default value
1220     cv_chatmacro0.defaultvalue = HUSTR_CHATMACRO0;
1221     cv_chatmacro1.defaultvalue = HUSTR_CHATMACRO1;
1222     cv_chatmacro2.defaultvalue = HUSTR_CHATMACRO2;
1223     cv_chatmacro3.defaultvalue = HUSTR_CHATMACRO3;
1224     cv_chatmacro4.defaultvalue = HUSTR_CHATMACRO4;
1225     cv_chatmacro5.defaultvalue = HUSTR_CHATMACRO5;
1226     cv_chatmacro6.defaultvalue = HUSTR_CHATMACRO6;
1227     cv_chatmacro7.defaultvalue = HUSTR_CHATMACRO7;
1228     cv_chatmacro8.defaultvalue = HUSTR_CHATMACRO8;
1229     cv_chatmacro9.defaultvalue = HUSTR_CHATMACRO9;
1230 
1231     // link chatmacros to cvars
1232     chat_macros[0] = &cv_chatmacro0;
1233     chat_macros[1] = &cv_chatmacro1;
1234     chat_macros[2] = &cv_chatmacro2;
1235     chat_macros[3] = &cv_chatmacro3;
1236     chat_macros[4] = &cv_chatmacro4;
1237     chat_macros[5] = &cv_chatmacro5;
1238     chat_macros[6] = &cv_chatmacro6;
1239     chat_macros[7] = &cv_chatmacro7;
1240     chat_macros[8] = &cv_chatmacro8;
1241     chat_macros[9] = &cv_chatmacro9;
1242 
1243     // register chatmacro vars ready for config.cfg
1244     for (i=0; i<10; i++)
1245        CV_RegisterVar (chat_macros[i]);
1246 }
1247 
1248 
1249 //  chatmacro <0-9> "chat message"
1250 //
Command_Chatmacro_f(void)1251 void Command_Chatmacro_f (void)
1252 {
1253     int    i;
1254 
1255     if (COM_Argc()<2)
1256     {
1257         CONS_Printf ("chatmacro <0-9> : view chatmacro\n"
1258                      "chatmacro <0-9> \"chat message\" : change chatmacro\n");
1259         return;
1260     }
1261 
1262     i = atoi(COM_Argv(1)) % 10;
1263 
1264     if (COM_Argc()==2)
1265     {
1266         CONS_Printf("chatmacro %d is \"%s\"\n",i,chat_macros[i]->string);
1267         return;
1268     }
1269 
1270     // change a chatmacro
1271     CV_Set (chat_macros[i], COM_Argv(2));
1272 }
1273 
1274 
HU_Register_Commands(void)1275 void HU_Register_Commands( void )
1276 {
1277     COM_AddCommand ("say"    , Command_Say_f, CC_chat);
1278     COM_AddCommand ("sayto"  , Command_Sayto_f, CC_chat);
1279     COM_AddCommand ("sayteam", Command_Sayteam_f, CC_chat);
1280     Register_NetXCmd(XD_SAY, Got_NetXCmd_Saycmd);
1281 }
1282