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