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