1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: hu_stuff.cpp 4633 2014-03-13 01:25:35Z dr_sean $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 2006-2014 by The Odamex 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 // DESCRIPTION:
20 // Heads-up displays
21 //
22 //-----------------------------------------------------------------------------
23
24 #include <algorithm>
25 #include <sstream>
26
27 #include "doomdef.h"
28 #include "z_zone.h"
29 #include "m_swap.h"
30 #include "hu_stuff.h"
31 #include "w_wad.h"
32 #include "s_sound.h"
33 #include "doomstat.h"
34 #include "st_stuff.h"
35 #include "gstrings.h"
36 #include "c_console.h"
37 #include "c_dispatch.h"
38 #include "c_cvars.h"
39 #include "v_text.h"
40
41 #include "cl_main.h"
42 #include "p_ctf.h"
43 #include "i_video.h"
44 #include "i_input.h"
45 #include "cl_netgraph.h"
46 #include "hu_mousegraph.h"
47
48 #include "hu_drawers.h"
49 #include "hu_elements.h"
50
51 #include "v_video.h"
52
53 #define QUEUESIZE 128
54 #define HU_INPUTX 0
55 #define HU_INPUTY (0 + (LESHORT(hu_font[0]->height) +1))
56
57 #define CTFBOARDWIDTH 236
58 #define CTFBOARDHEIGHT 40
59
60 #define DMBOARDWIDTH 368
61 #define DMBOARDHEIGHT 16
62
63 #define TEAMPLAYBORDER 10
64 #define DMBORDER 20
65
66 DCanvas *odacanvas = NULL;
67 extern DCanvas *screen;
68 extern byte *Ranges;
69
70 EXTERN_CVAR (hud_scaletext)
71 EXTERN_CVAR (sv_fraglimit)
72 EXTERN_CVAR (sv_timelimit)
73 EXTERN_CVAR (sv_scorelimit)
74 EXTERN_CVAR (cl_netgraph)
75 EXTERN_CVAR (hud_mousegraph)
76
77 int V_TextScaleXAmount();
78 int V_TextScaleYAmount();
79
80 // Chat
81 void HU_Init (void);
82 void HU_Drawer (void);
83 BOOL HU_Responder (event_t *ev);
84
85 patch_t *hu_font[HU_FONTSIZE];
86 patch_t* sbline;
87
88 void HU_DrawScores (player_t *plyr);
89 void HU_ConsoleScores (player_t *plyr);
90
91 // [Toke - Scores]
92 void HU_DMScores1 (player_t *player);
93 void HU_DMScores2 (player_t *player);
94 void HU_TeamScores1 (player_t *player);
95 void HU_TeamScores2 (player_t *player);
96
97 extern bool HasBehavior;
98 extern inline int V_StringWidth (const char *str);
99 size_t P_NumPlayersInGame();
100 static void ShoveChatStr (std::string str, byte who);
101 void C_ReleaseKeys();
102
103 static std::string input_text;
104 int headsupactive;
105 BOOL altdown;
106
107 NetGraph netgraph(10, 100);
108
109 EXTERN_CVAR (chatmacro0)
110 EXTERN_CVAR (chatmacro1)
111 EXTERN_CVAR (chatmacro2)
112 EXTERN_CVAR (chatmacro3)
113 EXTERN_CVAR (chatmacro4)
114 EXTERN_CVAR (chatmacro5)
115 EXTERN_CVAR (chatmacro6)
116 EXTERN_CVAR (chatmacro7)
117 EXTERN_CVAR (chatmacro8)
118 EXTERN_CVAR (chatmacro9)
119
120 cvar_t *chat_macros[10] =
121 {
122 &chatmacro0,
123 &chatmacro1,
124 &chatmacro2,
125 &chatmacro3,
126 &chatmacro4,
127 &chatmacro5,
128 &chatmacro6,
129 &chatmacro7,
130 &chatmacro8,
131 &chatmacro9
132 };
133
134 //
135 // HU_Init
136 //
HU_Init(void)137 void HU_Init (void)
138 {
139 int i, j, sub;
140 const char *tplate = "STCFN%.3d";
141 char buffer[12];
142
143 headsupactive = 0;
144 input_text = "";
145
146 // load the heads-up font
147 j = HU_FONTSTART;
148 sub = 0;
149
150 for (i = 0; i < HU_FONTSIZE; i++)
151 {
152 sprintf (buffer, tplate, j++ - sub);
153 hu_font[i] = W_CachePatch(buffer, PU_STATIC);
154 }
155
156 // Load the status bar line
157 sbline = W_CachePatch("SBLINE", PU_STATIC);
158 }
159
160 //
161 // HU_Responder
162 //
HU_Responder(event_t * ev)163 BOOL HU_Responder (event_t *ev)
164 {
165 unsigned char c;
166
167 if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT || ev->data1 == KEY_HAT1)
168 {
169 altdown = (ev->type == ev_keydown);
170 return false;
171 }
172 else if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)
173 {
174 return false;
175 }
176 else if ( (gamestate != GS_LEVEL && gamestate != GS_INTERMISSION)
177 || ev->type != ev_keydown)
178 {
179 if (headsupactive)
180 {
181 return true;
182 }
183
184 return false;
185 }
186
187 if (!headsupactive)
188 return false;
189
190 c = ev->data3; // [RH] Use localized keymap
191
192 // send a macro
193 if (altdown)
194 {
195 if ((ev->data2 >= '0' && ev->data2 <= '9') || (ev->data2 >= KEY_JOY1 && ev->data2 <= KEY_JOY10))
196 {
197 if (ev->data2 >= KEY_JOY1 && ev->data2 <= KEY_JOY10)
198 ShoveChatStr (chat_macros[ev->data2 - KEY_JOY1]->cstring(), headsupactive - 1);
199 else
200 ShoveChatStr (chat_macros[ev->data2 - '0']->cstring(), headsupactive - 1);
201
202 I_DisableKeyRepeat();
203 headsupactive = 0;
204 return true;
205 }
206 }
207 if (ev->data3 == KEY_ENTER)
208 {
209 ShoveChatStr (input_text, headsupactive - 1);
210 I_DisableKeyRepeat();
211 headsupactive = 0;
212 return true;
213 }
214 else if (ev->data1 == KEY_ESCAPE || ev->data1 == KEY_JOY2)
215 {
216 I_DisableKeyRepeat();
217 headsupactive = 0;
218 return true;
219 }
220 else if (ev->data1 == KEY_BACKSPACE)
221 {
222 if (input_text.length())
223 input_text.erase(input_text.end() - 1);
224
225 return true;
226 }
227 else
228 {
229 if (c < ' ' || c > '~') // ASCII only please
230 return false;
231
232 if(input_text.length() < MAX_CHATSTR_LEN)
233 input_text += c;
234
235 return true;
236 }
237
238 return false;
239 }
240
241 EXTERN_CVAR(hud_targetcount)
EXTERN_CVAR(sv_maxplayers)242 EXTERN_CVAR (sv_maxplayers)
243
244 //
245 // HU_Drawer
246 //
247 void HU_Drawer (void)
248 {
249 // Set up text scaling
250 int scaledxfac = CleanXfac, scaledyfac = CleanYfac;
251 if (hud_scaletext)
252 {
253 scaledxfac = V_TextScaleXAmount();
254 scaledyfac = V_TextScaleYAmount();
255 }
256
257 if (headsupactive)
258 {
259 static const char *prompt;
260 int i, x, c, y, promptwidth;
261
262 // Determine what Y height to display the chat prompt at.
263 // * screen->height is the "actual" screen height.
264 // * realviewheight is how big the view is, taking into account the
265 // status bar and current "screen size".
266 // * viewactive is false if you have a fullscreen automap or
267 // intermission on-screen.
268 // * ST_Y is the current Y height of the status bar.
269
270 if (!viewactive && gamestate != GS_INTERMISSION) {
271 // Fullscreen automap is visible
272 y = ST_Y - (20 * scaledyfac);
273 } else if (viewactive && screen->height != realviewheight) {
274 // Status bar is visible
275 y = ST_Y - (10 * scaledyfac);
276 } else {
277 // Must be fullscreen HUD or intermission
278 y = screen->height - (10 * scaledyfac);
279 }
280
281 if (headsupactive == 2)
282 prompt = "Say (TEAM): ";
283 else if (headsupactive == 1)
284 prompt = "Say: ";
285
286 promptwidth = V_StringWidth (prompt) * scaledxfac;
287 x = hu_font['_' - HU_FONTSTART]->width() * scaledxfac * 2 + promptwidth;
288
289 // figure out if the text is wider than the screen->
290 // if so, only draw the right-most portion of it.
291 for (i = input_text.length() - 1; i >= 0 && x < screen->width; i--)
292 {
293 c = toupper(input_text[i] & 0x7f) - HU_FONTSTART;
294 if (c < 0 || c >= HU_FONTSIZE)
295 {
296 x += 4 * scaledxfac;
297 }
298 else
299 {
300 x += hu_font[c]->width() * scaledxfac;
301 }
302 }
303
304 if (i >= 0)
305 i++;
306 else
307 i = 0;
308
309 // draw the prompt, text, and cursor
310 std::string show_text = input_text;
311 show_text += '_';
312 screen->DrawTextStretched ( CR_RED, 0, y, prompt,
313 scaledxfac, scaledyfac);
314 screen->DrawTextStretched ( CR_GREY, promptwidth, y, show_text.c_str() + i,
315 scaledxfac, scaledyfac);
316 }
317
318 if (multiplayer && consoleplayer().camera && !(demoplayback && democlassic)) {
319 if ((Actions[ACTION_SHOWSCORES] && gamestate != GS_INTERMISSION) ||
320 (displayplayer().health <= 0 && !displayplayer().spectator && gamestate != GS_INTERMISSION)) {
321 HU_DrawScores(&displayplayer());
322 }
323 }
324
325 // [csDoom] draw disconnected wire [Toke] Made this 1337er
326 // denis - moved to hu_stuff and uncommented
327 if (noservermsgs && (gamestate == GS_INTERMISSION || gamestate == GS_LEVEL) )
328 {
329 patch_t *netlag = W_CachePatch ("NET");
330 screen->DrawPatchCleanNoMove (netlag, 50*CleanXfac, CleanYfac);
331
332 // SoM: Not here.
333 //screen->Dim ();
334 }
335
336 if (cl_netgraph)
337 netgraph.draw();
338
339 if (hud_mousegraph)
340 mousegraph.draw(hud_mousegraph);
341 }
342
ShoveChatStr(std::string str,byte who)343 static void ShoveChatStr (std::string str, byte who)
344 {
345 // Do not send this chat message if the chat string is empty
346 if (str.length() == 0)
347 return;
348
349 if(str.length() > MAX_CHATSTR_LEN)
350 str.resize(MAX_CHATSTR_LEN);
351
352 MSG_WriteMarker (&net_buffer, clc_say);
353 MSG_WriteByte (&net_buffer, who);
354 MSG_WriteString (&net_buffer, str.c_str());
355 }
356
ShovePrivMsg(byte pid,std::string str)357 static void ShovePrivMsg(byte pid, std::string str)
358 {
359 // Do not send this chat message if the chat string is empty
360 if (str.length() == 0)
361 return;
362
363 if (str.length() > MAX_CHATSTR_LEN)
364 str.resize(MAX_CHATSTR_LEN);
365
366 MSG_WriteMarker(&net_buffer, clc_privmsg);
367 MSG_WriteByte(&net_buffer, pid);
368 MSG_WriteString(&net_buffer, str.c_str());
369 }
370
BEGIN_COMMAND(messagemode)371 BEGIN_COMMAND (messagemode)
372 {
373 if(!connected)
374 return;
375
376 headsupactive = 1;
377 C_HideConsole ();
378 I_EnableKeyRepeat();
379 input_text = "";
380 C_ReleaseKeys();
381 }
382 END_COMMAND (messagemode)
383
BEGIN_COMMAND(say)384 BEGIN_COMMAND (say)
385 {
386 if (argc > 1)
387 {
388 std::string chat = C_ArgCombine(argc - 1, (const char **)(argv + 1));
389 ShoveChatStr(chat, 0);
390 }
391 }
392 END_COMMAND (say)
393
BEGIN_COMMAND(messagemode2)394 BEGIN_COMMAND (messagemode2)
395 {
396 if(!connected || (sv_gametype != GM_TEAMDM && sv_gametype != GM_CTF && !consoleplayer().spectator))
397 return;
398
399 headsupactive = 2;
400 C_HideConsole ();
401 I_EnableKeyRepeat();
402 input_text = "";
403 C_ReleaseKeys();
404 }
405 END_COMMAND (messagemode2)
406
BEGIN_COMMAND(say_team)407 BEGIN_COMMAND (say_team)
408 {
409 if (argc > 1)
410 {
411 std::string chat = C_ArgCombine(argc - 1, (const char **)(argv + 1));
412 ShoveChatStr(chat, 1);
413 }
414 }
415 END_COMMAND (say_team)
416
BEGIN_COMMAND(say_to)417 BEGIN_COMMAND (say_to)
418 {
419 if (argc > 2)
420 {
421 player_t &player = nameplayer(argv[1]);
422 if (!validplayer(player))
423 {
424 Printf(PRINT_HIGH, "%s isn't the name of anybody on the server.\n", argv[1]);
425 return;
426 }
427
428 std::string chat = C_ArgCombine(argc - 2, (const char **)(argv + 2));
429 ShovePrivMsg(player.id, chat);
430 }
431 }
432 END_COMMAND (say_to)
433
434 EXTERN_CVAR(hud_scalescoreboard)
435
436 EXTERN_CVAR(sv_gametype)
437 EXTERN_CVAR(sv_maxplayers)
438 EXTERN_CVAR(sv_hostname)
439
440 namespace hud {
441
442 // [AM] Draw scoreboard header
drawHeader(player_t * player,int y)443 void drawHeader(player_t *player, int y) {
444 int color;
445 std::ostringstream buffer;
446 std::string str;
447
448 // Center
449 if (sv_gametype == GM_COOP) {
450 str = "COOPERATIVE";
451 } else if (sv_gametype == GM_DM && sv_maxplayers == 2) {
452 str = "DUEL";
453 } else if (sv_gametype == GM_DM) {
454 str = "DEATHMATCH";
455 } else if (sv_gametype == GM_TEAMDM) {
456 str = "TEAM DEATHMATCH";
457 } else if (sv_gametype == GM_CTF) {
458 str = "CAPTURE THE FLAG";
459 }
460
461 hud::DrawText(0, y, hud_scalescoreboard,
462 hud::X_CENTER, hud::Y_MIDDLE,
463 hud::X_CENTER, hud::Y_TOP,
464 str.c_str(), CR_GOLD, true);
465
466 brokenlines_t *hostname = V_BreakLines(192, sv_hostname.cstring());
467 for (size_t i = 0; i < 2 && hostname[i].width > 0; i++)
468 {
469 hud::DrawText(0, y + 8 * (i + 1), hud_scalescoreboard,
470 hud::X_CENTER, hud::Y_MIDDLE,
471 hud::X_CENTER, hud::Y_TOP,
472 hostname[i].string, CR_GREY, true);
473 }
474 V_FreeBrokenLines(hostname);
475
476 // Left
477 hud::DrawText(-236, y, hud_scalescoreboard,
478 hud::X_CENTER, hud::Y_MIDDLE,
479 hud::X_LEFT, hud::Y_TOP,
480 "CLIENTS: ", CR_GREY, true);
481 hud::DrawText(-236 + V_StringWidth("CLIENTS: "), y, hud_scalescoreboard,
482 hud::X_CENTER, hud::Y_MIDDLE,
483 hud::X_LEFT, hud::Y_TOP,
484 hud::ClientsSplit().c_str(), CR_GREEN, true);
485 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
486 hud::DrawText(-236, y + 8, hud_scalescoreboard,
487 hud::X_CENTER, hud::Y_MIDDLE,
488 hud::X_LEFT, hud::Y_TOP,
489 "BLUE PLAYERS: ", CR_GREY, true);
490 hud::DrawText(-236 + V_StringWidth("BLUE PLAYERS: "), y + 8, hud_scalescoreboard,
491 hud::X_CENTER, hud::Y_MIDDLE,
492 hud::X_LEFT, hud::Y_TOP,
493 TeamPlayers(color, TEAM_BLUE).c_str(), CR_GREEN, true);
494 hud::DrawText(-236, y + 16, hud_scalescoreboard,
495 hud::X_CENTER, hud::Y_MIDDLE,
496 hud::X_LEFT, hud::Y_TOP,
497 "RED PLAYERS: ", CR_GREY, true);
498 hud::DrawText(-236 + V_StringWidth("RED PLAYERS: "), y + 16, hud_scalescoreboard,
499 hud::X_CENTER, hud::Y_MIDDLE,
500 hud::X_LEFT, hud::Y_TOP,
501 TeamPlayers(color, TEAM_RED).c_str(), CR_GREEN, true);
502 } else {
503 hud::DrawText(-236, y + 8, hud_scalescoreboard,
504 hud::X_CENTER, hud::Y_MIDDLE,
505 hud::X_LEFT, hud::Y_TOP,
506 "PLAYERS: ", CR_GREY, true);
507 hud::DrawText(-236 + V_StringWidth("PLAYERS: "), y + 8, hud_scalescoreboard,
508 hud::X_CENTER, hud::Y_MIDDLE,
509 hud::X_LEFT, hud::Y_TOP,
510 hud::PlayersSplit().c_str(), CR_GREEN, true);
511 }
512
513 // Right
514 std::string timer, fraglimit, scorelimit;
515 if (gamestate == GS_INTERMISSION) {
516 timer = hud::IntermissionTimer();
517 } else {
518 timer = hud::Timer(color);
519 }
520 if (timer.empty()) {
521 timer = "N/A";
522 }
523 buffer.clear();
524 if (sv_fraglimit.asInt() == 0) {
525 buffer.str("N/A");
526 } else {
527 buffer.str("");
528 buffer << sv_fraglimit.asInt();
529 }
530 fraglimit = buffer.str();
531 buffer.clear();
532 if (sv_scorelimit.asInt() == 0) {
533 buffer.str("N/A");
534 } else {
535 buffer.str("");
536 buffer << sv_scorelimit.asInt();
537 }
538 scorelimit = buffer.str();
539
540 int rw = V_StringWidth("00:00");
541 if (sv_timelimit.asInt() == 0 && gamestate != GS_INTERMISSION) {
542 rw = V_StringWidth("N/A");
543 } else if (timer.size() > 5) {
544 rw = V_StringWidth("00:00:00");
545 }
546 if (V_StringWidth(fraglimit.c_str()) > rw) {
547 rw = V_StringWidth(fraglimit.c_str());
548 }
549 if (V_StringWidth(scorelimit.c_str()) > rw) {
550 rw = V_StringWidth(scorelimit.c_str());
551 }
552
553 hud::DrawText(236 - rw, y, hud_scalescoreboard,
554 hud::X_CENTER, hud::Y_MIDDLE,
555 hud::X_LEFT, hud::Y_TOP,
556 timer.c_str(), CR_GREEN, true);
557 hud::DrawText(236 - rw, y + 8, hud_scalescoreboard,
558 hud::X_CENTER, hud::Y_MIDDLE,
559 hud::X_LEFT, hud::Y_TOP,
560 fraglimit.c_str(), CR_GREEN, true);
561 hud::DrawText(236 - rw, y + 16, hud_scalescoreboard,
562 hud::X_CENTER, hud::Y_MIDDLE,
563 hud::X_LEFT, hud::Y_TOP,
564 scorelimit.c_str(), CR_GREEN, true);
565
566 hud::DrawText(236 - rw, y, hud_scalescoreboard,
567 hud::X_CENTER, hud::Y_MIDDLE,
568 hud::X_RIGHT, hud::Y_TOP,
569 "TIME LEFT: ", CR_GREY, true);
570 hud::DrawText(236 - rw, y + 8, hud_scalescoreboard,
571 hud::X_CENTER, hud::Y_MIDDLE,
572 hud::X_RIGHT, hud::Y_TOP,
573 "FRAGLIMIT: ", CR_GREY, true);
574 hud::DrawText(236 - rw, y + 16, hud_scalescoreboard,
575 hud::X_CENTER, hud::Y_MIDDLE,
576 hud::X_RIGHT, hud::Y_TOP,
577 "SCORELIMIT: ", CR_GREY, true);
578
579 // Line
580 for (short xi = -236 + 1;xi < 236;xi += 2) {
581 hud::DrawTranslatedPatch(xi, y + 24, hud_scalescoreboard,
582 hud::X_CENTER, hud::Y_MIDDLE,
583 hud::X_CENTER, hud::Y_TOP,
584 sbline, Ranges + CR_GREY * 256, true);
585 }
586 }
587
588 // [AM] Draw scores for teamless gametypes.
drawScores(player_t * player,int y,byte extra_rows)589 void drawScores(player_t *player, int y, byte extra_rows) {
590 std::string str;
591
592 // Colum headers
593 hud::DrawText(-227, y, hud_scalescoreboard,
594 hud::X_CENTER, hud::Y_MIDDLE,
595 hud::X_LEFT, hud::Y_TOP,
596 "Name", CR_GREY, true);
597 if (sv_gametype != GM_COOP) {
598 hud::DrawText(44, y, hud_scalescoreboard,
599 hud::X_CENTER, hud::Y_MIDDLE,
600 hud::X_RIGHT, hud::Y_TOP,
601 "FRG", CR_GREY, true);
602 hud::DrawText(92, y, hud_scalescoreboard,
603 hud::X_CENTER, hud::Y_MIDDLE,
604 hud::X_RIGHT, hud::Y_TOP,
605 "DTH", CR_GREY, true);
606 hud::DrawText(140, y, hud_scalescoreboard,
607 hud::X_CENTER, hud::Y_MIDDLE,
608 hud::X_RIGHT, hud::Y_TOP,
609 "K/D", CR_GREY, true);
610 } else {
611 hud::DrawText(92, y, hud_scalescoreboard,
612 hud::X_CENTER, hud::Y_MIDDLE,
613 hud::X_RIGHT, hud::Y_TOP,
614 "KIL", CR_GREY, true);
615 hud::DrawText(140, y, hud_scalescoreboard,
616 hud::X_CENTER, hud::Y_MIDDLE,
617 hud::X_RIGHT, hud::Y_TOP,
618 "DTH", CR_GREY, true);
619 }
620 hud::DrawText(188, y, hud_scalescoreboard,
621 hud::X_CENTER, hud::Y_MIDDLE,
622 hud::X_RIGHT, hud::Y_TOP,
623 "MIN", CR_GREY, true);
624 hud::DrawText(236, y, hud_scalescoreboard,
625 hud::X_CENTER, hud::Y_MIDDLE,
626 hud::X_RIGHT, hud::Y_TOP,
627 "PNG", CR_GREY, true);
628
629 // Line
630 for (short xi = -236 + 1;xi < 236;xi += 2) {
631 hud::DrawTranslatedPatch(xi, y + 8, hud_scalescoreboard,
632 hud::X_CENTER, hud::Y_MIDDLE,
633 hud::X_CENTER, hud::Y_TOP,
634 sbline, Ranges + CR_GREY * 256, true);
635 }
636
637 // Ingame Players
638 byte limit = extra_rows + 4;
639 hud::EAPlayerColors(-236, y + 11, 7, 7, hud_scalescoreboard,
640 hud::X_CENTER, hud::Y_MIDDLE,
641 hud::X_LEFT, hud::Y_TOP,
642 1, limit);
643 hud::EAPlayerNames(-227, y + 11, hud_scalescoreboard,
644 hud::X_CENTER, hud::Y_MIDDLE,
645 hud::X_LEFT, hud::Y_TOP,
646 1, limit, true);
647 if (sv_gametype != GM_COOP) {
648 hud::EAPlayerFrags(44, y + 11, hud_scalescoreboard,
649 hud::X_CENTER, hud::Y_MIDDLE,
650 hud::X_RIGHT, hud::Y_TOP,
651 1, limit, true);
652 hud::EAPlayerDeaths(92, y + 11, hud_scalescoreboard,
653 hud::X_CENTER, hud::Y_MIDDLE,
654 hud::X_RIGHT, hud::Y_TOP,
655 1, limit, true);
656 hud::EAPlayerKD(140, y + 11, hud_scalescoreboard,
657 hud::X_CENTER, hud::Y_MIDDLE,
658 hud::X_RIGHT, hud::Y_TOP,
659 1, limit, true);
660 } else {
661 hud::EAPlayerKills(92, y + 11, hud_scalescoreboard,
662 hud::X_CENTER, hud::Y_MIDDLE,
663 hud::X_RIGHT, hud::Y_TOP,
664 1, limit, true);
665 hud::EAPlayerDeaths(140, y + 11, hud_scalescoreboard,
666 hud::X_CENTER, hud::Y_MIDDLE,
667 hud::X_RIGHT, hud::Y_TOP,
668 1, limit, true);
669 }
670 hud::EAPlayerTimes(188, y + 11, hud_scalescoreboard,
671 hud::X_CENTER, hud::Y_MIDDLE,
672 hud::X_RIGHT, hud::Y_TOP,
673 1, limit, true);
674 hud::EAPlayerPings(236, y + 11, hud_scalescoreboard,
675 hud::X_CENTER, hud::Y_MIDDLE,
676 hud::X_RIGHT, hud::Y_TOP,
677 1, limit, true);
678 }
679
680 // [AM] Draw scores for team gametypes.
drawTeamScores(player_t * player,int y,byte extra_rows)681 void drawTeamScores(player_t *player, int y, byte extra_rows) {
682 int color;
683 std::ostringstream buffer;
684 std::string str;
685 static short tx[2] = {-236, 4};
686
687 for (byte i = 0;i < 2;i++) {
688 // Column headers
689 hud::DrawText(tx[i] + 9, y, hud_scalescoreboard,
690 hud::X_CENTER, hud::Y_MIDDLE,
691 hud::X_LEFT, hud::Y_TOP,
692 "Name", CR_GREY, true);
693 if (sv_gametype == GM_CTF) {
694 hud::DrawText(tx[i] + 168, y, hud_scalescoreboard,
695 hud::X_CENTER, hud::Y_MIDDLE,
696 hud::X_RIGHT, hud::Y_TOP,
697 "PTS", CR_GREY, true);
698 hud::DrawText(tx[i] + 200, y, hud_scalescoreboard,
699 hud::X_CENTER, hud::Y_MIDDLE,
700 hud::X_RIGHT, hud::Y_TOP,
701 "FRG", CR_GREY, true);
702 } else {
703 hud::DrawText(tx[i] + 164, y, hud_scalescoreboard,
704 hud::X_CENTER, hud::Y_MIDDLE,
705 hud::X_RIGHT, hud::Y_TOP,
706 "FRG", CR_GREY, true);
707 hud::DrawText(tx[i] + 200, y, hud_scalescoreboard,
708 hud::X_CENTER, hud::Y_MIDDLE,
709 hud::X_RIGHT, hud::Y_TOP,
710 "K/D", CR_GREY, true);
711 }
712 hud::DrawText(tx[i] + 232, y, hud_scalescoreboard,
713 hud::X_CENTER, hud::Y_MIDDLE,
714 hud::X_RIGHT, hud::Y_TOP,
715 "PNG", CR_GREY, true);
716
717 // Line
718 if (i == TEAM_BLUE) {
719 color = CR_BLUE;
720 } else {
721 color = CR_RED;
722 }
723
724 for (short xi = tx[i];xi < tx[i] + 232;xi += 2) {
725 hud::DrawTranslatedPatch(xi, y + 8, hud_scalescoreboard,
726 hud::X_CENTER, hud::Y_MIDDLE,
727 hud::X_LEFT, hud::Y_TOP,
728 sbline, Ranges + color * 256, true);
729 hud::DrawTranslatedPatch(xi, y + 19, hud_scalescoreboard,
730 hud::X_CENTER, hud::Y_MIDDLE,
731 hud::X_LEFT, hud::Y_TOP,
732 sbline, Ranges + color * 256, true);
733 }
734
735 // Team Info
736 str = hud::TeamName(color, i);
737 hud::DrawText(tx[i] + 9, y + 11, hud_scalescoreboard,
738 hud::X_CENTER, hud::Y_MIDDLE,
739 hud::X_LEFT, hud::Y_TOP,
740 str.c_str(), color, true);
741 if (sv_gametype == GM_CTF) {
742 hud::DrawText(tx[i] + 168, y + 11, hud_scalescoreboard,
743 hud::X_CENTER, hud::Y_MIDDLE,
744 hud::X_RIGHT, hud::Y_TOP,
745 hud::TeamPoints(color, i).c_str(), color, true);
746 str = hud::TeamFrags(color, i);
747 hud::DrawText(tx[i] + 200, y + 11, hud_scalescoreboard,
748 hud::X_CENTER, hud::Y_MIDDLE,
749 hud::X_RIGHT, hud::Y_TOP,
750 str.c_str(), color, true);
751 } else {
752 hud::DrawText(tx[i] + 164, y + 11, hud_scalescoreboard,
753 hud::X_CENTER, hud::Y_MIDDLE,
754 hud::X_RIGHT, hud::Y_TOP,
755 hud::TeamPoints(color, i).c_str(), color, true);
756 str = hud::TeamKD(color, i);
757 hud::DrawText(tx[i] + 200, y + 11, hud_scalescoreboard,
758 hud::X_CENTER, hud::Y_MIDDLE,
759 hud::X_RIGHT, hud::Y_TOP,
760 str.c_str(), color, true);
761 }
762 str = hud::TeamPing(color, i);
763 hud::DrawText(tx[i] + 232, y + 11, hud_scalescoreboard,
764 hud::X_CENTER, hud::Y_MIDDLE,
765 hud::X_RIGHT, hud::Y_TOP,
766 str.c_str(), color, true);
767
768 // Ingame Players
769 byte limit = extra_rows + 4;
770 hud::EATeamPlayerColors(tx[i], y + 22, 7, 7, hud_scalescoreboard,
771 hud::X_CENTER, hud::Y_MIDDLE,
772 hud::X_LEFT, hud::Y_TOP,
773 1, limit, i);
774 hud::EATeamPlayerNames(tx[i] + 9, y + 22, hud_scalescoreboard,
775 hud::X_CENTER, hud::Y_MIDDLE,
776 hud::X_LEFT, hud::Y_TOP,
777 1, limit, i, true);
778 if (sv_gametype == GM_CTF) {
779 hud::EATeamPlayerPoints(tx[i] + 168, y + 22, hud_scalescoreboard,
780 hud::X_CENTER, hud::Y_MIDDLE,
781 hud::X_RIGHT, hud::Y_TOP,
782 1, limit, i, true);
783 hud::EATeamPlayerFrags(tx[i] + 200, y + 22, hud_scalescoreboard,
784 hud::X_CENTER, hud::Y_MIDDLE,
785 hud::X_RIGHT, hud::Y_TOP,
786 1, limit, i, true);
787 } else {
788 hud::EATeamPlayerFrags(tx[i] + 164, y + 22, hud_scalescoreboard,
789 hud::X_CENTER, hud::Y_MIDDLE,
790 hud::X_RIGHT, hud::Y_TOP,
791 1, limit, i, true);
792 hud::EATeamPlayerKD(tx[i] + 200, y + 22, hud_scalescoreboard,
793 hud::X_CENTER, hud::Y_MIDDLE,
794 hud::X_RIGHT, hud::Y_TOP,
795 1, limit, i, true);
796 }
797 hud::EATeamPlayerPings(tx[i] + 232, y + 22, hud_scalescoreboard,
798 hud::X_CENTER, hud::Y_MIDDLE,
799 hud::X_RIGHT, hud::Y_TOP,
800 1, limit, i, true);
801 }
802 }
803
804 // [AM] Draw spectators.
drawSpectators(player_t * player,int y,byte extra_rows)805 void drawSpectators(player_t *player, int y, byte extra_rows) {
806 static short tx[3] = {-236, -79, 78};
807
808 // Line
809 for (short xi = -236 + 1;xi < 236;xi += 2) {
810 hud::DrawTranslatedPatch(xi, y, hud_scalescoreboard,
811 hud::X_CENTER, hud::Y_MIDDLE,
812 hud::X_CENTER, hud::Y_TOP,
813 sbline, Ranges + CR_GREY * 256, true);
814 }
815
816 byte specs = hud::CountSpectators();
817 byte skip = 0;
818 byte limit;
819 for (byte i = 0;i < 3;i++) {
820 limit = (specs + 2 - i) / 3;
821 if (extra_rows + 1 < limit) {
822 limit = extra_rows + 1;
823 }
824 hud::EASpectatorNames(tx[i], y + 3, hud_scalescoreboard,
825 hud::X_CENTER, hud::Y_MIDDLE,
826 hud::X_LEFT, hud::Y_TOP,
827 1, skip, limit, true);
828 hud::EASpectatorPings(tx[i] + 124, y + 3, hud_scalescoreboard,
829 hud::X_CENTER, hud::Y_MIDDLE,
830 hud::X_LEFT, hud::Y_TOP,
831 1, skip, limit, true);
832 skip += limit;
833 }
834 }
835
836 // [AM] Draw the scoreboard
Scoreboard(player_t * player)837 void Scoreboard(player_t *player) {
838 // Calculate height
839 int height, y;
840 byte extra_spec_rows = 0;
841 byte extra_player_rows = 0;
842
843 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
844 height = 99;
845
846 // Team scoreboard was designed for 4 players on a team. If
847 // there are more, increase the height.
848 byte blue = hud::CountTeamPlayers(TEAM_BLUE);
849 byte red = hud::CountTeamPlayers(TEAM_RED);
850 if (blue > 4 || red > 4) {
851 extra_player_rows += (blue > red) ? (blue - 4) : (red - 4);
852 }
853 } else {
854 height = 88;
855
856 // Normal scoreboard was designed for 4 players. If there are
857 // more, increase the height.
858 byte players = P_NumPlayersInGame();
859 if (players > 4) {
860 extra_player_rows += players - 4;
861 }
862 }
863
864 // If there are more than 3 spectators, increase the height.
865 byte specs = hud::CountSpectators();
866 if (specs > 3) {
867 extra_spec_rows += ((specs + 2) / 3) - 1;
868 }
869
870 height += (extra_player_rows + extra_spec_rows) * 8;
871
872 // 368 is our max height.
873 while (height > 368) {
874 // We have too many rows. Start removing extra spectator rows
875 // to make the scoreboard fit.
876 if (extra_spec_rows == 0) {
877 break;
878 }
879 extra_spec_rows -= 1;
880 height -= 8;
881 }
882
883 while (height > 368) {
884 // We still have too many rows. Start removing extra player
885 // rows to make the scoreboard fit.
886 if (extra_player_rows == 0) {
887 break;
888 }
889 extra_player_rows -= 1;
890 height -= 8;
891 }
892
893 // Starting Y position, measured from the center up.
894 y = -(height / 2);
895 if (y > -96) {
896 // Scoreboard starts slightly off-center, then grows to be centered.
897 // -96 makes it even with where the default lowres TDM begins.
898 // For the record, the lowres DM scoreboard begins at -54.
899 y = -96;
900 }
901
902 // Dim the background
903 hud::Dim(0, y, 480, height, hud_scalescoreboard,
904 hud::X_CENTER, hud::Y_MIDDLE,
905 hud::X_CENTER, hud::Y_TOP);
906
907 hud::drawHeader(player, y + 4);
908 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
909 hud::drawTeamScores(player, y + 31, extra_player_rows);
910 } else {
911 hud::drawScores(player, y + 31, extra_player_rows);
912 }
913 hud::drawSpectators(player, y + (height - 14 - (extra_spec_rows * 8)),
914 extra_spec_rows);
915 }
916
917 // [AM] Draw the low-resolution scoreboard header.
drawLowHeader(player_t * player,int y)918 void drawLowHeader(player_t *player, int y) {
919 std::string str;
920
921 // Center
922 if (sv_gametype == GM_COOP) {
923 str = "COOPERATIVE";
924 } else if (sv_gametype == GM_DM && sv_maxplayers == 2) {
925 str = "DUEL";
926 } else if (sv_gametype == GM_DM) {
927 str = "DEATHMATCH";
928 } else if (sv_gametype == GM_TEAMDM) {
929 str = "TEAM DEATHMATCH";
930 } else if (sv_gametype == GM_CTF) {
931 str = "CAPTURE THE FLAG";
932 }
933
934 hud::DrawText(0, y, hud_scalescoreboard,
935 hud::X_CENTER, hud::Y_MIDDLE,
936 hud::X_CENTER, hud::Y_TOP,
937 str.c_str(), CR_GOLD, true);
938
939 // Line
940 for (short xi = -146 + 1;xi < 146;xi += 2) {
941 hud::DrawTranslatedPatch(xi, y + 8, hud_scalescoreboard,
942 hud::X_CENTER, hud::Y_MIDDLE,
943 hud::X_CENTER, hud::Y_TOP,
944 sbline, Ranges + CR_GREY * 256, true);
945 }
946 }
947
948 // [AM] Draw low-resolution teamless gametype scores.
drawLowScores(player_t * player,int y,byte extra_rows)949 void drawLowScores(player_t *player, int y, byte extra_rows) {
950 std::string str;
951
952 // Colum headers
953 hud::DrawText(-137, y, hud_scalescoreboard,
954 hud::X_CENTER, hud::Y_MIDDLE,
955 hud::X_LEFT, hud::Y_TOP,
956 "Name", CR_GREY, true);
957 if (sv_gametype != GM_COOP) {
958 hud::DrawText(22, y, hud_scalescoreboard,
959 hud::X_CENTER, hud::Y_MIDDLE,
960 hud::X_RIGHT, hud::Y_TOP,
961 "FRG", CR_GREY, true);
962 hud::DrawText(50, y, hud_scalescoreboard,
963 hud::X_CENTER, hud::Y_MIDDLE,
964 hud::X_RIGHT, hud::Y_TOP,
965 "DTH", CR_GREY, true);
966 hud::DrawText(90, y, hud_scalescoreboard,
967 hud::X_CENTER, hud::Y_MIDDLE,
968 hud::X_RIGHT, hud::Y_TOP,
969 "K/D", CR_GREY, true);
970 } else {
971 hud::DrawText(62, y, hud_scalescoreboard,
972 hud::X_CENTER, hud::Y_MIDDLE,
973 hud::X_RIGHT, hud::Y_TOP,
974 "KIL", CR_GREY, true);
975 hud::DrawText(90, y, hud_scalescoreboard,
976 hud::X_CENTER, hud::Y_MIDDLE,
977 hud::X_RIGHT, hud::Y_TOP,
978 "DTH", CR_GREY, true);
979 }
980 hud::DrawText(118, y, hud_scalescoreboard,
981 hud::X_CENTER, hud::Y_MIDDLE,
982 hud::X_RIGHT, hud::Y_TOP,
983 "MIN", CR_GREY, true);
984 hud::DrawText(146, y, hud_scalescoreboard,
985 hud::X_CENTER, hud::Y_MIDDLE,
986 hud::X_RIGHT, hud::Y_TOP,
987 "PNG", CR_GREY, true);
988
989 // Line
990 for (short xi = -146 + 1;xi < 146;xi += 2) {
991 hud::DrawTranslatedPatch(xi, y + 8, hud_scalescoreboard,
992 hud::X_CENTER, hud::Y_MIDDLE,
993 hud::X_CENTER, hud::Y_TOP,
994 sbline, Ranges + CR_GREY * 256, true);
995 }
996
997 // Ingame Players
998 byte limit = extra_rows + 4;
999 hud::EAPlayerColors(-146, y + 11, 7, 7, hud_scalescoreboard,
1000 hud::X_CENTER, hud::Y_MIDDLE,
1001 hud::X_LEFT, hud::Y_TOP,
1002 1, limit);
1003 hud::EAPlayerNames(-137, y + 11, hud_scalescoreboard,
1004 hud::X_CENTER, hud::Y_MIDDLE,
1005 hud::X_LEFT, hud::Y_TOP,
1006 1, limit, true);
1007 if (sv_gametype != GM_COOP) {
1008 // NOTE: If we ever get true kill counts, we don't have
1009 // enough room on this scoreboard to show frags, kills,
1010 // deaths and K/D.
1011 hud::EAPlayerFrags(22, y + 11, hud_scalescoreboard,
1012 hud::X_CENTER, hud::Y_MIDDLE,
1013 hud::X_RIGHT, hud::Y_TOP,
1014 1, limit, true);
1015 hud::EAPlayerDeaths(50, y + 11, hud_scalescoreboard,
1016 hud::X_CENTER, hud::Y_MIDDLE,
1017 hud::X_RIGHT, hud::Y_TOP,
1018 1, limit, true);
1019 hud::EAPlayerKD(90, y + 11, hud_scalescoreboard,
1020 hud::X_CENTER, hud::Y_MIDDLE,
1021 hud::X_RIGHT, hud::Y_TOP,
1022 1, limit, true);
1023 } else {
1024 hud::EAPlayerKills(62, y + 11, hud_scalescoreboard,
1025 hud::X_CENTER, hud::Y_MIDDLE,
1026 hud::X_RIGHT, hud::Y_TOP,
1027 1, limit, true);
1028 hud::EAPlayerDeaths(90, y + 11, hud_scalescoreboard,
1029 hud::X_CENTER, hud::Y_MIDDLE,
1030 hud::X_RIGHT, hud::Y_TOP,
1031 1, limit, true);
1032 }
1033 hud::EAPlayerTimes(118, y + 11, hud_scalescoreboard,
1034 hud::X_CENTER, hud::Y_MIDDLE,
1035 hud::X_RIGHT, hud::Y_TOP,
1036 1, limit, true);
1037 hud::EAPlayerPings(146, y + 11, hud_scalescoreboard,
1038 hud::X_CENTER, hud::Y_MIDDLE,
1039 hud::X_RIGHT, hud::Y_TOP,
1040 1, limit, true);
1041 }
1042
1043 // [AM] Draw low-resolution team gametype scores.
drawLowTeamScores(player_t * player,int y,byte extra_rows)1044 void drawLowTeamScores(player_t *player, int y, byte extra_rows) {
1045 int color;
1046 std::string str;
1047
1048 // Column headers
1049 hud::DrawText(-137, y, hud_scalescoreboard,
1050 hud::X_CENTER, hud::Y_MIDDLE,
1051 hud::X_LEFT, hud::Y_TOP,
1052 "Name", CR_GREY, true);
1053 if (sv_gametype == GM_CTF) {
1054 hud::DrawText(34, y, hud_scalescoreboard,
1055 hud::X_CENTER, hud::Y_MIDDLE,
1056 hud::X_RIGHT, hud::Y_TOP,
1057 "PPL", CR_GREY, true);
1058 hud::DrawText(62, y, hud_scalescoreboard,
1059 hud::X_CENTER, hud::Y_MIDDLE,
1060 hud::X_RIGHT, hud::Y_TOP,
1061 "PTS", CR_GREY, true);
1062 hud::DrawText(90, y, hud_scalescoreboard,
1063 hud::X_CENTER, hud::Y_MIDDLE,
1064 hud::X_RIGHT, hud::Y_TOP,
1065 "FRG", CR_GREY, true);
1066 } else {
1067 hud::DrawText(22, y, hud_scalescoreboard,
1068 hud::X_CENTER, hud::Y_MIDDLE,
1069 hud::X_RIGHT, hud::Y_TOP,
1070 "PPL", CR_GREY, true);
1071 hud::DrawText(50, y, hud_scalescoreboard,
1072 hud::X_CENTER, hud::Y_MIDDLE,
1073 hud::X_RIGHT, hud::Y_TOP,
1074 "FRG", CR_GREY, true);
1075 hud::DrawText(90, y, hud_scalescoreboard,
1076 hud::X_CENTER, hud::Y_MIDDLE,
1077 hud::X_RIGHT, hud::Y_TOP,
1078 "K/D", CR_GREY, true);
1079 }
1080 hud::DrawText(118, y, hud_scalescoreboard,
1081 hud::X_CENTER, hud::Y_MIDDLE,
1082 hud::X_RIGHT, hud::Y_TOP,
1083 "MIN", CR_GREY, true);
1084 hud::DrawText(146, y, hud_scalescoreboard,
1085 hud::X_CENTER, hud::Y_MIDDLE,
1086 hud::X_RIGHT, hud::Y_TOP,
1087 "PNG", CR_GREY, true);
1088
1089 // Since we are doing blue-over-red scores, we need to measure how
1090 // far down we want to start for the red team.
1091 byte blue_size = hud::CountTeamPlayers(TEAM_BLUE);
1092 if (blue_size < 4) {
1093 blue_size = 4;
1094 } else if (blue_size > extra_rows + 4) {
1095 blue_size = extra_rows + 4;
1096 }
1097
1098 short ty[2] = {8, short(blue_size * 8 + 22) };
1099
1100 for (byte i = 0;i < 2;i++) {
1101 // Line
1102 if (i == TEAM_BLUE) {
1103 color = CR_BLUE;
1104 } else {
1105 color = CR_RED;
1106 }
1107
1108 for (short xi = -146 + 1;xi < 146;xi += 2) {
1109 hud::DrawTranslatedPatch(xi, y + ty[i], hud_scalescoreboard,
1110 hud::X_CENTER, hud::Y_MIDDLE,
1111 hud::X_CENTER, hud::Y_TOP,
1112 sbline, Ranges + color * 256, true);
1113 hud::DrawTranslatedPatch(xi, y + ty[i] + 11, hud_scalescoreboard,
1114 hud::X_CENTER, hud::Y_MIDDLE,
1115 hud::X_CENTER, hud::Y_TOP,
1116 sbline, Ranges + color * 256, true);
1117 }
1118
1119 // Team Info
1120 str = hud::TeamName(color, i);
1121 hud::DrawText(-137, y + ty[i] + 3, hud_scalescoreboard,
1122 hud::X_CENTER, hud::Y_MIDDLE,
1123 hud::X_LEFT, hud::Y_TOP,
1124 str.c_str(), color, true);
1125 if (sv_gametype == GM_CTF) {
1126 hud::DrawText(34, y + ty[i] + 3, hud_scalescoreboard,
1127 hud::X_CENTER, hud::Y_MIDDLE,
1128 hud::X_RIGHT, hud::Y_TOP,
1129 TeamPlayers(color, i).c_str(), color, true);
1130 hud::DrawText(62, y + ty[i] + 3, hud_scalescoreboard,
1131 hud::X_CENTER, hud::Y_MIDDLE,
1132 hud::X_RIGHT, hud::Y_TOP,
1133 hud::TeamPoints(color, i).c_str(), color, true);
1134 str = hud::TeamFrags(color, i);
1135 hud::DrawText(90 , y + ty[i] + 3, hud_scalescoreboard,
1136 hud::X_CENTER, hud::Y_MIDDLE,
1137 hud::X_RIGHT, hud::Y_TOP,
1138 str.c_str(), color, true);
1139 } else {
1140 hud::DrawText(22, y + ty[i] + 3, hud_scalescoreboard,
1141 hud::X_CENTER, hud::Y_MIDDLE,
1142 hud::X_RIGHT, hud::Y_TOP,
1143 TeamPlayers(color, i).c_str(), color, true);
1144 hud::DrawText(50, y + ty[i] + 3, hud_scalescoreboard,
1145 hud::X_CENTER, hud::Y_MIDDLE,
1146 hud::X_RIGHT, hud::Y_TOP,
1147 hud::TeamPoints(color, i).c_str(), color, true);
1148 str = hud::TeamKD(color, i);
1149 hud::DrawText(90, y + ty[i] + 3, hud_scalescoreboard,
1150 hud::X_CENTER, hud::Y_MIDDLE,
1151 hud::X_RIGHT, hud::Y_TOP,
1152 str.c_str(), color, true);
1153 }
1154 str = hud::TeamPing(color, i);
1155 hud::DrawText(146, y + ty[i] + 3, hud_scalescoreboard,
1156 hud::X_CENTER, hud::Y_MIDDLE,
1157 hud::X_RIGHT, hud::Y_TOP,
1158 str.c_str(), color, true);
1159
1160 // Ingame Players
1161 byte limit = extra_rows + 4;
1162 hud::EATeamPlayerColors(-146, y + ty[i] + 14, 7, 7, hud_scalescoreboard,
1163 hud::X_CENTER, hud::Y_MIDDLE,
1164 hud::X_LEFT, hud::Y_TOP,
1165 1, limit, i);
1166 hud::EATeamPlayerNames(-137, y + ty[i] + 14, hud_scalescoreboard,
1167 hud::X_CENTER, hud::Y_MIDDLE,
1168 hud::X_LEFT, hud::Y_TOP,
1169 1, limit, i, true);
1170 if (sv_gametype == GM_CTF) {
1171 hud::EATeamPlayerPoints(62, y + ty[i] + 14, hud_scalescoreboard,
1172 hud::X_CENTER, hud::Y_MIDDLE,
1173 hud::X_RIGHT, hud::Y_TOP,
1174 1, limit, i, true);
1175 hud::EATeamPlayerFrags(90, y + ty[i] + 14, hud_scalescoreboard,
1176 hud::X_CENTER, hud::Y_MIDDLE,
1177 hud::X_RIGHT, hud::Y_TOP,
1178 1, limit, i, true);
1179 } else {
1180 hud::EATeamPlayerFrags(50, y + ty[i] + 14, hud_scalescoreboard,
1181 hud::X_CENTER, hud::Y_MIDDLE,
1182 hud::X_RIGHT, hud::Y_TOP,
1183 1, limit, i, true);
1184 hud::EATeamPlayerKD(90, y + ty[i] + 14, hud_scalescoreboard,
1185 hud::X_CENTER, hud::Y_MIDDLE,
1186 hud::X_RIGHT, hud::Y_TOP,
1187 1, limit, i, true);
1188 }
1189 hud::EATeamPlayerTimes(118, y + ty[i] + 14, hud_scalescoreboard,
1190 hud::X_CENTER, hud::Y_MIDDLE,
1191 hud::X_RIGHT, hud::Y_TOP,
1192 1, limit, i, true);
1193 hud::EATeamPlayerPings(146, y + ty[i] + 14, hud_scalescoreboard,
1194 hud::X_CENTER, hud::Y_MIDDLE,
1195 hud::X_RIGHT, hud::Y_TOP,
1196 1, limit, i, true);
1197 }
1198 }
1199
1200 // [AM] Draw low-resolution spectators.
drawLowSpectators(player_t * player,int y,byte extra_rows)1201 void drawLowSpectators(player_t *player, int y, byte extra_rows) {
1202 static short tx[2] = {-146, 1};
1203
1204 // Line
1205 for (short xi = -146 + 1;xi < 146;xi += 2) {
1206 hud::DrawTranslatedPatch(xi, y, hud_scalescoreboard,
1207 hud::X_CENTER, hud::Y_MIDDLE,
1208 hud::X_CENTER, hud::Y_TOP,
1209 sbline, Ranges + CR_GREY * 256, true);
1210 }
1211
1212 byte specs = hud::CountSpectators();
1213 byte limit;
1214 byte skip = 0;
1215 for (byte i = 0;i < 2;i++) {
1216 limit = (specs + 1 - i) / 2;
1217 if (extra_rows + 1 < limit) {
1218 limit = extra_rows + 1;
1219 }
1220 hud::EASpectatorNames(tx[i], y + 3, hud_scalescoreboard,
1221 hud::X_CENTER, hud::Y_MIDDLE,
1222 hud::X_LEFT, hud::Y_TOP,
1223 1, skip, limit, true);
1224 hud::EASpectatorPings(tx[i] + 121, y + 3, hud_scalescoreboard,
1225 hud::X_CENTER, hud::Y_MIDDLE,
1226 hud::X_LEFT, hud::Y_TOP,
1227 1, skip, limit, true);
1228 skip += limit;
1229 }
1230 }
1231
1232 // [AM] Draw the low-resolution scoreboard.
LowScoreboard(player_t * player)1233 void LowScoreboard(player_t *player) {
1234 int height, y;
1235 byte extra_spec_rows = 0;
1236 byte extra_player_rows = 0;
1237
1238 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
1239 height = 129;
1240
1241 // Team scoreboard was designed for 4 players on a team. If
1242 // there are more (per team), increase the height.
1243 byte blue = hud::CountTeamPlayers(TEAM_BLUE);
1244 byte red = hud::CountTeamPlayers(TEAM_RED);
1245 if (blue > 4) {
1246 extra_player_rows += blue - 4;
1247 }
1248 if (red > 4) {
1249 extra_player_rows += red - 4;
1250 }
1251 } else {
1252 height = 72;
1253
1254 // Normal scoreboard was designed for 4 players. If there are
1255 // more, increase the height.
1256 byte players = P_NumPlayersInGame();
1257 if (players > 4) {
1258 extra_player_rows += players - 4;
1259 }
1260 }
1261
1262 // If there are more than 2 spectators, increase the height.
1263 byte specs = hud::CountSpectators();
1264 if (specs > 2) {
1265 extra_spec_rows += ((specs + 1) / 2) - 1;
1266 }
1267
1268 height += (extra_player_rows + extra_spec_rows) * 8;
1269
1270 // 180 is our max height.
1271 while (height > 180) {
1272 // We have too many rows. Start removing extra spectator rows
1273 // to make the scoreboard fit.
1274 if (extra_spec_rows == 0) {
1275 break;
1276 }
1277 extra_spec_rows -= 1;
1278 height -= 8;
1279 }
1280
1281 while (height > 180) {
1282 // We still have too many rows. Start removing extra player
1283 // rows to make the scoreboard fit.
1284 if (extra_player_rows == 0) {
1285 break;
1286 }
1287 extra_player_rows -= 1;
1288 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
1289 // Removing one player row sometimes means removing two
1290 // lines on the low resolution team scoreboard.
1291 if (extra_player_rows + 4 < hud::CountTeamPlayers(TEAM_BLUE)) {
1292 height -= 8;
1293 }
1294 if (extra_player_rows + 4 < hud::CountTeamPlayers(TEAM_RED)) {
1295 height -= 8;
1296 }
1297 } else {
1298 height -= 8;
1299 }
1300 }
1301
1302 // Starting Y position, measured from the center up.
1303 y = -(height / 2);
1304 if (y > -64) {
1305 // Scoreboard starts slightly off-center, then grows to be centered.
1306 // -96 makes it even with where the default lowres TDM begins.
1307 // For the record, the lowres DM scoreboard begins at -54.
1308 y = -64;
1309 }
1310
1311 // Dim the background.
1312 hud::Dim(0, y, 300, height, hud_scalescoreboard,
1313 hud::X_CENTER, hud::Y_MIDDLE,
1314 hud::X_CENTER, hud::Y_TOP);
1315
1316 hud::drawLowHeader(player, y + 4);
1317 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
1318 hud::drawLowTeamScores(player, y + 15,
1319 extra_player_rows);
1320 } else {
1321 hud::drawLowScores(player, y + 15,
1322 extra_player_rows);
1323 }
1324 hud::drawLowSpectators(player, y + (height - 14 - (extra_spec_rows * 8)),
1325 extra_spec_rows);
1326 }
1327
1328 }
1329
HU_DrawScores(player_t * player)1330 void HU_DrawScores(player_t *player) {
1331 // We need at least 480 scaled horizontal units for our "high resolution"
1332 // scoreboard to fit.
1333 if (hud::XSize(hud_scalescoreboard) >= 480) {
1334 hud::Scoreboard(player);
1335 } else {
1336 hud::LowScoreboard(player);
1337 }
1338 }
1339
1340 //
1341 // [Toke] OdamexEffect
1342 // Draws the 50% reduction in brightness effect
1343 //
OdamexEffect(int xa,int ya,int xb,int yb)1344 void OdamexEffect (int xa, int ya, int xb, int yb)
1345 {
1346 if (xa < 0 || ya < 0 || xb > screen->width || yb > screen->height)
1347 return;
1348
1349 screen->Dim(xa, ya, xb - xa, yb - ya);
1350 }
1351
1352
1353 //
1354 // Comparison functors for sorting vectors of players
1355 //
1356 struct compare_player_frags
1357 {
operator ()compare_player_frags1358 bool operator()(const player_t* arg1, const player_t* arg2) const
1359 {
1360 return arg2->fragcount < arg1->fragcount;
1361 }
1362 };
1363
1364 struct compare_player_kills
1365 {
operator ()compare_player_kills1366 bool operator()(const player_t* arg1, const player_t* arg2) const
1367 {
1368 return arg2->killcount < arg1->killcount;
1369 }
1370 };
1371
1372 struct compare_player_points
1373 {
operator ()compare_player_points1374 bool operator()(const player_t* arg1, const player_t* arg2) const
1375 {
1376 return arg2->points < arg1->points;
1377 }
1378 };
1379
1380 struct compare_player_names
1381 {
operator ()compare_player_names1382 bool operator()(const player_t* arg1, const player_t* arg2) const
1383 {
1384 return arg1->userinfo.netname < arg2->userinfo.netname;
1385 }
1386 };
1387
1388
HU_CalculateKillDeathRatio(const player_t * player)1389 static float HU_CalculateKillDeathRatio(const player_t* player)
1390 {
1391 if (player->killcount <= 0) // Copied from HU_DMScores1.
1392 return 0.0f;
1393 else if (player->killcount >= 1 && player->deathcount == 0)
1394 return float(player->killcount);
1395 else
1396 return float(player->killcount) / float(player->deathcount);
1397 }
1398
HU_CalculateFragDeathRatio(const player_t * player)1399 static float HU_CalculateFragDeathRatio(const player_t* player)
1400 {
1401 if (player->fragcount <= 0) // Copied from HU_DMScores1.
1402 return 0.0f;
1403 else if (player->fragcount >= 1 && player->deathcount == 0)
1404 return float(player->fragcount);
1405 else
1406 return float(player->fragcount) / float(player->deathcount);
1407 }
1408
1409 //
1410 // HU_ConsoleScores
1411 // Draws scoreboard to console.
1412 //
1413 // [AM] This should be cleaned up and commonized, because it's useful on the
1414 // client and server.
1415 //
HU_ConsoleScores(player_t * player)1416 void HU_ConsoleScores(player_t *player)
1417 {
1418 char str[1024];
1419
1420 typedef std::list<const player_t*> PlayerPtrList;
1421 PlayerPtrList sortedplayers;
1422 PlayerPtrList sortedspectators;
1423
1424 for (Players::const_iterator it = players.begin(); it != players.end(); ++it)
1425 if (!it->spectator && it->playerstate != PST_CONTACT && it->playerstate != PST_DOWNLOAD)
1426 sortedplayers.push_back(&*it);
1427
1428 for (Players::const_iterator it = players.begin(); it != players.end(); ++it)
1429 if (it->spectator && it->playerstate != PST_CONTACT && it->playerstate != PST_DOWNLOAD)
1430 sortedspectators.push_back(&*it);
1431
1432 // One of these at each end prevents the following from
1433 // drawing on the screen itself.
1434 C_ToggleConsole();
1435
1436 if (sv_gametype == GM_CTF)
1437 {
1438 compare_player_points comparison_functor;
1439 sortedplayers.sort(comparison_functor);
1440
1441 Printf_Bold("\n--------------------------------------\n");
1442 Printf_Bold(" CAPTURE THE FLAG\n");
1443
1444 if (sv_scorelimit)
1445 sprintf(str, "Scorelimit: %-6d", sv_scorelimit.asInt());
1446 else
1447 sprintf(str, "Scorelimit: N/A ");
1448
1449 Printf_Bold("%s ", str);
1450
1451 if (sv_timelimit)
1452 sprintf(str, "Timelimit: %-7d", sv_timelimit.asInt());
1453 else
1454 sprintf(str, "Timelimit: N/A");
1455
1456 Printf_Bold("%18s\n", str);
1457
1458 for (int team_num = 0; team_num < NUMTEAMS; team_num++)
1459 {
1460 if (team_num == TEAM_BLUE)
1461 Printf_Bold("\n-----------------------------BLUE TEAM\n");
1462 else if (team_num == TEAM_RED)
1463 Printf_Bold("\n------------------------------RED TEAM\n");
1464 else // shouldn't happen
1465 Printf_Bold("\n--------------------------UNKNOWN TEAM\n");
1466
1467 Printf_Bold("Name Points Caps Frags Time\n");
1468 Printf_Bold("--------------------------------------\n");
1469
1470 for (PlayerPtrList::const_iterator it = sortedplayers.begin(); it != sortedplayers.end(); ++it)
1471 {
1472 const player_t* itplayer = *it;
1473 if (itplayer->userinfo.team == team_num)
1474 {
1475 sprintf(str, "%-15s %-6d N/A %-5d %4d\n",
1476 itplayer->userinfo.netname.c_str(),
1477 itplayer->points,
1478 //itplayer->captures,
1479 itplayer->fragcount,
1480 itplayer->GameTime / 60);
1481
1482 if (itplayer == player)
1483 Printf_Bold(str);
1484 else
1485 Printf(PRINT_HIGH, str);
1486 }
1487 }
1488 }
1489 }
1490
1491 else if (sv_gametype == GM_TEAMDM)
1492 {
1493 compare_player_frags comparison_functor;
1494 sortedplayers.sort(comparison_functor);
1495
1496 Printf_Bold("\n--------------------------------------\n");
1497 Printf_Bold(" TEAM DEATHMATCH\n");
1498
1499 if (sv_fraglimit)
1500 sprintf(str, "Fraglimit: %-7d", sv_fraglimit.asInt());
1501 else
1502 sprintf(str, "Fraglimit: N/A ");
1503
1504 Printf_Bold("%s ", str);
1505
1506 if (sv_timelimit)
1507 sprintf(str, "Timelimit: %-7d", sv_timelimit.asInt());
1508 else
1509 sprintf(str, "Timelimit: N/A");
1510
1511 Printf_Bold("%18s\n", str);
1512
1513 for (int team_num = 0; team_num < NUMTEAMS; team_num++)
1514 {
1515 if (team_num == TEAM_BLUE)
1516 Printf_Bold("\n-----------------------------BLUE TEAM\n");
1517 else if (team_num == TEAM_RED)
1518 Printf_Bold("\n------------------------------RED TEAM\n");
1519 else // shouldn't happen
1520 Printf_Bold("\n--------------------------UNKNOWN TEAM\n");
1521
1522 Printf_Bold("Name Frags Deaths K/D Time\n");
1523 Printf_Bold("--------------------------------------\n");
1524
1525 for (PlayerPtrList::const_iterator it = sortedplayers.begin(); it != sortedplayers.end(); ++it)
1526 {
1527 const player_t* itplayer = *it;
1528 if (itplayer->userinfo.team == team_num)
1529 {
1530 sprintf(str, "%-15s %-5d %-6d %2.1f %4d\n",
1531 itplayer->userinfo.netname.c_str(),
1532 itplayer->fragcount,
1533 itplayer->deathcount,
1534 HU_CalculateFragDeathRatio(itplayer),
1535 itplayer->GameTime / 60);
1536
1537 if (itplayer == player)
1538 Printf_Bold(str);
1539 else
1540 Printf(PRINT_HIGH, str);
1541 }
1542 }
1543 }
1544 }
1545
1546 else if (sv_gametype == GM_DM)
1547 {
1548 compare_player_frags comparison_functor;
1549 sortedplayers.sort(comparison_functor);
1550
1551 Printf_Bold("\n--------------------------------------\n");
1552 Printf_Bold(" DEATHMATCH\n");
1553
1554 if (sv_fraglimit)
1555 sprintf(str, "Fraglimit: %-7d", sv_fraglimit.asInt());
1556 else
1557 sprintf(str, "Fraglimit: N/A ");
1558
1559 Printf_Bold("%s ", str);
1560
1561 if (sv_timelimit)
1562 sprintf(str, "Timelimit: %-7d", sv_timelimit.asInt());
1563 else
1564 sprintf(str, "Timelimit: N/A");
1565
1566 Printf_Bold("%18s\n", str);
1567
1568 Printf_Bold("Name Frags Deaths K/D Time\n");
1569 Printf_Bold("--------------------------------------\n");
1570
1571 for (PlayerPtrList::const_iterator it = sortedplayers.begin(); it != sortedplayers.end(); ++it)
1572 {
1573 const player_t* itplayer = *it;
1574 sprintf(str, "%-15s %-5d %-6d %2.1f %4d\n",
1575 itplayer->userinfo.netname.c_str(),
1576 itplayer->fragcount,
1577 itplayer->deathcount,
1578 HU_CalculateFragDeathRatio(itplayer),
1579 itplayer->GameTime / 60);
1580
1581 if (itplayer == player)
1582 Printf_Bold(str);
1583 else
1584 Printf(PRINT_HIGH, str);
1585 }
1586
1587 }
1588
1589 else if (sv_gametype == GM_COOP)
1590 {
1591 compare_player_kills comparison_functor;
1592 sortedplayers.sort(comparison_functor);
1593
1594 Printf_Bold("\n--------------------------------------\n");
1595 Printf_Bold(" COOPERATIVE\n");
1596 Printf_Bold("Name Kills Deaths K/D Time\n");
1597 Printf_Bold("--------------------------------------\n");
1598
1599 for (PlayerPtrList::const_iterator it = sortedplayers.begin(); it != sortedplayers.end(); ++it)
1600 {
1601 const player_t* itplayer = *it;
1602 sprintf(str,"%-15s %-5d %-6d %2.1f %4d\n",
1603 itplayer->userinfo.netname.c_str(),
1604 itplayer->killcount,
1605 itplayer->deathcount,
1606 HU_CalculateKillDeathRatio(itplayer),
1607 itplayer->GameTime / 60);
1608
1609 if (itplayer == player)
1610 Printf_Bold(str);
1611 else
1612 Printf(PRINT_HIGH, str);
1613 }
1614 }
1615
1616 if (!sortedspectators.empty())
1617 {
1618 compare_player_names comparison_functor;
1619 sortedspectators.sort(comparison_functor);
1620
1621 Printf_Bold("\n----------------------------SPECTATORS\n");
1622
1623 for (PlayerPtrList::const_iterator it = sortedspectators.begin(); it != sortedspectators.end(); ++it)
1624 {
1625 const player_t* itplayer = *it;
1626 sprintf(str, "%-15s\n", itplayer->userinfo.netname.c_str());
1627 if (itplayer == player)
1628 Printf_Bold(str);
1629 else
1630 Printf(PRINT_HIGH, str);
1631 }
1632 }
1633
1634 Printf(PRINT_HIGH, "\n");
1635
1636 C_ToggleConsole();
1637 }
1638
BEGIN_COMMAND(displayscores)1639 BEGIN_COMMAND (displayscores)
1640 {
1641 if (multiplayer)
1642 HU_ConsoleScores(&consoleplayer());
1643 else
1644 Printf(PRINT_HIGH, "This command is only used for multiplayer games.");
1645 }
1646 END_COMMAND (displayscores)
1647
1648 VERSION_CONTROL (hu_stuff_cpp, "$Id: hu_stuff.cpp 4633 2014-03-13 01:25:35Z dr_sean $")
1649
1650
1651