Descent3/netgames/dmfc/dmfcstats.cpp

1468 lines
46 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
--- HISTORICAL COMMENTS FOLLOW ---
* $Logfile: /DescentIII/Main/Dmfc/dmfcstats.cpp $
* $Revision: 1.1.1.1 $
* $Date: 2003/08/26 03:57:21 $
* $Author: kevinb $
*
* Functions for in-game stats system
*
* $Log: dmfcstats.cpp,v $
* Revision 1.1.1.1 2003/08/26 03:57:21 kevinb
* initial 1.5 import
*
*
* 34 10/26/99 10:32a Jeff
* fixed COM interface bug
*
* 33 9/03/99 5:48p Jeff
* fixed crash if the stats is brought up before all player records are
* recieved
*
* 32 7/14/99 11:48a Jeff
* localized text for patch fixes
*
* 31 7/11/99 6:09p Jeff
* for team games (or where the divide up by teams flag is set), process
* everyone in the game and display them, before the disconnected people,
* so everyone is displayed.
*
* 30 7/09/99 6:17p Jeff
* fixed bugs when scrolling down in a dedicated server game
*
* 29 7/06/99 11:47p Jeff
* selected pilot when stats are enabled is yourself, not server
*
* 28 5/23/99 3:04a Jason
* fixed bug with player rankings not being updated correctly
*
* 27 5/22/99 1:12a Jeff
* correctly handle Viewer_object
*
* 26 5/19/99 5:26p Jeff
* fixed crash trying to view stats before getting player records from
* server
*
* 25 5/13/99 4:55p Ardussi
* changes for compiling on the Mac
*
* 24 5/03/99 12:07p Jeff
* safety precautions
*
* 23 3/29/99 7:20p Jeff
* added vis stuff for coop stat views
*
* 22 3/27/99 4:53p Jeff
* player rankings in multiplayer games implemented. Fixed join message
* so it doesn't get cut off
*
* 21 3/17/99 12:25p Jeff
* converted DMFC to be COM interface
*
* 20 3/05/99 1:30p Jeff
* fixed 99% of the high res issues
*
* 19 2/11/99 12:51a Jeff
* changed names of exported variables
*
* 18 2/08/99 7:49p Jeff
* removed calls to get frame parameters (function is dead..pending
* removal)
*
* 17 2/08/99 5:22p Jeff
* exported rend_SetZBuffer...fixed up small view of stats
*
* 16 2/08/99 12:06a Jeff
* some new flags for more configuration. Added an option to see from a
* player's view
*
* 15 2/06/99 6:59p Jeff
* created RenderHUDGetTextLineWidth and RenderHUDGetTextHeight
*
* 14 1/31/99 7:26p Matt
* Renamed a bunch of functions to have HUD capitalized
*
* 13 1/24/99 8:31p Jeff
* updated stat manager to show team information
*
* 12 1/20/99 8:07p Jeff
* disconnected players in color mode team show up as grey
*
* 11 1/19/99 3:55a Jeff
* all strings localized out
*
* 10 1/16/99 2:55p Jeff
* possibly fixed some stat manager bugs...needs testing
*
* 9 1/13/99 4:36p Jeff
* fixed scroll bug
*
* 8 1/07/99 5:01p Jeff
* added Int3's and updated all net games to use stats manager...correctly
* too
*
* 7 1/04/99 12:21p Jeff
* added support for hosts.allow/deny and updates stats manager a little
*
* 6 12/08/98 4:47p Jeff
* umm, various changes, fixed pilot pics so that they work for everyone
* now
*
* 5 12/08/98 3:29p Jeff
* updated the team control dialog so the server can determine if they
* want to make the clients wait
*
* 4 12/08/98 12:17p Jeff
* various changes that include an improved Team Control dialog (doesn't
* switch teams until exit..) and spew/respawn players that change teams
*
* 3 12/04/98 7:04p Jeff
* almost finished up dmfc stat manager
*
* 2 12/03/98 7:05p Jeff
* updated new stats
*
* $NoKeywords: $
*/
#include "gamedll_header.h"
#include "DMFC.h"
#include "dmfcinternal.h"
#include <algorithm>
extern DMFCBase *basethis;
extern player_record Player_records[MAX_PLAYER_RECORDS];
#define ROW_PIXEL_GAP 2
#define PAGE_DISP_TIME 2.0f
#define ALPHA_IN_TIME 0.4f
#define PLIST_WIN_W 400
#define PLIST_WIN_H 440
#define PLIST_WIN_X 10
#define PLIST_WIN_Y 20
#define DLIST_WIN_W 210
#define DLIST_WIN_H 440
#define DLIST_WIN_X PLIST_WIN_W + PLIST_WIN_X + 10
#define DLIST_WIN_Y 20
// constructor
CDmfcStats::CDmfcStats() {
m_cPtrs = 0;
m_init = false;
m_flags = 0;
m_SortedPRecs = NULL;
m_pMaxNumberDisplayed = NULL;
m_MaxCount = 0;
m_iPLColCount = 0;
m_iDColCount = 0;
m_PLColInfo = NULL;
m_DColInfo = NULL;
m_PLColCallback = NULL;
m_DColCallback = NULL;
m_PLColCallbackBMP = NULL;
m_DColCallbackBMP = NULL;
m_TeamCallback = NULL;
m_GameName = NULL;
m_top_index = 0;
m_selected_index = 0;
m_bottom_index = -1;
m_enabled = false;
m_font_height = 0;
m_y_start_plist = 0;
m_y_start_detail = 0;
m_x_start_plist = 0;
m_x_start_detail = 0;
m_selected_prec = 0;
m_highlight_bmp = BAD_BITMAP_HANDLE;
m_background_bmp = BAD_BITMAP_HANDLE;
m_page_index = 0;
m_can_fit_count = -1;
m_page_display_time = 0;
m_has_multiple_pages = false;
m_alpha = 0;
alpha_in_time = 0;
}
// destructor
CDmfcStats::~CDmfcStats() {
if (m_GameName) {
free(m_GameName);
}
if (m_PLColInfo) {
free(m_PLColInfo);
}
if (m_DColInfo) {
free(m_DColInfo);
}
if (m_highlight_bmp > BAD_BITMAP_HANDLE)
DLLbm_FreeBitmap(m_highlight_bmp);
if (m_background_bmp > BAD_BITMAP_HANDLE)
DLLbm_FreeBitmap(m_background_bmp);
}
// initialization member function
bool CDmfcStats::Initialize(tDmfcStatsInit *init_info) {
m_font_height = DLLgrfont_GetHeight(basethis->Game_fonts[HUD_FONT_INDEX]);
// m_font_height = DLLRenderHUDGetTextHeight("X");
m_flags = init_info->flags;
m_SortedPRecs = init_info->SortedPlayerRecords;
if (!m_SortedPRecs)
goto error_init;
m_pMaxNumberDisplayed = init_info->MaxNumberDisplayed;
if ((m_flags & DSIF_ONLY_X_PLAYERS_SHOWN) && (!m_pMaxNumberDisplayed))
goto error_init;
m_PLColCallback = init_info->clbPlayerColumn;
m_DColCallback = init_info->clbDetailedColumn;
m_PLColCallbackBMP = init_info->clbPlayerColumnBMP;
m_DColCallbackBMP = init_info->clbDetailedColumnBMP;
m_TeamCallback = init_info->clTeamLine;
m_iPLColCount = init_info->cColumnCountPlayerList;
m_iDColCount = init_info->cColumnCountDetailed;
if (m_iPLColCount > 0 && !init_info->PlayerListColumns)
goto error_init;
if (m_iDColCount > 0 && !init_info->DetailedColumns)
goto error_init;
if (m_iPLColCount > 0) {
m_PLColInfo = (tDmfcStatsColumnInfo *)malloc(sizeof(tDmfcStatsColumnInfo) * m_iPLColCount);
if (!m_PLColInfo)
goto error_init;
memcpy(m_PLColInfo, init_info->PlayerListColumns, sizeof(tDmfcStatsColumnInfo) * m_iPLColCount);
}
if (m_iDColCount > 0) {
m_DColInfo = (tDmfcStatsColumnInfo *)malloc(sizeof(tDmfcStatsColumnInfo) * m_iDColCount);
if (!m_DColInfo) {
goto error_init;
}
memcpy(m_DColInfo, init_info->DetailedColumns, sizeof(tDmfcStatsColumnInfo) * m_iDColCount);
}
if (init_info->GameName) {
m_GameName = strdup(init_info->GameName);
}
m_y_start_plist = PLIST_WIN_Y + 2 * (m_font_height + ROW_PIXEL_GAP);
m_y_start_detail = DLIST_WIN_Y + (m_font_height + ROW_PIXEL_GAP);
m_x_start_plist = PLIST_WIN_X;
m_x_start_detail = DLIST_WIN_X;
m_highlight_bmp = DLLbm_AllocBitmap(32, 32, 0);
m_background_bmp = DLLbm_AllocBitmap(32, 32, 0);
uint16_t *data;
if (m_highlight_bmp == -1)
m_highlight_bmp = BAD_BITMAP_HANDLE;
if (m_background_bmp == -1)
m_background_bmp = BAD_BITMAP_HANDLE;
if (m_highlight_bmp > BAD_BITMAP_HANDLE) {
data = DLLbm_data(m_highlight_bmp, 0);
for (int x = 0; x < 32 * 32; x++) {
data[x] = GR_RGB16(50, 50, 50) | OPAQUE_FLAG;
}
}
if (m_background_bmp > BAD_BITMAP_HANDLE) {
data = DLLbm_data(m_background_bmp, 0);
for (int x = 0; x < 32 * 32; x++) {
data[x] = GR_RGB16(0, 0, 0) | OPAQUE_FLAG;
}
}
m_init = true;
return true;
error_init:
if (m_GameName) {
free(m_GameName);
}
if (m_PLColInfo) {
free(m_PLColInfo);
}
if (m_DColInfo) {
free(m_DColInfo);
}
return false;
}
//=================================
// enables/disables the displaying of the stats screen
void CDmfcStats::Enable(bool enable) {
m_enabled = enable;
if (enable) {
player_record *pr = PRec_GetPRecordByPnum(basethis->GetPlayerNum());
if (pr && pr->state != STATE_EMPTY) {
m_selected_prec = pr - Player_records;
} else {
m_selected_prec = 0;
}
alpha_in_time = 0;
m_alpha = 0;
m_page_index = 0;
m_can_fit_count = -1;
m_page_display_time = 0;
m_has_multiple_pages = false;
basethis->Players[basethis->GetPlayerNum()].small_dll_obj = -1;
if (m_flags & DSIF_SHOWPLAYERVIEW) {
player_record *pr = basethis->GetPlayerRecord(m_selected_prec);
if (pr && pr->state == STATE_INGAME) {
basethis->Players[basethis->GetPlayerNum()].small_dll_obj = basethis->Players[pr->pnum].objnum;
}
}
} else {
basethis->Players[basethis->GetPlayerNum()].small_dll_obj = -1;
}
}
// returns the state of the stats manager
bool CDmfcStats::IsEnabled() { return m_enabled; }
// scrolls the player list down one
void CDmfcStats::ScrollDown() {
if (CanScrollDown()) {
if (m_flags & DSIF_SEPERATE_BY_TEAM) {
// we need to handle team stats a bit different
// we must state on connected people until no more
// connected people exist, then go through the list
// through disconnected people
player_record *pr = basethis->GetPlayerRecord(m_selected_prec);
if (!pr || pr->state == STATE_EMPTY) {
m_selected_index++;
m_selected_prec = m_translate[m_selected_index];
} else {
bool isingame = (pr->state == STATE_INGAME) ? true : false;
while (1) {
m_selected_index++;
if (m_selected_index == (m_MaxCount - 1)) {
// we hit the bottom
m_selected_prec = m_translate[m_selected_index];
if (!isingame)
break; // we have gotten to the bottom of everyone
pr = basethis->GetPlayerRecord(m_selected_prec);
// check to see if we want this player
if (pr->state == STATE_INGAME)
break;
// now go through the disconnected folk
isingame = false;
m_selected_index = -1; // set to -1, since it will be incremented right away
continue;
}
m_selected_prec = m_translate[m_selected_index];
pr = basethis->GetPlayerRecord(m_selected_prec);
if (!pr || pr->state == STATE_EMPTY)
continue;
// should we use this player?
if (isingame) {
if (pr->state != STATE_INGAME)
continue; // move to the next
} else {
if (pr->state != STATE_DISCONNECTED)
continue; // move to the next
}
// all matches up if we haven't continue by here
break;
}
}
} else {
m_selected_index++;
m_selected_prec = m_translate[m_selected_index];
}
m_page_index = 0;
m_can_fit_count = -1;
m_page_display_time = 0;
m_has_multiple_pages = false;
basethis->Players[basethis->GetPlayerNum()].small_dll_obj = -1;
if (m_flags & DSIF_SHOWPLAYERVIEW) {
player_record *pr = basethis->GetPlayerRecord(m_selected_prec);
if (pr && pr->state == STATE_INGAME) {
basethis->Players[basethis->GetPlayerNum()].small_dll_obj = basethis->Players[pr->pnum].objnum;
}
}
}
}
// scrolls the player list up one
void CDmfcStats::ScrollUp() {
if (CanScrollUp()) {
if (m_flags & DSIF_SEPERATE_BY_TEAM) {
// we need to handle team stats a bit different
// we must state on connected people until no more
// connected people exist, then go through the list
// through disconnected people
player_record *pr = basethis->GetPlayerRecord(m_selected_prec);
if (!pr || pr->state == STATE_EMPTY) {
m_selected_index--;
m_selected_prec = m_translate[m_selected_index];
} else {
bool isingame = (pr->state == STATE_INGAME) ? true : false;
while (1) {
m_selected_index--;
if (m_selected_index == 0) {
// we hit the top
m_selected_prec = m_translate[m_selected_index];
if (isingame)
break; // we have gotten to the top of everyone
pr = basethis->GetPlayerRecord(m_selected_prec);
// check to see if we want this player
if (pr->state == STATE_DISCONNECTED)
break;
// now go through the connected folk
isingame = true;
m_selected_index = m_MaxCount; // set to m_MaxCount, since it will be incremented right away
continue;
}
m_selected_prec = m_translate[m_selected_index];
pr = basethis->GetPlayerRecord(m_selected_prec);
if (!pr || pr->state == STATE_EMPTY)
continue;
// should we use this player?
if (isingame) {
if (pr->state != STATE_INGAME)
continue; // move to the next
} else {
if (pr->state != STATE_DISCONNECTED)
continue; // move to the next
}
// all matches up if we haven't continue by here
break;
}
}
} else {
m_selected_index--;
m_selected_prec = m_translate[m_selected_index];
}
m_page_index = 0;
m_can_fit_count = -1;
m_page_display_time = 0;
m_has_multiple_pages = false;
basethis->Players[basethis->GetPlayerNum()].small_dll_obj = -1;
if (m_flags & DSIF_SHOWPLAYERVIEW) {
player_record *pr = basethis->GetPlayerRecord(m_selected_prec);
if (pr && pr->state == STATE_INGAME) {
basethis->Players[basethis->GetPlayerNum()].small_dll_obj = basethis->Players[pr->pnum].objnum;
}
}
}
}
// returns true if the player list can scroll down any more
bool CDmfcStats::CanScrollDown() { return (m_selected_index < (m_MaxCount - 1)) ? true : false; }
// returns true if the player list can scroll up any more
bool CDmfcStats::CanScrollUp() { return (bool)(m_selected_index > 0); }
void drawline(int lx, int rx, int y, float perc) {
int width = ((float)(rx - lx)) * perc;
int center = lx + (rx - lx) / 2;
width = width / 2;
lx = center - width;
rx = center + width;
/*
rx = lx + width;
*/
int ny = y;
basethis->ConvertHUDCoord(lx, y, &lx, &y);
basethis->ConvertHUDCoord(rx, ny, &rx, &ny);
DLLrend_DrawLine(lx, y, rx, ny);
}
// renders the stats screen
void CDmfcStats::Render() {
int i, c, curr_x, curr_y;
player_record *pr;
char buffer[256];
DLLrend_SetFlatColor(GR_WHITE);
float perc_done = (alpha_in_time < ALPHA_IN_TIME) ? (alpha_in_time / ALPHA_IN_TIME) : 1.0f;
bool need_to_display_observer_icon;
bool is_dedicated_server;
int last_team = -1;
int teams_displayed = 0;
bool team_disp[DLLMAX_TEAMS];
for (i = 0; i < DLLMAX_TEAMS; i++) {
team_disp[i] = false;
}
// render all the player list columns
curr_x = m_x_start_plist;
curr_y = PLIST_WIN_Y + 1;
// render window and headings
DLLRenderHUDQuad(PLIST_WIN_X, PLIST_WIN_Y, PLIST_WIN_W, PLIST_WIN_H, 0, 0, 1, 1, m_background_bmp, perc_done * 200.0f,
0);
DLLgrtext_SetFont(basethis->Game_fonts[HUD_FONT_INDEX]);
int l_width = DLLRenderHUDGetTextLineWidth(m_GameName);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, (PLIST_WIN_W / 2) - (l_width / 2), curr_y, m_GameName);
curr_y += (m_font_height + ROW_PIXEL_GAP);
for (c = 0; c < m_iPLColCount; c++) {
strcpy(buffer, m_PLColInfo[c].title);
basethis->ClipString(m_PLColInfo[c].width, buffer, false);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, curr_x, curr_y, buffer);
curr_x += m_PLColInfo[c].width;
}
curr_y += (m_font_height + (ROW_PIXEL_GAP) / 2);
if (m_iPLColCount > 0)
drawline(PLIST_WIN_X, PLIST_WIN_X + PLIST_WIN_W - 1, curr_y, perc_done);
curr_x = m_x_start_plist;
curr_y = m_y_start_plist + 4;
bool displaying_in_game_players = true;
bool not_overflowing = true;
bool update_bottom_index = true;
redisplay_list:
for (i = m_top_index; i < m_MaxCount; i++) {
pr = PRec_GetPRecord(m_translate[i]);
if (!pr || pr->state == STATE_EMPTY)
continue;
if (m_flags & DSIF_SEPERATE_BY_TEAM) {
// for team games we seperate disconnected and ingame people
if (displaying_in_game_players) {
// filter out non-ingame players
if (pr->state != STATE_INGAME)
continue;
} else {
// filter out non-disconnected players
if (pr->state != STATE_DISCONNECTED)
continue;
}
}
is_dedicated_server = basethis->IsPlayerDedicatedServer(pr);
if ((m_flags & DSIF_SEPERATE_BY_TEAM) && (last_team != pr->team) && !is_dedicated_server) {
i--; // this line doesn't count
last_team = pr->team;
buffer[0] = '\0';
if (!team_disp[last_team]) {
teams_displayed++;
team_disp[last_team] = true;
}
if (m_TeamCallback) {
snprintf(buffer, sizeof(buffer), DTXT_TEAMLABEL, basethis->GetTeamString(pr->team));
int length = strlen(buffer);
buffer[length] = ' '; // lose the \0
(*m_TeamCallback)(pr->team, &buffer[length], sizeof(buffer) - length - 1);
buffer[sizeof(buffer) - 1] = '\0';
int width = (PLIST_WIN_X + PLIST_WIN_W - curr_x - 5);
float ratio = DEFAULT_HUD_WIDTH / ((float)*basethis->Game_window_w);
// clip the string to the width
basethis->ClipString(width, buffer, false);
int l = ratio * ((float)DLLRenderHUDGetTextLineWidth(buffer));
int x = (width / 2) - (l / 2);
DLLRenderHUDText(basethis->GetTeamColor(pr->team), m_alpha, 0, x, curr_y, buffer);
}
goto skip_line; // don't process this line
}
// draw the highlight bar if selected bitmap
if (m_selected_index == i) {
DLLRenderHUDQuad(m_x_start_plist, curr_y, PLIST_WIN_W, m_font_height, 0, 0, 1, 1, m_highlight_bmp,
perc_done * 128.0f, 0);
}
need_to_display_observer_icon =
(pr->state == STATE_INGAME) ? (basethis->IsPlayerObserver(pr->pnum) ? true : false) : false;
for (c = 0; c < m_iPLColCount; c++) {
buffer[0] = '\0';
if (!is_dedicated_server || m_PLColInfo[c].type == DSCOL_PILOT_NAME || m_PLColInfo[c].type == DSCOL_BLANK) {
switch (m_PLColInfo[c].type) {
case DSCOL_BMP:
break;
case DSCOL_KILLS_LEVEL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.kills[DSTAT_LEVEL]);
break;
case DSCOL_KILLS_OVERALL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.kills[DSTAT_OVERALL]);
break;
case DSCOL_KILLS_BOTH:
snprintf(buffer, sizeof(buffer), "%d[%d]", pr->dstats.kills[DSTAT_LEVEL], pr->dstats.kills[DSTAT_OVERALL]);
break;
case DSCOL_DEATHS_LEVEL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.deaths[DSTAT_LEVEL]);
break;
case DSCOL_DEATHS_OVERALL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.deaths[DSTAT_OVERALL]);
break;
case DSCOL_DEATHS_BOTH:
snprintf(buffer, sizeof(buffer), "%d[%d]", pr->dstats.deaths[DSTAT_LEVEL], pr->dstats.deaths[DSTAT_OVERALL]);
break;
case DSCOL_SUICIDES_LEVEL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.suicides[DSTAT_LEVEL]);
break;
case DSCOL_SUICIDES_OVERALL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.suicides[DSTAT_OVERALL]);
break;
case DSCOL_SUICIDES_BOTH:
snprintf(buffer, sizeof(buffer), "%d[%d]", pr->dstats.suicides[DSTAT_LEVEL],
pr->dstats.suicides[DSTAT_OVERALL]);
break;
case DSCOL_PILOT_NAME:
strcpy(buffer, pr->callsign);
break;
case DSCOL_PILOT_TEAM:
snprintf(buffer, sizeof(buffer), "%s", basethis->GetTeamString(pr->team));
break;
case DSCOL_PING:
if (pr->state == STATE_INGAME)
snprintf(buffer, sizeof(buffer), "%.0f", basethis->NetPlayers[pr->pnum].ping_time * 1000.0f);
else
strcpy(buffer, "N/A");
break;
case DSCOL_CUSTOM:
if (m_PLColCallback)
(*m_PLColCallback)(m_translate[i], c, buffer, sizeof(buffer));
break;
case DSCOL_BLANK:
buffer[0] = '\0';
break;
}
} else {
strcpy(buffer, "N/A");
}
int width_to_use = m_PLColInfo[c].width;
if (m_PLColInfo[c].type != DSCOL_BMP) {
if (need_to_display_observer_icon) {
if (m_flags & DSIF_SHOW_OBSERVERICON && m_PLColInfo[c].type == DSCOL_PILOT_NAME) {
need_to_display_observer_icon = false;
DLLRenderHUDQuad(curr_x, curr_y, m_font_height, m_font_height, 0, 0, 1, 1, basethis->hBitmapObserver,
m_alpha, 0);
curr_x += m_font_height + 1;
width_to_use -= (m_font_height + 1);
}
}
// clip the string to the width
basethis->ClipString(std::min(width_to_use, (PLIST_WIN_X + PLIST_WIN_W - curr_x - 5)), buffer, false);
// print out string
ddgr_color color;
switch (m_PLColInfo[c].color_type) {
case DSCOLOR_NORMAL:
color = (pr->state == STATE_INGAME) ? GR_GREEN : GR_GREY;
break;
case DSCOLOR_TEAM:
color = (m_PLColInfo[c].type == DSCOL_PILOT_NAME)
? (basethis->GetTeamColor(pr->team))
: ((pr->state == STATE_INGAME) ? (basethis->GetTeamColor(pr->team)) : (GR_GREY));
break;
case DSCOLOR_SHIPCOLOR:
color = (pr->state == STATE_INGAME) ? basethis->Player_colors[pr->pnum] : GR_GREY;
break;
case DSCOLOR_CUSTOM:
color = m_PLColInfo[c].color;
break;
default:
color = (pr->state == STATE_INGAME) ? GR_GREEN : GR_GREY;
break;
};
DLLRenderHUDText(color, m_alpha, 0, curr_x, curr_y, buffer);
} else {
// we need to draw a custom bitmap
(*m_PLColCallbackBMP)(m_translate[i], c, curr_x, curr_y, m_PLColInfo[c].width, m_font_height + ROW_PIXEL_GAP,
m_alpha);
}
curr_x += width_to_use;
if (curr_x > PLIST_WIN_W + PLIST_WIN_X)
break; // we can't fit anymore columns
}
// update the bottom index
if (update_bottom_index || i > m_bottom_index) {
m_bottom_index = i;
update_bottom_index = false; // we force an update the first time through
}
skip_line:
curr_x = m_x_start_plist;
curr_y += (m_font_height + ROW_PIXEL_GAP);
if ((curr_y + m_font_height + ROW_PIXEL_GAP) > PLIST_WIN_H + PLIST_WIN_Y) {
not_overflowing = false;
break; // we are done here
}
}
if ((m_flags & DSIF_SEPERATE_BY_TEAM) && displaying_in_game_players && not_overflowing) {
// we can continue displaying, but just the disconnected people now
last_team = -1; // so we redraw the team names
displaying_in_game_players = false;
// display the disconnected people
goto redisplay_list;
}
// Display the remaining teams
if ((m_flags & DSIF_SEPERATE_BY_TEAM) && (teams_displayed < DLLMAX_TEAMS) &&
((curr_y + m_font_height + ROW_PIXEL_GAP) < PLIST_WIN_H + PLIST_WIN_Y)) {
int tcount = basethis->GetNumTeams();
for (i = 0; i < tcount; i++) {
if (team_disp[i])
continue;
team_disp[i] = true;
last_team = i;
buffer[0] = '\0';
teams_displayed++;
if (m_TeamCallback) {
snprintf(buffer, sizeof(buffer), DTXT_TEAMLABEL, basethis->GetTeamString(i));
int length = strlen(buffer);
buffer[length] = ' '; // lose the \0
(*m_TeamCallback)(i, &buffer[length], sizeof(buffer) - length - 1);
buffer[sizeof(buffer) - 1] = '\0';
int width = (PLIST_WIN_X + PLIST_WIN_W - curr_x - 5);
float ratio = DEFAULT_HUD_WIDTH / ((float)*basethis->Game_window_w);
// clip the string to the width
basethis->ClipString(width, buffer, false);
int l = ratio * ((float)DLLRenderHUDGetTextLineWidth(buffer));
int x = (width / 2) - (l / 2);
DLLRenderHUDText(basethis->GetTeamColor(i), m_alpha, 0, x, curr_y, buffer);
}
curr_y += (m_font_height + ROW_PIXEL_GAP);
if ((curr_y + m_font_height + ROW_PIXEL_GAP) > PLIST_WIN_H + PLIST_WIN_Y) {
break; // we are done here
}
}
}
// render the detailed menu
curr_x = m_x_start_detail;
curr_y = DLIST_WIN_Y + 1;
DLLRenderHUDQuad(DLIST_WIN_X, DLIST_WIN_Y, DLIST_WIN_W, DLIST_WIN_H, 0, 0, 1, 1, m_background_bmp, perc_done * 200.0f,
0);
pr = PRec_GetPRecord(m_selected_prec);
if (!pr || pr->state == STATE_EMPTY || pr->pinfo == NULL)
return;
is_dedicated_server = basethis->IsPlayerDedicatedServer(pr);
// display the heading for the player
strcpy(buffer, pr->callsign);
basethis->ClipString(DLIST_WIN_W, buffer, false);
l_width = DLLRenderHUDGetTextLineWidth(buffer);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, DLIST_WIN_X + (DLIST_WIN_W / 2) - (l_width / 2), curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
if (m_flags & DSIF_SHOW_PIC && pr->state == STATE_INGAME && !is_dedicated_server) {
if (basethis->PilotPicBmpHandles[pr->pnum] > BAD_BITMAP_HANDLE) {
// display the pilot picture
DLLRenderHUDQuad(DLIST_WIN_X + (DLIST_WIN_W / 2) - 32, curr_y, 64, 64, 0, 0, 1, 1,
basethis->PilotPicBmpHandles[pr->pnum], m_alpha, 0);
curr_y += 70;
} else if (basethis->AreLogosEnabled()) { // only show logo's if enabled
bool animated;
int bmp_to_disp = BAD_BITMAP_HANDLE;
int logo_handle = basethis->GetPlayerLogoBmp(pr->pnum, &animated);
if (logo_handle != -1) {
if (animated) {
// display a vclip
vclip *vc = &basethis->GameVClips[logo_handle];
float interval = 1.0f / 10.0f;
int frames_elapsed = (int)(alpha_in_time / interval);
int frame = frames_elapsed % vc->num_frames;
bmp_to_disp = vc->frames[frame];
} else {
bmp_to_disp = logo_handle;
}
DLLRenderHUDQuad(DLIST_WIN_X + (DLIST_WIN_W / 2) - 32, curr_y, 64, 64, 0, 0, 1, 1, bmp_to_disp, m_alpha, 0);
curr_y += 70;
}
}
}
int old_y = curr_y;
int max_w = 0;
int x_to_use = curr_x + 5;
PInfo *pi;
tPExtraInfo *ei;
pi = (PInfo *)pr->pinfo;
ei = pi->GetExtraInfo();
// drawline(DLIST_WIN_X,DLIST_WIN_X+DLIST_WIN_W-1,curr_y,perc_done); curr_y += 3;
// game status
strcpy(buffer, DTXT_STAT_STATUS);
// l_width = (DLLgrtext_GetTextLineWidth(buffer)*(*basethis->Hud_aspect_x));
l_width = DLLRenderHUDGetTextLineWidth(buffer);
max_w = l_width;
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
// ranking (if available)
int player_rank;
char player_rank_buffer[64];
player_rank =
(pr->state == STATE_INGAME && !is_dedicated_server) ? GetPlayerRankIndex(pr->pnum, player_rank_buffer) : -1;
if (player_rank != -1) {
strcpy(buffer, DTXT_RANK);
l_width = DLLRenderHUDGetTextLineWidth(buffer);
if (l_width > max_w)
max_w = l_width;
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
}
// team (if available)
if (basethis->GetNumTeams() > 1 && !is_dedicated_server) {
strcpy(buffer, DTXT_STAT_TEAM);
l_width = DLLRenderHUDGetTextLineWidth(buffer);
if (l_width > max_w)
max_w = l_width;
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
}
// time in game
strcpy(buffer, DTXT_STAT_PLAYTIME);
l_width = DLLRenderHUDGetTextLineWidth(buffer);
if (l_width > max_w)
max_w = l_width;
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
uint8_t streak_type = 0;
if (!is_dedicated_server) {
// none of this should be displayed for a dedicated server
if (ei->kills_in_a_row)
streak_type = 1;
else if (ei->deaths_in_a_row)
streak_type = 2;
// kills/deaths in a row
if (streak_type > 0) {
strcpy(buffer, DTXT_STAT_STREAK);
l_width = DLLRenderHUDGetTextLineWidth(buffer);
if (l_width > max_w)
max_w = l_width;
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
}
// last player you killed
if (!(m_flags & DSIF_NOLASTVICTIM)) {
strcpy(buffer, DTXT_STAT_LASTVICTIM);
l_width = DLLRenderHUDGetTextLineWidth(buffer);
if (l_width > max_w)
max_w = l_width;
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
}
// last player to kill you
if (!(m_flags & DSIF_NOLASTKILLER)) {
strcpy(buffer, DTXT_STAT_LASTKILLER);
l_width = DLLRenderHUDGetTextLineWidth(buffer);
if (l_width > max_w)
max_w = l_width;
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
}
}
curr_y = old_y;
x_to_use += (max_w + 5);
int width_to_clip;
width_to_clip = DLIST_WIN_X + DLIST_WIN_W - x_to_use - 5;
// game status
if (pr->state == STATE_INGAME) {
if (basethis->IsPlayerDedicatedServer(pr)) {
strcpy(buffer, DTXT_STAT_SERVER);
} else {
if (basethis->IsPlayerObserver(pr->pnum)) {
strcpy(buffer, DTXT_STAT_OBSERVER);
} else {
strcpy(buffer, DTXT_STAT_PLAYING);
}
}
} else {
strcpy(buffer, DTXT_STAT_DISCONNECTED);
}
basethis->ClipString(width_to_clip, buffer, false);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
// player ranking (if available)
if (player_rank != -1) {
basethis->ClipString(width_to_clip, player_rank_buffer, false);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, player_rank_buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
}
// team (if available)
if (basethis->GetNumTeams() > 1 && !is_dedicated_server) {
strcpy(buffer, basethis->GetTeamString(pr->team));
basethis->ClipString(width_to_clip, buffer, false);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
}
// time in game
strcpy(buffer, basethis->GetTimeString(basethis->GetTimeInGame(m_selected_prec)));
basethis->ClipString(width_to_clip, buffer, false);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
if (!is_dedicated_server) {
switch (streak_type) {
case 1: // kills in a row
snprintf(buffer, sizeof(buffer), "%d %s", ei->kills_in_a_row,
(ei->kills_in_a_row == 1) ? DTXT_STAT_KILL : DTXT_STAT_KILLS);
basethis->ClipString(width_to_clip, buffer, false);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
break;
case 2: // deaths in a row
snprintf(buffer, sizeof(buffer), "%d %s", ei->deaths_in_a_row,
(ei->deaths_in_a_row == 1) ? DTXT_STAT_DEATH : DTXT_STAT_DEATHS);
basethis->ClipString(width_to_clip, buffer, false);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
break;
default:
break;
}
player_record *tpr;
// last player you killed
if (ei->last_kill_num != -1) {
tpr = PRec_GetPRecord(ei->last_kill_num);
if (tpr && tpr->state != STATE_EMPTY) {
strcpy(buffer, tpr->callsign);
} else {
buffer[0] = '\0';
}
} else {
buffer[0] = '\0';
}
if (!(m_flags & DSIF_NOLASTVICTIM)) {
basethis->ClipString(width_to_clip, buffer, false);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
}
// last player to kill you
if (ei->last_death_num != -1) {
tpr = PRec_GetPRecord(ei->last_death_num);
if (tpr && tpr->state != STATE_EMPTY) {
strcpy(buffer, tpr->callsign);
} else {
buffer[0] = '\0';
}
} else {
buffer[0] = '\0';
}
if (!(m_flags & DSIF_NOLASTKILLER)) {
basethis->ClipString(width_to_clip, buffer, false);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, x_to_use, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
}
}
// drawline(DLIST_WIN_X,DLIST_WIN_X+DLIST_WIN_W-1,curr_y,perc_done); curr_y += 10;
curr_x = m_x_start_detail;
// now do the detail columns (if any for this player)
for (c = 0; c < m_iDColCount; c++) {
strcpy(buffer, m_DColInfo[c].title);
basethis->ClipString(m_DColInfo[c].width, buffer, false);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, curr_x, curr_y, buffer);
curr_x += m_DColInfo[c].width;
}
if (m_iDColCount > 0)
curr_y += (m_font_height + ROW_PIXEL_GAP);
curr_x = m_x_start_detail;
if (m_iDColCount > 0)
drawline(DLIST_WIN_X, DLIST_WIN_X + DLIST_WIN_W - 1, curr_y, perc_done);
curr_y += 3;
for (c = 0; c < m_iDColCount; c++) {
buffer[0] = '\0';
if (!is_dedicated_server || m_DColInfo[c].type == DSCOL_PILOT_NAME || m_DColInfo[c].type == DSCOL_BLANK) {
switch (m_DColInfo[c].type) {
case DSCOL_BMP:
break;
case DSCOL_KILLS_LEVEL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.kills[DSTAT_LEVEL]);
break;
case DSCOL_KILLS_OVERALL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.kills[DSTAT_OVERALL]);
break;
case DSCOL_KILLS_BOTH:
snprintf(buffer, sizeof(buffer), "%d[%d]", pr->dstats.kills[DSTAT_LEVEL], pr->dstats.kills[DSTAT_OVERALL]);
break;
case DSCOL_DEATHS_LEVEL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.deaths[DSTAT_LEVEL]);
break;
case DSCOL_DEATHS_OVERALL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.deaths[DSTAT_OVERALL]);
break;
case DSCOL_DEATHS_BOTH:
snprintf(buffer, sizeof(buffer), "%d[%d]", pr->dstats.deaths[DSTAT_LEVEL], pr->dstats.deaths[DSTAT_OVERALL]);
break;
case DSCOL_SUICIDES_LEVEL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.suicides[DSTAT_LEVEL]);
break;
case DSCOL_SUICIDES_OVERALL:
snprintf(buffer, sizeof(buffer), "%d", pr->dstats.suicides[DSTAT_OVERALL]);
break;
case DSCOL_SUICIDES_BOTH:
snprintf(buffer, sizeof(buffer), "%d[%d]", pr->dstats.suicides[DSTAT_LEVEL],
pr->dstats.suicides[DSTAT_OVERALL]);
break;
case DSCOL_PILOT_NAME:
strcpy(buffer, pr->callsign);
break;
case DSCOL_PILOT_TEAM:
snprintf(buffer, sizeof(buffer), "%s", basethis->GetTeamString(pr->team));
break;
case DSCOL_PING:
if (pr->state == STATE_INGAME)
snprintf(buffer, sizeof(buffer), "%.0f", basethis->NetPlayers[pr->pnum].ping_time * 1000.0f);
else
strcpy(buffer, "---");
break;
case DSCOL_CUSTOM:
if (m_DColCallback)
(*m_DColCallback)(m_selected_prec, c, buffer, sizeof(buffer));
break;
case DSCOL_BLANK:
buffer[0] = '\0';
break;
}
} else {
strcpy(buffer, "N/A");
}
if (m_DColInfo[c].type != DSCOL_BMP) {
// clip the string to the width
basethis->ClipString(std::min(m_DColInfo[c].width, (DLIST_WIN_X + DLIST_WIN_W - curr_x - 5)), buffer, false);
// print out string
ddgr_color color;
switch (m_DColInfo[c].color_type) {
case DSCOLOR_NORMAL:
color = (pr->state == STATE_INGAME) ? GR_GREEN : GR_GREY;
break;
case DSCOLOR_TEAM:
color = basethis->GetTeamColor(pr->team);
break;
case DSCOLOR_SHIPCOLOR:
color = (pr->state == STATE_INGAME) ? basethis->Player_colors[pr->pnum] : GR_GREY;
break;
case DSCOLOR_CUSTOM:
color = m_DColInfo[c].color;
break;
default:
color = (pr->state == STATE_INGAME) ? GR_GREEN : GR_GREY;
break;
};
DLLRenderHUDText(color, m_alpha, 0, curr_x, curr_y, buffer);
} else {
// we need to draw a custom bitmap
(*m_DColCallbackBMP)(m_selected_prec, c, curr_x, curr_y, m_DColInfo[c].width, m_font_height + ROW_PIXEL_GAP,
m_alpha);
}
curr_x += m_DColInfo[c].width;
if (curr_x > DLIST_WIN_W + DLIST_WIN_X)
break; // we can't fit anymore columns
}
curr_y += (m_font_height + ROW_PIXEL_GAP);
// finally do the who-killed-who stats for the player
int col_callsign, col_kills, col_deaths;
col_callsign = m_x_start_detail + 5;
col_kills = col_callsign + 100;
col_deaths = col_kills + 50;
int colw_callsign, colw_kills, colw_deaths;
colw_callsign = col_kills - col_callsign;
colw_kills = col_deaths - col_kills;
colw_deaths = DLIST_WIN_X + DLIST_WIN_W - col_deaths;
if (!(m_flags & DSIF_NODETAILEDINFO)) {
DLLRenderHUDText(GR_WHITE, m_alpha, 0, col_callsign, curr_y, DTXT_STAT_PILOTNAMELABEL);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, col_kills, curr_y, DTXT_STAT_KILLS);
DLLRenderHUDText(GR_WHITE, m_alpha, 0, col_deaths, curr_y, DTXT_STAT_DEATHS);
curr_y += (m_font_height + ROW_PIXEL_GAP);
drawline(DLIST_WIN_X, DLIST_WIN_X + DLIST_WIN_W - 1, curr_y, perc_done);
curr_y += 3;
if (m_can_fit_count == -1) {
// we need to recalculate the how-many-items-can-fit-on-this-page value
int count = 0;
int y = curr_y;
while (y + (m_font_height + ROW_PIXEL_GAP) < DLIST_WIN_H + DLIST_WIN_Y) {
count++;
y += (m_font_height + ROW_PIXEL_GAP);
}
m_can_fit_count = count;
}
tPInfoStat stat;
player_record *dpr;
int stats_displayed = 0;
bool clean_exit = true;
int start_index = (m_page_index * m_can_fit_count);
bool team_game = (basethis->GetNumTeams() > 1) ? true : false;
if (basethis->FindPInfoStatFirst(m_selected_prec, &stat)) {
if (stat.slot != m_selected_prec) {
dpr = basethis->GetPlayerRecord(stat.slot);
stats_displayed++;
ASSERT(dpr != NULL);
if (dpr && start_index < stats_displayed) {
ddgr_color color;
color = (dpr->state == STATE_INGAME)
? ((team_game) ? basethis->GetTeamColor(dpr->team) : basethis->Player_colors[dpr->pnum])
: GR_GREY;
strcpy(buffer, dpr->callsign);
basethis->ClipString(colw_callsign - 5, buffer, false);
DLLRenderHUDText(color, m_alpha, 0, col_callsign, curr_y, buffer);
snprintf(buffer, sizeof(buffer), "%d", stat.kills);
basethis->ClipString(colw_kills - 5, buffer, false);
DLLRenderHUDText(color, m_alpha, 0, col_kills, curr_y, buffer);
snprintf(buffer, sizeof(buffer), "%d", stat.deaths);
basethis->ClipString(colw_deaths - 5, buffer, false);
DLLRenderHUDText(color, m_alpha, 0, col_deaths, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
}
}
while (basethis->FindPInfoStatNext(&stat)) {
if (stat.slot != m_selected_prec) {
dpr = basethis->GetPlayerRecord(stat.slot);
stats_displayed++;
ASSERT(dpr != NULL);
if (dpr && start_index < stats_displayed) {
ddgr_color color;
color = (dpr->state == STATE_INGAME)
? ((team_game) ? basethis->GetTeamColor(dpr->team) : basethis->Player_colors[dpr->pnum])
: GR_GREY;
strcpy(buffer, dpr->callsign);
basethis->ClipString(colw_callsign - 5, buffer, false);
DLLRenderHUDText(color, m_alpha, 0, col_callsign, curr_y, buffer);
snprintf(buffer, sizeof(buffer), "%d", stat.kills);
basethis->ClipString(colw_kills - 5, buffer, false);
DLLRenderHUDText(color, m_alpha, 0, col_kills, curr_y, buffer);
snprintf(buffer, sizeof(buffer), "%d", stat.deaths);
basethis->ClipString(colw_deaths - 5, buffer, false);
DLLRenderHUDText(color, m_alpha, 0, col_deaths, curr_y, buffer);
curr_y += (m_font_height + ROW_PIXEL_GAP);
}
}
if (curr_y + (m_font_height + ROW_PIXEL_GAP) > DLIST_WIN_H + DLIST_WIN_Y) {
if (basethis->FindPInfoStatNext(&stat)) {
clean_exit = false; // there are more stats
m_has_multiple_pages = true;
}
break; // can't fit anymore
}
}
}
basethis->FindPInfoStatClose();
m_page_display_time += *basethis->Frametime;
if ((m_page_display_time >= PAGE_DISP_TIME) && m_has_multiple_pages) {
if (!clean_exit)
m_page_index++;
else
m_page_index = 0; // if there was a clean exit, that means we were on the last page,
// go back to the beginning
m_page_display_time = 0;
}
}
if (m_flags & DSIF_SHOWPLAYERVIEW && m_flags & DSIF_NODETAILEDINFO && pr->state == STATE_INGAME) {
DLLgrtext_Flush();
float x, y, w, h;
x = (float)DLIST_WIN_X + 2;
y = (float)curr_y;
h = w = (float)std::min(DLIST_WIN_W - 4, (DLIST_WIN_Y + DLIST_WIN_H - curr_y) - 4);
object *obj_view = &basethis->Objects[basethis->Players[pr->pnum].objnum];
x *= *basethis->Hud_aspect_x;
y *= *basethis->Hud_aspect_y;
w *= *basethis->Hud_aspect_x;
h *= *basethis->Hud_aspect_y;
x += *basethis->Game_window_x;
y += *basethis->Game_window_y;
if (obj_view->type == OBJ_PLAYER) {
DisplayPlayerView((int)x, (int)y, (int)w, (int)h, obj_view);
}
}
alpha_in_time += *basethis->Frametime;
m_alpha = (perc_done * 255.0f);
}
// validates all the variables
void CDmfcStats::ValidateInfo() {
// change index to match the last selected prec num so it doesn't move around
int new_idx = -1;
for (int p = 0; p < m_MaxCount; p++) {
if (m_translate[p] == m_selected_prec) {
// found it, update the index
new_idx = p;
break;
}
}
if (new_idx == -1) {
// the old prec wasn't found
m_selected_index = 0;
} else {
m_selected_index = new_idx;
}
// make sure selected index is in view
while (m_selected_index < m_top_index) {
m_top_index--;
}
if (m_bottom_index != -1) { // make sure it has been set in a frame
while (m_selected_index > m_bottom_index) {
m_top_index++;
m_bottom_index++;
}
}
}
// the do-it-all function, call once per frame
void CDmfcStats::DoFrame() {
if (!m_enabled)
return;
int high_count;
int count;
int index;
static slot_state last_state = STATE_EMPTY;
player_record *pr;
if (m_flags & DSIF_ONLY_X_PLAYERS_SHOWN)
high_count = *m_pMaxNumberDisplayed;
else
high_count = MAX_PLAYER_RECORDS;
// count the number of players in the game/disconnected
// build translation table
count = 0;
index = 0;
if (m_SortedPRecs[0] != 0) {
count = 0;
index = 0;
}
for (int p = 0; p < MAX_PLAYER_RECORDS; p++) {
// increase count to see how many at most there are to display
pr = PRec_GetPRecord(p);
if (pr && pr->state != STATE_EMPTY && !basethis->IsPlayerDedicatedServer(pr))
count++;
// update translation table
pr = PRec_GetPRecord(m_SortedPRecs[p]);
if (pr && pr->state != STATE_EMPTY && !basethis->IsPlayerDedicatedServer(pr)) {
m_translate[index] = m_SortedPRecs[p];
index++;
}
}
if (m_flags & DSIF_SEPERATE_BY_TEAM) {
// Seperate by teams
int team_index;
int real_index = 0;
int num_teams = basethis->GetNumTeams();
int temp_array[MAX_PLAYER_RECORDS];
int prec, p;
bool getting_ingame_people = true;
get_disconnected_people:
for (team_index = 0; team_index < num_teams; team_index++) {
for (p = 0; p < index; p++) {
prec = m_translate[p];
pr = PRec_GetPRecord(prec);
if (pr->state != STATE_EMPTY && pr->team == team_index) {
// we must make sure to move all disconnected people to the
// bottom
if ((getting_ingame_people && pr->state == STATE_INGAME) ||
(!getting_ingame_people && pr->state == STATE_DISCONNECTED)) {
temp_array[real_index] = prec;
real_index++;
}
}
}
// next team
}
if (getting_ingame_people) {
getting_ingame_people = false;
goto get_disconnected_people;
}
memcpy(m_translate, temp_array, sizeof(int) * MAX_PLAYER_RECORDS);
}
m_MaxCount = std::min({high_count, count, index});
pr = PRec_GetPRecord(m_selected_prec);
if (!pr || pr->state != last_state) {
m_can_fit_count = -1; // we need to recalc this
m_page_index = 0;
m_has_multiple_pages = false;
if (pr)
last_state = pr->state;
}
ValidateInfo();
Render();
// save the last selected prec num for the next frame
m_selected_prec = m_translate[m_selected_index];
}
void CDmfcStats::DisplayPlayerView(int x, int y, int w, int h, object *viewer) {
object *save_viewer_object = *basethis->Viewer_object;
DLLrend_SetZBufferState(1);
// Set up for rendering
DLLStartFrame(x, y, x + w, y + h, true);
// Reset facings for mine stuff
DLLResetFacings();
// Set the viewer for this render
*basethis->Viewer_object = viewer;
// Render the world
DLLGameRenderWorld(viewer, &viewer->pos, viewer->roomnum, &viewer->orient, *basethis->Render_zoom, false);
// Restore the viewre
*basethis->Viewer_object = save_viewer_object;
// Done rendering
DLLEndFrame();
}