1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id$
5 //
6 // Copyright (C) 2012 by Alex Mayfield.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
17 //
18 // DESCRIPTION:
19 // HUD elements. Holds 'variable' information for the HUD such
20 // as player data, formatted text strings and patches.
21 //
22 //-----------------------------------------------------------------------------
23
24 #include <algorithm>
25 #include <sstream>
26
27 #include "c_cvars.h"
28 #include "cl_demo.h"
29 #include "m_fixed.h" // This should probably go into d_netinf.h
30 #include "d_netinf.h"
31 #include "d_player.h"
32 #include "doomstat.h"
33 #include "g_warmup.h"
34 #include "hu_drawers.h"
35 #include "hu_elements.h"
36 #include "p_ctf.h"
37 #include "v_text.h"
38 #include "v_video.h"
39
40 size_t P_NumPlayersInGame(void);
41 int CL_GetPlayerColor(player_t*);
42
43 extern NetDemo netdemo;
44 extern bool HasBehavior;
45 extern fixed_t FocalLengthX;
46
47 EXTERN_CVAR (sv_fraglimit)
48 EXTERN_CVAR (sv_gametype)
49 EXTERN_CVAR (sv_maxclients)
50 EXTERN_CVAR (sv_maxplayers)
51 EXTERN_CVAR (sv_maxplayersperteam)
52 EXTERN_CVAR (sv_scorelimit)
53 EXTERN_CVAR (sv_timelimit)
54
55 EXTERN_CVAR (hud_targetnames)
56 EXTERN_CVAR (sv_allowtargetnames)
57
58 size_t P_NumPlayersInGame();
59 size_t P_NumPlayersOnTeam(team_t team);
60
61 namespace hud {
62
63 // Player sorting functions
cmpFrags(const player_t * arg1,const player_t * arg2)64 static bool STACK_ARGS cmpFrags(const player_t *arg1, const player_t *arg2) {
65 return arg2->fragcount < arg1->fragcount;
66 }
67
cmpKills(const player_t * arg1,const player_t * arg2)68 static bool STACK_ARGS cmpKills(const player_t *arg1, const player_t *arg2) {
69 return arg2->killcount < arg1->killcount;
70 }
71
cmpPoints(const player_t * arg1,const player_t * arg2)72 static bool STACK_ARGS cmpPoints (const player_t *arg1, const player_t *arg2) {
73 return arg2->points < arg1->points;
74 }
75
76 // Returns true if a player is ingame.
ingamePlayer(player_t * player)77 bool ingamePlayer(player_t* player) {
78 return (player->ingame() && player->spectator == false);
79 }
80
81 // Returns true if a player is ingame and on a specific team
inTeamPlayer(player_t * player,const byte team)82 bool inTeamPlayer(player_t* player, const byte team) {
83 return (player->ingame() && player->userinfo.team == team && player->spectator == false);
84 }
85
86 // Returns true if a player is a spectator
spectatingPlayer(player_t * player)87 bool spectatingPlayer(player_t* player) {
88 return (player->ingame() && player->spectator == true);
89 }
90
91 // Returns a sorted player list. Calculates at most once a gametic.
sortedPlayers(void)92 std::vector<player_t *> sortedPlayers(void) {
93 static int sp_tic = -1;
94 static std::vector<player_t *> sortedplayers(players.size());
95
96 if (sp_tic == gametic) {
97 return sortedplayers;
98 }
99
100 sortedplayers.clear();
101 for (Players::iterator it = players.begin();it != players.end();++it) {
102 sortedplayers.push_back(&*it);
103 }
104
105 if (sv_gametype == GM_COOP) {
106 std::sort(sortedplayers.begin(), sortedplayers.end(), cmpKills);
107 } else {
108 std::sort(sortedplayers.begin(), sortedplayers.end(), cmpFrags);
109 if (sv_gametype == GM_CTF) {
110 std::sort(sortedplayers.begin(), sortedplayers.end(), cmpPoints);
111 }
112 }
113
114 sp_tic = gametic;
115 return sortedplayers;
116 }
117
118 // Return a text color based on a ping.
pingTextColor(unsigned short ping)119 int pingTextColor(unsigned short ping) {
120 if (ping <= 80) {
121 return CR_GREEN;
122 } else if (ping <= 160) {
123 return CR_GOLD;
124 } else if (ping <= 240) {
125 return CR_ORANGE;
126 }
127 return CR_RED;
128 }
129
130 // Return a text color based on a team.
teamTextColor(byte team)131 int teamTextColor(byte team) {
132 int color;
133
134 switch (team) {
135 case TEAM_BLUE:
136 color = CR_BLUE;
137 break;
138 case TEAM_RED:
139 color = CR_RED;
140 break;
141 default:
142 color = CR_GREY;
143 break;
144 }
145
146 return color;
147 }
148
149 //// OLD-STYLE "VARIABLES" ////
150 // Please don't add any more of these.
151
152 // Return a "help" string.
HelpText()153 std::string HelpText() {
154 if (P_NumPlayersInGame() >= sv_maxplayers)
155 {
156 return "Game is full";
157 }
158
159 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF)
160 {
161 size_t min_players = MAXPLAYERS;
162 for (byte i = 0;i < NUMTEAMS;i++)
163 {
164 size_t players = P_NumPlayersOnTeam((team_t)i);
165 if (players < min_players)
166 min_players = players;
167 }
168 if (sv_maxplayersperteam && min_players >= sv_maxplayersperteam) {
169 return "Game is full";
170 }
171 }
172
173 return "Press USE to join";
174 }
175
176 // Return a string that contains the name of the player being spectated,
177 // or a blank string if you are looking out of your own viewpoint.
SpyPlayerName(int & color)178 std::string SpyPlayerName(int& color) {
179 color = CR_GREY;
180 player_t *plyr = &displayplayer();
181
182 if (plyr == &consoleplayer()) {
183 return "";
184 }
185
186 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
187 color = teamTextColor(plyr->userinfo.team);
188 }
189
190 return plyr->userinfo.netname;
191 }
192
193 // Returns a string that contains the current warmup state.
Warmup(int & color)194 std::string Warmup(int& color)
195 {
196 color = CR_GREY;
197 player_t *dp = &displayplayer();
198 player_t *cp = &consoleplayer();
199
200 ::Warmup::status_t wstatus = warmup.get_status();
201
202 if (wstatus == ::Warmup::WARMUP)
203 {
204 if (dp->spectator)
205 return "Warmup: You are spectating";
206 else if (dp->ready)
207 {
208 color = CR_GREEN;
209 if (dp == cp)
210 return "Warmup: You are ready";
211 else
212 return "Warmup: This player is ready";
213 }
214 else
215 {
216 color = CR_RED;
217 if (dp == cp)
218 return "Warmup: You are not ready";
219 else
220 return "Warmup: This player is not ready";
221 }
222 }
223 else if (wstatus == ::Warmup::COUNTDOWN || wstatus == ::Warmup::FORCE_COUNTDOWN)
224 {
225 color = CR_GOLD;
226 return "Match is about to start...";
227 }
228 return "";
229 }
230
231 // Return a string that contains the amount of time left in the map,
232 // or a blank string if there is no timer needed. Can also display
233 // warmup information if it exists.
Timer(int & color)234 std::string Timer(int& color) {
235 color = CR_GREY;
236
237 if (!multiplayer || !(sv_timelimit > 0.0f)) {
238 return "";
239 }
240
241 int timeleft = level.timeleft;
242
243 if (timeleft < 0) {
244 timeleft = 0;
245 }
246
247 int hours = timeleft / (TICRATE * 3600);
248
249 timeleft -= hours * TICRATE * 3600;
250 int minutes = timeleft / (TICRATE * 60);
251
252 timeleft -= minutes * TICRATE * 60;
253 int seconds = timeleft / TICRATE;
254
255 if (minutes <= 0) {
256 color = CR_BRICK;
257 }
258
259 char str[9];
260 if (hours) {
261 sprintf(str, "%02d:%02d:%02d", hours, minutes, seconds);
262 } else {
263 sprintf(str, "%02d:%02d", minutes, seconds);
264 }
265
266 return str;
267 }
268
IntermissionTimer()269 std::string IntermissionTimer() {
270 if (gamestate != GS_INTERMISSION) {
271 return "";
272 }
273
274 int timeleft = level.inttimeleft * TICRATE;
275
276 if (timeleft < 0) {
277 timeleft = 0;
278 }
279
280 int hours = timeleft / (TICRATE * 3600);
281
282 timeleft -= hours * TICRATE * 3600;
283 int minutes = timeleft / (TICRATE * 60);
284
285 timeleft -= minutes * TICRATE * 60;
286 int seconds = timeleft / TICRATE;
287
288 char str[9];
289 if (hours) {
290 sprintf(str, "%02d:%02d:%02d", hours, minutes, seconds);
291 } else {
292 sprintf(str, "%02d:%02d", minutes, seconds);
293 }
294
295 return str;
296 }
297
298 // Return a "spread" of personal frags or team points that the
299 // current player or team is ahead or behind by.
PersonalSpread(int & color)300 std::string PersonalSpread(int& color) {
301 color = CR_BRICK;
302 player_t *plyr = &displayplayer();
303
304 if (sv_gametype == GM_DM) {
305 // Seek the highest number of frags.
306 byte ingame = 0;
307 size_t maxother = 0;
308 short maxfrags = -32768;
309 for (Players::iterator it = players.begin();it != players.end();++it) {
310 if (!validplayer(*it)) {
311 continue;
312 }
313
314 if (!(it->ingame()) || it->spectator) {
315 continue;
316 }
317
318 if (it->fragcount == maxfrags) {
319 maxother++;
320 }
321
322 if (it->fragcount > maxfrags) {
323 maxfrags = it->fragcount;
324 maxother = 0;
325 }
326
327 ingame += 1;
328 }
329
330 // A spread needs two players to make sense.
331 if (ingame <= 1) {
332 return "";
333 }
334
335 // Return the correct spread.
336 if (maxfrags == plyr->fragcount && maxother > 0) {
337 // We have the maximum number of frags but we share the
338 // throne with someone else. But at least we can take
339 // a little shortcut here.
340 color = CR_GREEN;
341 return "+0";
342 }
343
344 std::ostringstream buffer;
345 if (maxfrags == plyr->fragcount) {
346 // We have the maximum number of frags. Calculate how
347 // far above the other players we are.
348 short nextfrags = -32768;
349 for (Players::iterator it = players.begin();it != players.end();++it) {
350 if (!validplayer(*it)) {
351 continue;
352 }
353
354 if (!(it->ingame()) || it->spectator) {
355 continue;
356 }
357
358 if (it->id == plyr->id) {
359 continue;
360 }
361
362 if (it->fragcount > nextfrags) {
363 nextfrags = it->fragcount;
364 }
365 }
366
367 color = CR_GREEN;
368 buffer << "+" << maxfrags - nextfrags;
369 return buffer.str();
370 }
371
372 // We are behind the leader.
373 buffer << (plyr->fragcount - maxfrags);
374 return buffer.str();
375 } else if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
376 // Team spreads are significantly easier. Just compare two numbers.
377 // FIXME: Not if we have more than two teams!
378 std::ostringstream buffer;
379 switch (plyr->userinfo.team) {
380 case TEAM_BLUE:
381 if (TEAMpoints[TEAM_BLUE] >= TEAMpoints[TEAM_RED]) {
382 color = CR_GREEN;
383 buffer << "+";
384 }
385 buffer << (TEAMpoints[TEAM_BLUE] - TEAMpoints[TEAM_RED]);
386 break;
387 case TEAM_RED:
388 if (TEAMpoints[TEAM_RED] >= TEAMpoints[TEAM_BLUE]) {
389 color = CR_GREEN;
390 buffer << "+";
391 }
392 buffer << (TEAMpoints[TEAM_RED] - TEAMpoints[TEAM_BLUE]);
393 break;
394 default:
395 // No valid team? Something is wrong...
396 return "";
397 }
398 return buffer.str();
399 }
400
401 // We're not in an appropriate gamemode.
402 return "";
403 }
404
405 // Return a string that contains the current team score or personal
406 // frags of the individual player. Optionally returns the "limit"
407 // as well.
PersonalScore(int & color)408 std::string PersonalScore(int& color) {
409 color = CR_GREY;
410 std::ostringstream buffer;
411 player_t *plyr = &displayplayer();
412
413 if (sv_gametype == GM_DM) {
414 buffer << plyr->fragcount;
415 if (sv_fraglimit.asInt() > 0) {
416 buffer << "/" << sv_fraglimit.asInt();
417 }
418 } else if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
419 color = teamTextColor(plyr->userinfo.team);
420 buffer << TEAMpoints[plyr->userinfo.team];
421
422 if (sv_gametype == GM_TEAMDM) {
423 if (sv_fraglimit.asInt() > 0) {
424 buffer << "/" << sv_fraglimit.asInt();
425 }
426 } else {
427 if (sv_scorelimit.asInt() > 0) {
428 buffer << "/" << sv_scorelimit.asInt();
429 }
430 }
431 }
432
433 return buffer.str();
434 }
435
436 // Return the amount of time elapsed in a netdemo.
NetdemoElapsed()437 std::string NetdemoElapsed() {
438 if (!(netdemo.isPlaying() || netdemo.isPaused())) {
439 return "";
440 }
441
442 int timeelapsed = netdemo.calculateTimeElapsed();
443 int hours = timeelapsed / 3600;
444
445 timeelapsed -= hours * 3600;
446 int minutes = timeelapsed / 60;
447
448 timeelapsed -= minutes * 60;
449 int seconds = timeelapsed;
450
451 char str[9];
452 if (hours) {
453 sprintf(str, "%02d:%02d:%02d", hours, minutes, seconds);
454 } else {
455 sprintf(str, "%02d:%02d", minutes, seconds);
456 }
457
458 return str;
459 }
460
461 // Return current map number/total maps in demo
NetdemoMaps()462 std::string NetdemoMaps() {
463 std::vector<int> maptimes = netdemo.getMapChangeTimes();
464
465 // Single map demo
466 if (maptimes.empty()) {
467 return "";
468 }
469
470 std::ostringstream buffer;
471 size_t current_map = 1;
472
473 // See if we're in a tic past one of the map change times.
474 for (size_t i = 0;i < maptimes.size();i++) {
475 if (maptimes[i] <= netdemo.calculateTimeElapsed()) {
476 current_map = (i + 1);
477 }
478 }
479
480 buffer << current_map << "/" << maptimes.size();
481 return buffer.str();
482 }
483
484 // Returns clients connected / max clients.
ClientsSplit()485 std::string ClientsSplit() {
486 std::ostringstream buffer;
487
488 buffer << players.size() << "/" << sv_maxclients;
489 return buffer.str();
490 }
491
492 // Returns players connected / max players.
PlayersSplit()493 std::string PlayersSplit() {
494 std::ostringstream buffer;
495
496 buffer << P_NumPlayersInGame() << "/" << sv_maxplayers;
497 return buffer.str();
498 }
499
500 // Returns the number of players on a team
CountTeamPlayers(byte team)501 byte CountTeamPlayers(byte team) {
502 byte count = 0;
503 for (size_t i = 0;i < sortedPlayers().size();i++) {
504 player_t* player = sortedPlayers()[i];
505 if (inTeamPlayer(player, team)) {
506 count++;
507 }
508 }
509 return count;
510 }
511
512 // Returns the number of spectators on a team
CountSpectators()513 byte CountSpectators() {
514 byte count = 0;
515 for (size_t i = 0;i < sortedPlayers().size();i++) {
516 player_t* player = sortedPlayers()[i];
517 if (spectatingPlayer(player)) {
518 count++;
519 }
520 }
521 return count;
522 }
523
TeamPlayers(int & color,byte team)524 std::string TeamPlayers(int& color, byte team) {
525 color = teamTextColor(team);
526
527 std::ostringstream buffer;
528 buffer << (short)CountTeamPlayers(team);
529 return buffer.str();
530 }
531
TeamName(int & color,byte team)532 std::string TeamName(int& color, byte team) {
533 color = teamTextColor(team);
534
535 switch (team) {
536 case TEAM_BLUE:
537 return "BLUE TEAM";
538 case TEAM_RED:
539 return "RED TEAM";
540 default:
541 return "NO TEAM";
542 }
543 }
544
TeamFrags(int & color,byte team)545 std::string TeamFrags(int& color, byte team) {
546 if (CountTeamPlayers(team) == 0) {
547 color = CR_GREY;
548 return "---";
549 }
550
551 color = teamTextColor(team);
552
553 int fragcount = 0;
554 for (size_t i = 0;i < sortedPlayers().size();i++) {
555 player_t* player = sortedPlayers()[i];
556 if (inTeamPlayer(player, team)) {
557 fragcount += player->fragcount;
558 }
559 }
560
561 std::ostringstream buffer;
562 buffer << fragcount;
563 return buffer.str();
564 }
565
TeamPoints(int & color,byte team)566 std::string TeamPoints(int& color, byte team) {
567 if (team >= NUMTEAMS) {
568 color = CR_GREY;
569 return "---";
570 }
571
572 color = teamTextColor(team);
573
574 std::ostringstream buffer;
575 buffer << TEAMpoints[team];
576 return buffer.str();
577 }
578
TeamKD(int & color,byte team)579 std::string TeamKD(int& color, byte team) {
580 if (CountTeamPlayers(team) == 0) {
581 color = CR_GREY;
582 return "---";
583 }
584
585 color = teamTextColor(team);
586
587 int killcount = 0;
588 unsigned int deathcount = 0;
589 for (size_t i = 0;i < sortedPlayers().size();i++) {
590 player_t* player = sortedPlayers()[i];
591 if (inTeamPlayer(player, team)) {
592 killcount += player->fragcount;
593 deathcount += player->deathcount;
594 }
595 }
596
597 std::ostringstream buffer;
598 buffer.precision(2);
599 buffer << std::fixed;
600 float kd;
601 if (deathcount == 0)
602 kd = (float)killcount / 1.0f;
603 else {
604 kd = (float)killcount / (float)deathcount;
605 }
606 buffer << kd;
607 return buffer.str();
608 }
609
TeamPing(int & color,byte team)610 std::string TeamPing(int& color, byte team) {
611 if (CountTeamPlayers(team) == 0) {
612 color = CR_GREY;
613 return "---";
614 }
615
616 unsigned int ping = 0;
617 for (size_t i = 0;i < sortedPlayers().size();i++) {
618 player_t* player = sortedPlayers()[i];
619 if (inTeamPlayer(player, team)) {
620 ping += player->ping;
621 }
622 }
623
624 ping = ping / CountTeamPlayers(team);
625 color = pingTextColor(ping);
626
627 std::ostringstream buffer;
628 buffer << ping;
629 return buffer.str();
630 }
631
632 //// NEW-STYLE "ELEMENTS" ////
633
634 // "Elemants" are actually all-in-one drawable packages. There are two types
635 // of elements: plain old elements and ElementArrays, which draw the same thing
636 // multiple times. I'm still trying to figure out the interface for these though.
637
638 // Draw a list of player colors in the game. Lines up with player names.
EAPlayerColors(int x,int y,const unsigned short w,const unsigned short h,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit)639 void EAPlayerColors(int x, int y,
640 const unsigned short w, const unsigned short h,
641 const float scale,
642 const x_align_t x_align, const y_align_t y_align,
643 const x_align_t x_origin, const y_align_t y_origin,
644 const short padding, const short limit) {
645 byte drawn = 0;
646 for (size_t i = 0;i < sortedPlayers().size();i++) {
647 // Make sure we're not overrunning our limit.
648 if (limit != 0 && drawn >= limit) {
649 break;
650 }
651
652 player_t* player = sortedPlayers()[i];
653 if (ingamePlayer(player)) {
654 int playercolor = CL_GetPlayerColor(player);
655 int color = BestColor(GetDefaultPalette()->basecolors,
656 RPART(playercolor),
657 GPART(playercolor),
658 BPART(playercolor),
659 GetDefaultPalette()->numcolors);
660
661 if (!screen->is8bit()) color = playercolor;
662
663 hud::Clear(x, y, w, h, scale, x_align, y_align, x_origin, y_origin, color);
664 y += h + padding;
665 drawn += 1;
666 }
667 }
668 }
669
670 // Draw a list of player colors on a team. Lines up with player names.
EATeamPlayerColors(int x,int y,const unsigned short w,const unsigned short h,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const byte team)671 void EATeamPlayerColors(int x, int y,
672 const unsigned short w, const unsigned short h,
673 const float scale,
674 const x_align_t x_align, const y_align_t y_align,
675 const x_align_t x_origin, const y_align_t y_origin,
676 const short padding, const short limit,
677 const byte team) {
678 byte drawn = 0;
679 for (size_t i = 0;i < sortedPlayers().size();i++) {
680 // Make sure we're not overrunning our limit.
681 if (limit != 0 && drawn >= limit) {
682 break;
683 }
684
685 player_t* player = sortedPlayers()[i];
686 if (inTeamPlayer(player, team)) {
687 int playercolor = CL_GetPlayerColor(player);
688 int color = BestColor(GetDefaultPalette()->basecolors,
689 RPART(playercolor),
690 GPART(playercolor),
691 BPART(playercolor),
692 GetDefaultPalette()->numcolors);
693
694 if (!screen->is8bit()) color = playercolor;
695
696 hud::Clear(x, y, w, h, scale, x_align, y_align, x_origin, y_origin, color);
697 y += h + padding;
698 drawn += 1;
699 }
700 }
701 }
702
703 // Draw a list of players in the game.
EAPlayerNames(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const bool force_opaque)704 void EAPlayerNames(int x, int y, const float scale,
705 const x_align_t x_align, const y_align_t y_align,
706 const x_align_t x_origin, const y_align_t y_origin,
707 const short padding, const short limit,
708 const bool force_opaque) {
709 byte drawn = 0;
710 for (size_t i = 0;i < sortedPlayers().size();i++) {
711 // Make sure we're not overrunning our limit.
712 if (limit != 0 && drawn >= limit) {
713 break;
714 }
715
716 player_t* player = sortedPlayers()[i];
717 if (ingamePlayer(player)) {
718 int color = CR_GREY;
719 if (player->id == displayplayer().id) {
720 color = CR_GOLD;
721 }
722 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
723 player->userinfo.netname.c_str(), color, force_opaque);
724 y += 7 + padding;
725 drawn += 1;
726 }
727 }
728 }
729
730 // Draw a list of players on a team.
EATeamPlayerNames(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const byte team,const bool force_opaque)731 void EATeamPlayerNames(int x, int y, const float scale,
732 const x_align_t x_align, const y_align_t y_align,
733 const x_align_t x_origin, const y_align_t y_origin,
734 const short padding, const short limit,
735 const byte team, const bool force_opaque) {
736 byte drawn = 0;
737 for (size_t i = 0;i < sortedPlayers().size();i++) {
738 // Make sure we're not overrunning our limit.
739 if (limit != 0 && drawn >= limit) {
740 break;
741 }
742
743 player_t* player = sortedPlayers()[i];
744 if (inTeamPlayer(player, team)) {
745 int color = CR_GREY;
746 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
747 if (player->userinfo.team == TEAM_BLUE) {
748 if (player->flags[it_redflag]) {
749 color = CR_RED;
750 } else if (player->flags[it_blueflag]) {
751 color = CR_BLUE;
752 } else if (player->ready) {
753 color = CR_GREEN;
754 } else if (player->id == displayplayer().id) {
755 color = CR_GOLD;
756 }
757 } else if (player->userinfo.team == TEAM_RED) {
758 if (player->flags[it_blueflag]) {
759 color = CR_BLUE;
760 } else if (player->flags[it_redflag]) {
761 color = CR_RED;
762 } else if (player->ready) {
763 color = CR_GREEN;
764 } else if (player->id == displayplayer().id) {
765 color = CR_GOLD;
766 }
767 }
768 } else {
769 if (player->id == displayplayer().id) {
770 color = CR_GOLD;
771 }
772 }
773 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
774 player->userinfo.netname.c_str(), color, force_opaque);
775 y += 7 + padding;
776 drawn += 1;
777 }
778 }
779 }
780
781 // Draw a list of spectators.
EASpectatorNames(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,short skip,const short limit,const bool force_opaque)782 void EASpectatorNames(int x, int y, const float scale,
783 const x_align_t x_align, const y_align_t y_align,
784 const x_align_t x_origin, const y_align_t y_origin,
785 const short padding, short skip, const short limit,
786 const bool force_opaque) {
787 byte drawn = 0;
788 for (size_t i = 0;i < sortedPlayers().size();i++) {
789 // Make sure we're not overrunning our limit.
790 if (limit != 0 && drawn >= limit) {
791 break;
792 }
793
794 player_t* player = sortedPlayers()[i];
795 if (spectatingPlayer(player)) {
796 if (skip <= 0) {
797 int color = CR_GREY;
798 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
799 if (player->ready) {
800 color = CR_GREEN;
801 } else if (player->id == displayplayer().id) {
802 color = CR_GOLD;
803 }
804 } else {
805 if (player->id == displayplayer().id) {
806 color = CR_GOLD;
807 }
808 }
809 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
810 player->userinfo.netname.c_str(), color, force_opaque);
811 y += 7 + padding;
812 drawn += 1;
813 } else {
814 skip -= 1;
815 }
816 }
817 }
818 }
819
820 // Draw a list of frags in the game. Lines up with player names.
EAPlayerFrags(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const bool force_opaque)821 void EAPlayerFrags(int x, int y, const float scale,
822 const x_align_t x_align, const y_align_t y_align,
823 const x_align_t x_origin, const y_align_t y_origin,
824 const short padding, const short limit,
825 const bool force_opaque) {
826 byte drawn = 0;
827 for (size_t i = 0;i < sortedPlayers().size();i++) {
828 // Make sure we're not overrunning our limit.
829 if (limit != 0 && drawn >= limit) {
830 break;
831 }
832
833 player_t* player = sortedPlayers()[i];
834 if (ingamePlayer(player)) {
835 std::ostringstream buffer;
836 buffer << player->fragcount;
837
838 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
839 buffer.str().c_str(), CR_GREY, force_opaque);
840 y += 7 + padding;
841 drawn += 1;
842 }
843 }
844 }
845
846 // Draw a list of frags on a team. Lines up with player names.
EATeamPlayerFrags(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const byte team,const bool force_opaque)847 void EATeamPlayerFrags(int x, int y, const float scale,
848 const x_align_t x_align, const y_align_t y_align,
849 const x_align_t x_origin, const y_align_t y_origin,
850 const short padding, const short limit,
851 const byte team, const bool force_opaque) {
852 byte drawn = 0;
853 for (size_t i = 0;i < sortedPlayers().size();i++) {
854 // Make sure we're not overrunning our limit.
855 if (limit != 0 && drawn >= limit) {
856 break;
857 }
858
859 player_t* player = sortedPlayers()[i];
860 if (inTeamPlayer(player, team)) {
861 std::ostringstream buffer;
862 buffer << player->fragcount;
863
864 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
865 buffer.str().c_str(), CR_GREY, force_opaque);
866 y += 7 + padding;
867 drawn += 1;
868 }
869 }
870 }
871
872 // Draw a list of kills in the game. Lines up with player names.
EAPlayerKills(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const bool force_opaque)873 void EAPlayerKills(int x, int y, const float scale,
874 const x_align_t x_align, const y_align_t y_align,
875 const x_align_t x_origin, const y_align_t y_origin,
876 const short padding, const short limit,
877 const bool force_opaque) {
878 byte drawn = 0;
879 for (size_t i = 0;i < sortedPlayers().size();i++) {
880 // Make sure we're not overrunning our limit.
881 if (limit != 0 && drawn >= limit) {
882 break;
883 }
884
885 player_t* player = sortedPlayers()[i];
886 if (ingamePlayer(player)) {
887 std::ostringstream buffer;
888 buffer << player->killcount;
889
890 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
891 buffer.str().c_str(), CR_GREY, force_opaque);
892 y += 7 + padding;
893 drawn += 1;
894 }
895 }
896 }
897
898 // Draw a list of deaths in the game. Lines up with player names.
EAPlayerDeaths(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const bool force_opaque)899 void EAPlayerDeaths(int x, int y, const float scale,
900 const x_align_t x_align, const y_align_t y_align,
901 const x_align_t x_origin, const y_align_t y_origin,
902 const short padding, const short limit,
903 const bool force_opaque) {
904 byte drawn = 0;
905 for (size_t i = 0;i < sortedPlayers().size();i++) {
906 // Make sure we're not overrunning our limit.
907 if (limit != 0 && drawn >= limit) {
908 break;
909 }
910
911 player_t* player = sortedPlayers()[i];
912 if (ingamePlayer(player)) {
913 std::ostringstream buffer;
914 buffer << player->deathcount;
915
916 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
917 buffer.str().c_str(), CR_GREY, force_opaque);
918 y += 7 + padding;
919 drawn += 1;
920 }
921 }
922 }
923
924 // Draw a list of points on a team. Lines up with player names.
EATeamPlayerPoints(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const byte team,const bool force_opaque)925 void EATeamPlayerPoints(int x, int y, const float scale,
926 const x_align_t x_align, const y_align_t y_align,
927 const x_align_t x_origin, const y_align_t y_origin,
928 const short padding, const short limit,
929 const byte team, const bool force_opaque) {
930 byte drawn = 0;
931 for (size_t i = 0;i < sortedPlayers().size();i++) {
932 // Make sure we're not overrunning our limit.
933 if (limit != 0 && drawn >= limit) {
934 break;
935 }
936
937 player_t* player = sortedPlayers()[i];
938 if (inTeamPlayer(player, team)) {
939 std::ostringstream buffer;
940 buffer << player->points;
941
942 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
943 buffer.str().c_str(), CR_GREY, force_opaque);
944 y += 7 + padding;
945 drawn += 1;
946 }
947 }
948 }
949
950 // Draw a list of K/D ratios in the game. Lines up with player names.
951 // FIXME: Nothing in the player struct holds player kills, so we use
952 // frags instead.
EAPlayerKD(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const bool force_opaque)953 void EAPlayerKD(int x, int y, const float scale,
954 const x_align_t x_align, const y_align_t y_align,
955 const x_align_t x_origin, const y_align_t y_origin,
956 const short padding, const short limit,
957 const bool force_opaque) {
958 byte drawn = 0;
959 for (size_t i = 0;i < sortedPlayers().size();i++) {
960 // Make sure we're not overrunning our limit.
961 if (limit != 0 && drawn >= limit) {
962 break;
963 }
964
965 player_t* player = sortedPlayers()[i];
966 if (ingamePlayer(player)) {
967 std::ostringstream buffer;
968 buffer.precision(2);
969 buffer << std::fixed;
970 float kd;
971 if (player->deathcount == 0)
972 kd = (float)player->fragcount / 1.0f;
973 else {
974 kd = (float)player->fragcount / (float)player->deathcount;
975 }
976 buffer << kd;
977
978 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
979 buffer.str().c_str(), CR_GREY, force_opaque);
980 y += 7 + padding;
981 drawn += 1;
982 }
983 }
984 }
985
986 // Draw a list of K/D ratios on a team. Lines up with player names.
987 // FIXME: Nothing in the player struct holds player kills, so we use
988 // frags instead.
EATeamPlayerKD(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const byte team,const bool force_opaque)989 void EATeamPlayerKD(int x, int y, const float scale,
990 const x_align_t x_align, const y_align_t y_align,
991 const x_align_t x_origin, const y_align_t y_origin,
992 const short padding, const short limit,
993 const byte team, const bool force_opaque) {
994 byte drawn = 0;
995 for (size_t i = 0;i < sortedPlayers().size();i++) {
996 // Make sure we're not overrunning our limit.
997 if (limit != 0 && drawn >= limit) {
998 break;
999 }
1000
1001 player_t* player = sortedPlayers()[i];
1002 if (inTeamPlayer(player, team)) {
1003 std::ostringstream buffer;
1004 buffer.precision(2);
1005 buffer << std::fixed;
1006 float kd;
1007 if (player->deathcount == 0)
1008 kd = (float)player->fragcount / 1.0f;
1009 else {
1010 kd = (float)player->fragcount / (float)player->deathcount;
1011 }
1012 buffer << kd;
1013
1014 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
1015 buffer.str().c_str(), CR_GREY, force_opaque);
1016 y += 7 + padding;
1017 drawn += 1;
1018 }
1019 }
1020 }
1021
1022 // Draw a list of player times in the game (in minutes). Lines up with
1023 // player names.
EAPlayerTimes(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const bool force_opaque)1024 void EAPlayerTimes(int x, int y, const float scale,
1025 const x_align_t x_align, const y_align_t y_align,
1026 const x_align_t x_origin, const y_align_t y_origin,
1027 const short padding, const short limit,
1028 const bool force_opaque) {
1029 byte drawn = 0;
1030 for (size_t i = 0;i < sortedPlayers().size();i++) {
1031 // Make sure we're not overrunning our limit.
1032 if (limit != 0 && drawn >= limit) {
1033 break;
1034 }
1035
1036 player_t* player = sortedPlayers()[i];
1037 if (ingamePlayer(player)) {
1038 std::ostringstream buffer;
1039 buffer << player->GameTime / 60;
1040
1041 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
1042 buffer.str().c_str(), CR_GREY, force_opaque);
1043 y += 7 + padding;
1044 drawn += 1;
1045 }
1046 }
1047 }
1048
1049 // Draw a list of player times on a team (in minutes). Lines up with
1050 // player names.
EATeamPlayerTimes(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const byte team,const bool force_opaque)1051 void EATeamPlayerTimes(int x, int y, const float scale,
1052 const x_align_t x_align, const y_align_t y_align,
1053 const x_align_t x_origin, const y_align_t y_origin,
1054 const short padding, const short limit,
1055 const byte team, const bool force_opaque) {
1056 byte drawn = 0;
1057 for (size_t i = 0;i < sortedPlayers().size();i++) {
1058 // Make sure we're not overrunning our limit.
1059 if (limit != 0 && drawn >= limit) {
1060 break;
1061 }
1062
1063 player_t* player = sortedPlayers()[i];
1064 if (inTeamPlayer(player, team)) {
1065 std::ostringstream buffer;
1066 buffer << player->GameTime / 60;
1067
1068 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
1069 buffer.str().c_str(), CR_GREY, force_opaque);
1070 y += 7 + padding;
1071 drawn += 1;
1072 }
1073 }
1074 }
1075
1076 // Draw a list of pings in the game. Lines up with player names.
EAPlayerPings(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const bool force_opaque)1077 void EAPlayerPings(int x, int y, const float scale,
1078 const x_align_t x_align, const y_align_t y_align,
1079 const x_align_t x_origin, const y_align_t y_origin,
1080 const short padding, const short limit,
1081 const bool force_opaque) {
1082 byte drawn = 0;
1083 for (size_t i = 0;i < sortedPlayers().size();i++) {
1084 // Make sure we're not overrunning our limit.
1085 if (limit != 0 && drawn >= limit) {
1086 break;
1087 }
1088
1089 player_t* player = sortedPlayers()[i];
1090 if (ingamePlayer(player)) {
1091 std::ostringstream buffer;
1092 buffer << player->ping;
1093
1094 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
1095 buffer.str().c_str(), pingTextColor(player->ping),
1096 force_opaque);
1097 y += 7 + padding;
1098 drawn += 1;
1099 }
1100 }
1101 }
1102
1103 // Draw a list of pings on a team. Lines up with player names.
EATeamPlayerPings(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const byte team,const bool force_opaque)1104 void EATeamPlayerPings(int x, int y, const float scale,
1105 const x_align_t x_align, const y_align_t y_align,
1106 const x_align_t x_origin, const y_align_t y_origin,
1107 const short padding, const short limit,
1108 const byte team, const bool force_opaque) {
1109 byte drawn = 0;
1110 for (size_t i = 0;i < sortedPlayers().size();i++) {
1111 // Make sure we're not overrunning our limit.
1112 if (limit != 0 && drawn >= limit) {
1113 break;
1114 }
1115
1116 player_t* player = sortedPlayers()[i];
1117 if (inTeamPlayer(player, team)) {
1118 std::ostringstream buffer;
1119 buffer << player->ping;
1120
1121 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
1122 buffer.str().c_str(), pingTextColor(player->ping),
1123 force_opaque);
1124 y += 7 + padding;
1125 drawn += 1;
1126 }
1127 }
1128 }
1129
1130 // Draw a list of spectator pings. Lines up with spectators given the same
1131 // skip and limit.
EASpectatorPings(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,short skip,const short limit,const bool force_opaque)1132 void EASpectatorPings(int x, int y, const float scale,
1133 const x_align_t x_align, const y_align_t y_align,
1134 const x_align_t x_origin, const y_align_t y_origin,
1135 const short padding, short skip, const short limit,
1136 const bool force_opaque) {
1137 byte drawn = 0;
1138 for (size_t i = 0;i < sortedPlayers().size();i++) {
1139 // Make sure we're not overrunning our limit.
1140 if (limit != 0 && drawn >= limit) {
1141 break;
1142 }
1143
1144 player_t* player = sortedPlayers()[i];
1145 if (spectatingPlayer(player)) {
1146 if (skip <= 0) {
1147 std::ostringstream buffer;
1148 buffer << player->ping;
1149
1150 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
1151 buffer.str().c_str(), pingTextColor(player->ping),
1152 force_opaque);
1153 y += 7 + padding;
1154 drawn += 1;
1155 } else {
1156 skip -= 1;
1157 }
1158 }
1159 }
1160 }
1161
1162 // Target structure.
1163 typedef struct {
1164 player_t* PlayPtr;
1165 int Distance;
1166 int Color;
1167 } TargetInfo_t;
1168
1169 // Draw a list of targets. Thanks to GhostlyDeath for the original function.
EATargets(int x,int y,const float scale,const x_align_t x_align,const y_align_t y_align,const x_align_t x_origin,const y_align_t y_origin,const short padding,const short limit,const bool force_opaque)1170 void EATargets(int x, int y, const float scale,
1171 const x_align_t x_align, const y_align_t y_align,
1172 const x_align_t x_origin, const y_align_t y_origin,
1173 const short padding, const short limit,
1174 const bool force_opaque) {
1175 if (gamestate != GS_LEVEL) {
1176 // We're not in the game.
1177 return;
1178 }
1179
1180 if(!displayplayer().mo) {
1181 // For some reason, the displayed player doesn't have a mobj, so
1182 // if we let this function run we're going to crash.
1183 return;
1184 }
1185
1186 if (!hud_targetnames) {
1187 // We don't want to see target names.
1188 return;
1189 }
1190
1191 if (!consoleplayer().spectator && !sv_allowtargetnames) {
1192 // The server doesn't want us to use target names.
1193 return;
1194 }
1195
1196 std::vector<TargetInfo_t> Targets;
1197
1198 // What players should be drawn?
1199 for (Players::iterator it = players.begin();it != players.end();++it)
1200 {
1201 if (it->spectator || !(it->mo) || it->mo->health <= 0)
1202 continue;
1203
1204 // We don't care about the player whose eyes we are looking through.
1205 if (&*it == &(displayplayer()))
1206 continue;
1207
1208 if (!P_ActorInFOV(displayplayer().mo, it->mo, 45.0f, 512 * FRACUNIT))
1209 continue;
1210
1211 // Pick a decent color for the player name.
1212 int color;
1213 if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) {
1214 // In teamgames, we want to use team colors for targets.
1215 color = teamTextColor(it->userinfo.team);
1216 } else {
1217 color = CR_GREY;
1218 }
1219
1220 // Ok, make the temporary player info then add it
1221 TargetInfo_t temp = {
1222 &*it,
1223 P_AproxDistance2(displayplayer().mo, it->mo) >> FRACBITS,
1224 color
1225 };
1226 Targets.push_back(temp);
1227 }
1228
1229 // GhostlyDeath -- Now Sort (hopefully I got my selection sort working!)
1230 for (size_t i = 0; i < Targets.size(); i++) {
1231 for (size_t j = i + 1; j < Targets.size(); j++) {
1232 if (Targets[j].Distance < Targets[i].Distance) {
1233 player_t* PlayPtr = Targets[i].PlayPtr;
1234 int Distance = Targets[i].Distance;
1235 int Color = Targets[i].Color;
1236 Targets[i].PlayPtr = Targets[j].PlayPtr;
1237 Targets[i].Distance = Targets[j].Distance;
1238 Targets[i].Color = Targets[j].Color;
1239 Targets[j].PlayPtr = PlayPtr;
1240 Targets[j].Distance = Distance;
1241 Targets[j].Color = Color;
1242 }
1243 }
1244 }
1245
1246 // [AM] New ElementArray drawing function
1247 byte drawn = 0;
1248 for (size_t i = 0;i < Targets.size();i++) {
1249 // Make sure we're not overrunning our limit.
1250 if (limit != 0 && drawn >= limit) {
1251 break;
1252 }
1253
1254 if (Targets[i].PlayPtr == &(consoleplayer())) {
1255 // You're looking at yourself.
1256 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
1257 "You", Targets[i].Color);
1258 } else {
1259 hud::DrawText(x, y, scale, x_align, y_align, x_origin, y_origin,
1260 Targets[i].PlayPtr->userinfo.netname.c_str(),
1261 Targets[i].Color);
1262 }
1263
1264 y += 7 + padding;
1265 drawn += 1;
1266 }
1267 }
1268
1269 }
1270