Код:
//
DECLARE_COMMAND(m_Scoreboard, ShowScores);
DECLARE_COMMAND(m_Scoreboard, HideScores);
DECLARE_MESSAGE(m_Scoreboard, ScoreInfoHUD);
DECLARE_MESSAGE(m_Scoreboard, TeamInfoHUD);
DECLARE_MESSAGE(m_Scoreboard, TeamScoreHUD);
//
int CHudScoreboard::Init(void)
{
gHUD.AddHudElem(this);
// Hook messages & commands here
HOOK_COMMAND("+showscores", ShowScores);
HOOK_COMMAND("-showscores", HideScores);
HOOK_MESSAGE(ScoreInfoHUD);
HOOK_MESSAGE(TeamScoreHUD);
HOOK_MESSAGE(TeamInfoHUD);
InitHUDData();
cl_showpacketloss = CVAR_CREATE("cl_showpacketloss", "1", FCVAR_ARCHIVE);
return 1;
}
int CHudScoreboard::VidInit(void)
{
// Load sprites here
return 1;
}
void CHudScoreboard::InitHUDData(void)
{
memset(g_PlayerExtraInfo, 0, sizeof g_PlayerExtraInfo);
m_iLastKilledBy = 0;
m_fLastKillTime = 0;
m_iPlayerNum = 0;
m_iNumTeams = 0;
memset(g_TeamInfo, 0, sizeof g_TeamInfo);
m_iFlags &= ~HUD_ACTIVE; // starts out inactive
m_iFlags |= HUD_INTERMISSION; // is always drawn during an intermission
}
// X positions
// relative to the side of the scoreboard
#define NAME_RANGE_MIN 20
#define NAME_RANGE_MAX 145
#define KILLS_RANGE_MIN 130
#define KILLS_RANGE_MAX 170
#define DIVIDER_POS 180
#define DEATHS_RANGE_MIN 185
#define DEATHS_RANGE_MAX 210
#define PING_RANGE_MIN 245
#define PING_RANGE_MAX 295
#define PL_RANGE_MIN 315
#define PL_RANGE_MAX 375
int SCOREBOARD_WIDTH = 320;
// Y positions
#define ROW_GAP 13
#define ROW_RANGE_MIN 15
#define ROW_RANGE_MAX ( ScreenHeight - 50 )
//
int CHudScoreboard::Draw(float fTime)
{
int can_show_packetloss = 0;
int FAR_RIGHT;
if (!m_iShowscoresHeld && gHUD.m_Health.m_iHealth > 0 && !gHUD.m_iIntermission)
return 1;
GetAllPlayersInfo();
// Packetloss removed on Kelly 'shipping nazi' Bailey's orders
if (cl_showpacketloss && cl_showpacketloss->value && (ScreenWidth >= 400))
{
can_show_packetloss = 1;
SCOREBOARD_WIDTH = 400;
}
else
{
SCOREBOARD_WIDTH = 320;
}
// just sort the list on the fly
// list is sorted first by frags, then by deaths
float list_slot = 0;
int xpos_rel = (ScreenWidth - SCOREBOARD_WIDTH) / 2;
// print the heading line
int ypos = ROW_RANGE_MIN + (list_slot * ROW_GAP);
int xpos = NAME_RANGE_MIN + xpos_rel;
if (!gHUD.m_Teamplay)
gHUD.DrawHudStringHLSCORE(xpos, ypos, NAME_RANGE_MAX + xpos_rel, "Player", 255, 140, 0);
else
gHUD.DrawHudStringHLSCORE(xpos, ypos, NAME_RANGE_MAX + xpos_rel, "Teams", 255, 140, 0);
gHUD.DrawHudStringReverseHLSCORE(KILLS_RANGE_MAX + xpos_rel, ypos, 0, "kills", 255, 140, 0);
gHUD.DrawHudStringHLSCORE(DIVIDER_POS + xpos_rel, ypos, ScreenWidth, "/", 255, 140, 0);
gHUD.DrawHudStringHLSCORE(DEATHS_RANGE_MIN + xpos_rel + 5, ypos, ScreenWidth, "deaths", 255, 140, 0);
gHUD.DrawHudStringHLSCORE(PING_RANGE_MAX + xpos_rel - 35, ypos, ScreenWidth, "latency", 255, 140, 0);
if (can_show_packetloss)
{
gHUD.DrawHudStringHLSCORE(PL_RANGE_MAX + xpos_rel - 35, ypos, ScreenWidth, "pkt loss", 255, 140, 0);
}
FAR_RIGHT = can_show_packetloss ? PL_RANGE_MAX : PING_RANGE_MAX;
FAR_RIGHT += 5;
list_slot += 1.2;
ypos = ROW_RANGE_MIN + (list_slot * ROW_GAP);
xpos = NAME_RANGE_MIN + xpos_rel;
FillRGBA(xpos - 5, ypos, FAR_RIGHT, 1, 255, 140, 0, 255); // draw the seperator line
list_slot += 0.8;
if (!gHUD.m_Teamplay)
{
// it's not teamplay, so just draw a simple player list
DrawPlayers(xpos_rel, list_slot);
return 1;
}
// clear out team scores
int i;
for (int i = 1; i <= m_iNumTeams; i++)
{
if (!g_TeamInfo[i].scores_overriden)
g_TeamInfo[i].frags = g_TeamInfo[i].deaths = 0;
g_TeamInfo[i].ping = g_TeamInfo[i].packetloss = 0;
}
// recalc the team scores, then draw them
for (i = 1; i < MAX_PLAYERS; i++)
{
if (g_PlayerInfoList[i].name == NULL)
continue; // empty player slot, skip
if (g_PlayerExtraInfo[i].teamname[0] == 0)
continue; // skip over players who are not in a team
// find what team this player is in
// find what team this player is in
int j;
for (int j = 1; j <= m_iNumTeams; j++)
{
if (!stricmp(g_PlayerExtraInfo[i].teamname, g_TeamInfo[j].name))
break;
}
if (j > m_iNumTeams) // player is not in a team, skip to the next guy
continue;
if (!g_TeamInfo[j].scores_overriden)
{
g_TeamInfo[j].frags += g_PlayerExtraInfo[i].frags;
g_TeamInfo[j].deaths += g_PlayerExtraInfo[i].deaths;
}
g_TeamInfo[j].ping += g_PlayerInfoList[i].ping;
g_TeamInfo[j].packetloss += g_PlayerInfoList[i].packetloss;
if (g_PlayerInfoList[i].thisplayer)
g_TeamInfo[j].ownteam = TRUE;
else
g_TeamInfo[j].ownteam = FALSE;
}
// find team ping/packetloss averages
for (i = 1; i <= m_iNumTeams; i++)
{
g_TeamInfo[i].already_drawn = FALSE;
if (g_TeamInfo[i].players > 0)
{
g_TeamInfo[i].ping /= g_TeamInfo[i].players; // use the average ping of all the players in the team as the teams ping
g_TeamInfo[i].packetloss /= g_TeamInfo[i].players; // use the average ping of all the players in the team as the teams ping
}
}
// Draw the teams
while (1)
{
int highest_frags = -99999; int lowest_deaths = 99999;
int best_team = 0;
for (i = 1; i <= m_iNumTeams; i++)
{
if (g_TeamInfo[i].players < 0)
continue;
if (!g_TeamInfo[i].already_drawn && g_TeamInfo[i].frags >= highest_frags)
{
if (g_TeamInfo[i].frags > highest_frags || g_TeamInfo[i].deaths < lowest_deaths)
{
best_team = i;
lowest_deaths = g_TeamInfo[i].deaths;
highest_frags = g_TeamInfo[i].frags;
}
}
}
// draw the best team on the scoreboard
if (!best_team)
break;
// draw out the best team
team_info_t *team_info = &g_TeamInfo[best_team];
ypos = ROW_RANGE_MIN + (list_slot * ROW_GAP);
// check we haven't drawn too far down
if (ypos > ROW_RANGE_MAX) // don't draw to close to the lower border
break;
xpos = NAME_RANGE_MIN + xpos_rel;
int r = 255, g = 225, b = 55; // draw the stuff kinda yellowish
if (team_info->ownteam) // if it is their team, draw the background different color
{
// overlay the background in blue, then draw the score text over it
FillRGBA(NAME_RANGE_MIN + xpos_rel - 5, ypos, FAR_RIGHT, ROW_GAP, 0, 0, 255, 70);
}
// draw their name (left to right)
gHUD.DrawHudStringHLSCORE(xpos, ypos, NAME_RANGE_MAX + xpos_rel, team_info->name, r, g, b);
// draw kills (right to left)
xpos = KILLS_RANGE_MAX + xpos_rel;
gHUD.DrawHudNumberStringHLSCORE(xpos, ypos, KILLS_RANGE_MIN + xpos_rel, team_info->frags, r, g, b);
// draw divider
xpos = DIVIDER_POS + xpos_rel;
gHUD.DrawHudStringHLSCORE(xpos, ypos, xpos + 20, "/", r, g, b);
// draw deaths
xpos = DEATHS_RANGE_MAX + xpos_rel;
gHUD.DrawHudNumberStringHLSCORE(xpos, ypos, DEATHS_RANGE_MIN + xpos_rel, team_info->deaths, r, g, b);
// draw ping
// draw ping & packetloss
static char buf[64];
sprintf(buf, "%d", team_info->ping);
xpos = ((PING_RANGE_MAX - PING_RANGE_MIN) / 2) + PING_RANGE_MIN + xpos_rel + 25;
UnpackRGB(r, g, b, RGB_YELLOWISH);
gHUD.DrawHudStringReverseHLSCORE(xpos, ypos, xpos - 50, buf, r, g, b);
// Packetloss removed on Kelly 'shipping nazi' Bailey's orders
if (can_show_packetloss)
{
xpos = ((PL_RANGE_MAX - PL_RANGE_MIN) / 2) + PL_RANGE_MIN + xpos_rel + 25;
sprintf(buf, " %d", team_info->packetloss);
gHUD.DrawHudStringHLSCORE(xpos, ypos, xpos + 50, buf, r, g, b);
}
team_info->already_drawn = TRUE; // set the already_drawn to be TRUE, so this team won't get drawn again
list_slot++;
// draw all the players that belong to this team, indented slightly
list_slot = DrawPlayers(xpos_rel, list_slot, 10, team_info->name);
}
// draw all the players who are not in a team
list_slot += 0.5;
DrawPlayers(xpos_rel, list_slot, 0, "");
return 1;
}
// returns the ypos where it finishes drawing
int CHudScoreboard::DrawPlayers(int xpos_rel, float list_slot, int nameoffset, char *team)
{
int can_show_packetloss = 0;
int FAR_RIGHT;
// Packetloss removed on Kelly 'shipping nazi' Bailey's orders
if (cl_showpacketloss && cl_showpacketloss->value && (ScreenWidth >= 400))
{
can_show_packetloss = 1;
SCOREBOARD_WIDTH = 400;
}
else
{
SCOREBOARD_WIDTH = 320;
}
FAR_RIGHT = can_show_packetloss ? PL_RANGE_MAX : PING_RANGE_MAX;
FAR_RIGHT += 5;
// draw the players, in order, and restricted to team if set
while (1)
{
// Find the top ranking player
int highest_frags = -99999; int lowest_deaths = 99999;
int best_player = 0;
for (int i = 1; i < MAX_PLAYERS; i++)
{
if (g_PlayerInfoList[i].name && g_PlayerExtraInfo[i].frags >= highest_frags)
{
if (!(team && stricmp(g_PlayerExtraInfo[i].teamname, team))) // make sure it is the specified team
{
extra_player_info_t *pl_info = &g_PlayerExtraInfo[i];
if (pl_info->frags > highest_frags || pl_info->deaths < lowest_deaths)
{
best_player = i;
lowest_deaths = pl_info->deaths;
highest_frags = pl_info->frags;
}
}
}
}
if (!best_player)
break;
// draw out the best player
hud_player_info_t *pl_info = &g_PlayerInfoList[best_player];
int ypos = ROW_RANGE_MIN + (list_slot * ROW_GAP);
// check we haven't drawn too far down
if (ypos > ROW_RANGE_MAX) // don't draw to close to the lower border
break;
int xpos = NAME_RANGE_MIN + xpos_rel;
int r = 255, g = 255, b = 255;
if (best_player == m_iLastKilledBy && m_fLastKillTime && m_fLastKillTime > gHUD.m_flTime)
{
if (pl_info->thisplayer)
{ // green is the suicide color? i wish this could do grey...
FillRGBA(NAME_RANGE_MIN + xpos_rel - 5, ypos, FAR_RIGHT, ROW_GAP, 80, 155, 0, 70);
}
else
{ // Highlight the killers name - overlay the background in red, then draw the score text over it
FillRGBA(NAME_RANGE_MIN + xpos_rel - 5, ypos, FAR_RIGHT, ROW_GAP, 255, 0, 0, ((float)15 * (float)(m_fLastKillTime - gHUD.m_flTime)));
}
}
else if (pl_info->thisplayer) // if it is their name, draw it a different color
{
// overlay the background in blue, then draw the score text over it
FillRGBA(NAME_RANGE_MIN + xpos_rel - 5, ypos, FAR_RIGHT, ROW_GAP, 0, 0, 255, 70);
}
// draw their name (left to right)
gHUD.DrawHudStringHLSCORE(xpos + nameoffset, ypos, NAME_RANGE_MAX + xpos_rel, pl_info->name, r, g, b);
// draw kills (right to left)
xpos = KILLS_RANGE_MAX + xpos_rel;
gHUD.DrawHudNumberStringHLSCORE(xpos, ypos, KILLS_RANGE_MIN + xpos_rel, g_PlayerExtraInfo[best_player].frags, r, g, b);
// draw divider
xpos = DIVIDER_POS + xpos_rel;
gHUD.DrawHudStringHLSCORE(xpos, ypos, xpos + 20, "/", r, g, b);
// draw deaths
xpos = DEATHS_RANGE_MAX + xpos_rel;
gHUD.DrawHudNumberStringHLSCORE(xpos, ypos, DEATHS_RANGE_MIN + xpos_rel, g_PlayerExtraInfo[best_player].deaths, r, g, b);
// draw ping & packetloss
static char buf[64];
sprintf(buf, "%d", g_PlayerInfoList[best_player].ping);
xpos = ((PING_RANGE_MAX - PING_RANGE_MIN) / 2) + PING_RANGE_MIN + xpos_rel + 25;
gHUD.DrawHudStringReverseHLSCORE(xpos, ypos, xpos - 50, buf, r, g, b);
// Packetloss removed on Kelly 'shipping nazi' Bailey's orders
if (can_show_packetloss)
{
if (g_PlayerInfoList[best_player].packetloss >= 63)
{
UnpackRGB(r, g, b, RGB_REDISH);
sprintf(buf, " !!!!");
}
else
{
sprintf(buf, " %d", g_PlayerInfoList[best_player].packetloss);
}
xpos = ((PL_RANGE_MAX - PL_RANGE_MIN) / 2) + PL_RANGE_MIN + xpos_rel + 25;
gHUD.DrawHudStringHLSCORE(xpos, ypos, xpos + 50, buf, r, g, b);
}
pl_info->name = NULL; // set the name to be NULL, so this client won't get drawn again
list_slot++;
}
return list_slot;
}
void CHudScoreboard::GetAllPlayersInfo(void)
{
for (int i = 1; i < MAX_PLAYERS; i++)
{
GetPlayerInfo(i, &g_PlayerInfoList[i]);
if (g_PlayerInfoList[i].thisplayer)
m_iPlayerNum = i; // !!!HACK: this should be initialized elsewhere... maybe gotten from the engine
}
}
int CHudScoreboard::MsgFunc_ScoreInfoHUD(const char *pszNameHUD, int iSizeHUD, void *pbufHUD)
{
m_iFlags |= HUD_ACTIVE;
BEGIN_READ(pbufHUD, iSizeHUD);
short cl = READ_BYTE();
short frags = READ_SHORT();
short deaths = READ_SHORT();
short playerclass = READ_SHORT();
short teamnumber = READ_SHORT();
if (cl > 0 && cl <= MAX_PLAYERS)
{
g_PlayerExtraInfo[cl].frags = frags;
g_PlayerExtraInfo[cl].deaths = deaths;
g_PlayerExtraInfo[cl].playerclass = playerclass;
g_PlayerExtraInfo[cl].teamnumber = teamnumber;
gViewPort->UpdateOnPlayerInfo();
}
return 1;
}
// Message handler for TeamInfo message
// accepts two values:
// byte: client number
// string: client team name
int CHudScoreboard::MsgFunc_TeamInfoHUD(const char *pszNameHUD, int iSizeHUD, void *pbufHUD)
{
BEGIN_READ(pbufHUD, iSizeHUD);
short cl = READ_BYTE();
if (cl > 0 && cl <= MAX_PLAYERS)
{ // set the players team
strncpy(g_PlayerExtraInfo[cl].teamname, READ_STRING(), MAX_TEAM_NAME);
}
// rebuild the list of teams
// clear out player counts from teams
int i;
for (int i = 1; i <= m_iNumTeams; i++)
{
g_TeamInfo[i].players = 0;
}
// rebuild the team list
GetAllPlayersInfo();
m_iNumTeams = 0;
for (i = 1; i < MAX_PLAYERS; i++)
{
if (g_PlayerInfoList[i].name == NULL)
continue;
if (g_PlayerExtraInfo[i].teamname[0] == 0)
continue; // skip over players who are not in a team
int j;
// is this player in an existing team?
for (int j = 1; j <= m_iNumTeams; j++)
{
if (g_TeamInfo[j].name[0] == '\0')
break;
if (!stricmp(g_PlayerExtraInfo[i].teamname, g_TeamInfo[j].name))
break;
}
if (j > m_iNumTeams)
{ // they aren't in a listed team, so make a new one
// search through for an empty team slot
for (int j = 1; j <= m_iNumTeams; j++)
{
if (g_TeamInfo[j].name[0] == '\0')
break;
}
m_iNumTeams = max(j, m_iNumTeams);
strncpy(g_TeamInfo[j].name, g_PlayerExtraInfo[i].teamname, MAX_TEAM_NAME);
g_TeamInfo[j].players = 0;
}
g_TeamInfo[j].players++;
}
// clear out any empty teams
for (i = 1; i <= m_iNumTeams; i++)
{
if (g_TeamInfo[i].players < 1)
memset(&g_TeamInfo[i], 0, sizeof(team_info_t));
}
return 1;
}
// Message handler for TeamScore message
// accepts three values:
// string: team name
// short: teams kills
// short: teams deaths
// if this message is never received, then scores will simply be the combined totals of the players.
int CHudScoreboard::MsgFunc_TeamScoreHUD(const char *pszNameHUD, int iSizeHUD, void *pbufHUD)
{
BEGIN_READ(pbufHUD, iSizeHUD);
char *TeamName = READ_STRING();
// find the team matching the name
int i;
for (int i = 1; i <= m_iNumTeams; i++)
{
if (!stricmp(TeamName, g_TeamInfo[i].name))
break;
}
if (i > m_iNumTeams)
return 1;
// use this new score data instead of combined player scores
g_TeamInfo[i].scores_overriden = TRUE;
g_TeamInfo[i].frags = READ_SHORT();
g_TeamInfo[i].deaths = READ_SHORT();
return 1;
}
void CHudScoreboard::DeathMsg(int killer, int victim)
{
// if we were the one killed, or the world killed us, set the scoreboard to indicate suicide
if (victim == m_iPlayerNum || killer == 0)
{
m_iLastKilledBy = killer ? killer : m_iPlayerNum;
m_fLastKillTime = gHUD.m_flTime + 10; // display who we were killed by for 10 seconds
if (killer == m_iPlayerNum)
m_iLastKilledBy = m_iPlayerNum;
}
}
void CHudScoreboard::UserCmd_ShowScores(void)
{
m_iShowscoresHeld = TRUE;
}
void CHudScoreboard::UserCmd_HideScores(void)
{
m_iShowscoresHeld = FALSE;
}
//