This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Commando/playermanager.cpp

1520 lines
39 KiB
C++
Raw Permalink Normal View History

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** 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/>.
*/
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando *
* *
* $Archive:: /Commando/Code/Commando/playermanager.cpp $*
* *
* $Author:: Denzil_l $*
* *
* $Modtime:: 2/27/02 6:08p $*
* *
* $Revision:: 128 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "playermanager.h"
#include <win.h>
#include <stdio.h>
#include <float.h>
#include "teammanager.h"
#include "miscutil.h"
#include "_globals.h"
#include "assets.h"
#include "cnetwork.h"
#include "multihud.h"
#include "textdisplay.h"
#include "font3d.h"
#include "gamedata.h"
#include "wwdebug.h"
#include "chunkio.h"
#include "useroptions.h"
#include "smartgameobj.h"
#include "playertype.h"
#include "devoptions.h"
#include "render2d.h"
#include "wwprofile.h"
#include "gametype.h"
#include "translatedb.h"
#include "string_ids.h"
#include "systeminfolog.h"
#include "consolemode.h"
#include "gamespyadmin.h"
#include "demosupport.h"
//
// Class statics
//
SList<cPlayer> cPlayerManager::PlayerList;
cPlayer * cPlayerManager::Player_Array[];
Render2DTextClass * cPlayerManager::PTextRenderer = NULL;
Font3DInstanceClass * cPlayerManager::PFont = NULL;
const float cPlayerManager::Y_INCREMENT_FACTOR = 1.2f;
int cPlayerManager::XPos = 0;
int cPlayerManager::YPos = 0;
Notifier<PlayerMgrEvent> cPlayerManager::mNotifier;
//------------------------------------------------------------------------------------
void cPlayerManager::Onetime_Init(void)
{
WWDEBUG_SAY(("cPlayerManager::Onetime_Init\n"));
if (!ConsoleBox.Is_Exclusive()) {
WWASSERT(PFont == NULL);
WWASSERT(WW3DAssetManager::Get_Instance() != NULL);
PFont = WW3DAssetManager::Get_Instance()->Get_Font3DInstance("FONT6x8.TGA");
WWASSERT(PFont != NULL);
PFont->Set_Mono_Spaced();
SET_REF_OWNER(PFont);
WWASSERT(PTextRenderer == NULL);
PTextRenderer = new Render2DTextClass(PFont);
WWASSERT(PTextRenderer != NULL);
PTextRenderer->Set_Coordinate_Range(Render2DClass::Get_Screen_Resolution());
}
ZeroMemory(Player_Array, sizeof(Player_Array));
}
//------------------------------------------------------------------------------------
void cPlayerManager::Onetime_Shutdown(void)
{
WWDEBUG_SAY(("cPlayerManager::Onetime_Shutdown\n"));
if (!ConsoleBox.Is_Exclusive()) {
WWASSERT(PTextRenderer != NULL);
delete PTextRenderer;
PTextRenderer = NULL;
WWASSERT(PFont != NULL);
PFont->Release_Ref();
PFont = NULL;
}
}
//------------------------------------------------------------------------------------
void cPlayerManager::Think(void)
{
WWPROFILE("cPlayerManager::Think");
if (MultiHUDClass::Is_On()) {
WWASSERT(PTextRenderer != NULL);
Render_Player_List();
}
else {
if (PTextRenderer) PTextRenderer->Reset();
}
}
//-----------------------------------------------------------------------------------
void cPlayerManager::Render(void)
{
WWPROFILE("cPlayerManager::Render");
if (PTextRenderer != NULL) {
WWASSERT(PTextRenderer != NULL);
PTextRenderer->Render();
}
}
//------------------------------------------------------------------------------------
cPlayer * cPlayerManager::Find_Player(int id)
{
SLNode<cPlayer> * objnode;
for (objnode = PlayerList.Head(); objnode; objnode = objnode->Next()) {
cPlayer * p_player = objnode->Data();
WWASSERT(p_player != NULL);
if (p_player->Get_Is_Active().Is_True() &&
p_player->Get_Id() == id) {
return p_player; // found it
}
}
return NULL; // Not found
}
//------------------------------------------------------------------------------------
cPlayer * cPlayerManager::Find_Player(const WideStringClass & name)
{
SLNode<cPlayer> * objnode;
for (objnode = PlayerList.Head(); objnode; objnode = objnode->Next()) {
cPlayer *p_player = objnode->Data();
WWASSERT(p_player != NULL);
if (p_player->Get_Is_Active().Is_True() &&
!name.Compare_No_Case(p_player->Get_Name())) {
return p_player; // found it
}
}
return NULL; // Not found
}
//------------------------------------------------------------------------------------
cPlayer * cPlayerManager::Find_Inactive_Player(const WideStringClass & name)
{
cPlayer * p_result = NULL;
for (
SLNode<cPlayer> * objnode = PlayerList.Head();
objnode != NULL;
objnode = objnode->Next())
{
cPlayer * p_player = objnode->Data();
WWASSERT(p_player != NULL);
if (p_player->Get_Is_Active().Is_False() &&
!p_player->Get_Name().Compare_No_Case(name)) {
p_result = p_player;
break;
}
}
return p_result;
}
//------------------------------------------------------------------------------------
cPlayer * cPlayerManager::Find_Team_Player(int team_number)
{
//WWASSERT(The_Game()->Is_Team_Game());
WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);
SLNode<cPlayer> * objnode;
for (objnode = PlayerList.Head(); objnode; objnode = objnode->Next()) {
cPlayer * p_player = objnode->Data();
WWASSERT(p_player != NULL);
if (p_player->Get_Is_Active().Is_True() &&
p_player->Get_Player_Type() == team_number) {
return p_player; // found it
}
}
return NULL; // Not found
}
//------------------------------------------------------------------------------------
cPlayer * cPlayerManager::Find_Random_Team_Player(int team_number)
{
//WWASSERT(The_Game()->Is_Team_Game());
WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);
int chosen_player = rand() % Tally_Team_Size(team_number);
int count = 0;
SLNode<cPlayer> * objnode;
for (objnode = PlayerList.Head(); objnode; objnode = objnode->Next()) {
cPlayer * p_player = objnode->Data();
WWASSERT(p_player != NULL);
if (p_player->Get_Is_Active().Is_True() &&
p_player->Get_Player_Type() == team_number &&
count++ == chosen_player) {
return p_player; // found it
}
}
return NULL; // Not found
}
//------------------------------------------------------------------------------------
cPlayer * cPlayerManager::Find_Team_Mate(cPlayer * p_player1)
{
WWASSERT(p_player1 != NULL);
WWASSERT(p_player1->Get_Is_Active().Is_True());
//WWASSERT(The_Game()->Is_Team_Game());
int team_number = p_player1->Get_Player_Type();
WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);
SLNode<cPlayer> * objnode;
for (objnode = PlayerList.Head(); objnode; objnode = objnode->Next()) {
cPlayer * p_player2 = objnode->Data();
WWASSERT(p_player2 != NULL);
if (p_player2->Get_Is_Active().Is_True() &&
p_player2->Get_Id() != p_player1->Get_Id() &&
p_player2->Get_Player_Type() == team_number) {
return p_player2; // found it
}
}
return NULL; // Not found
}
cPlayer* cPlayerManager::Find_Clan_Mate(cPlayer* player)
{
if (player != NULL) {
unsigned long clan = player->Get_WOL_ClanID();
int playerID = player->Get_Id();
SLNode<cPlayer>* node = PlayerList.Head();
while (node) {
cPlayer* mate = node->Data();
WWASSERT(mate != NULL);
if (playerID != mate->Get_Id()) {
if (clan == mate->Get_WOL_ClanID()) {
return mate;
}
}
node = node->Next();
}
}
return NULL;
}
//------------------------------------------------------------------------------------
bool cPlayerManager::Is_Player_Present(int id)
{
return (Find_Player(id) != NULL);
}
//------------------------------------------------------------------------------------
bool cPlayerManager::Is_Player_Present(WideStringClass & name)
{
return (Find_Player(name) != NULL);
}
//------------------------------------------------------------------------------------
const WideStringClass & cPlayerManager::Get_Player_Name(int id)
{
cPlayer * p_player = Find_Player(id);
WWASSERT(p_player != NULL);
WWASSERT(p_player->Get_Is_Active().Is_True());
return p_player->Get_Name();
}
//------------------------------------------------------------------------------------
void cPlayerManager::Add(cPlayer * p_player)
{
WWASSERT(p_player != NULL);
PlayerList.Add_Tail(p_player);
PlayerMgrEvent event(PLAYER_ADDED, p_player);
mNotifier.NotifyObservers(event);
}
//------------------------------------------------------------------------------------
void cPlayerManager::Remove(cPlayer * p_player)
{
WWASSERT(p_player != NULL);
PlayerList.Remove(p_player);
PlayerMgrEvent event(PLAYER_REMOVED, p_player);
mNotifier.NotifyObservers(event);
}
void cPlayerManager::Deactivated(cPlayer* p_player)
{
PlayerMgrEvent event(PLAYER_DEACTIVATED, p_player);
mNotifier.NotifyObservers(event);
}
void cPlayerManager::Activated(cPlayer* p_player)
{
PlayerMgrEvent event(PLAYER_ACTIVATED, p_player);
mNotifier.NotifyObservers(event);
}
//------------------------------------------------------------------------------------
int cPlayerManager::Get_Average_Ladder_Points(void)
{
int numPlayers = 0;
int totalPoints = 0;
SList<cPlayer>* playerList = Get_Player_Object_List();
SLNode<cPlayer>* playerNode = playerList->Head();
while (playerNode) {
cPlayer* player = playerNode->Data();
if (player && player->Get_Is_Active().Is_True() && player->Is_Human()) {
++numPlayers;
totalPoints += player->Get_Ladder_Points();
}
playerNode = playerNode->Next();
}
if (numPlayers) {
return totalPoints / numPlayers;
}
return 0;
}
//------------------------------------------------------------------------------------
unsigned short cPlayerManager::Get_Average_WOL_Points(void)
{
unsigned long numPlayers = 0;
unsigned long totalPoints = 0;
SList<cPlayer>* playerList = Get_Player_Object_List();
SLNode<cPlayer>* playerNode = playerList->Head();
while (playerNode) {
cPlayer* player = playerNode->Data();
if (player && player->Get_Is_Active().Is_True() && player->Is_Human()) {
++numPlayers;
totalPoints += player->Get_WOL_Points();
}
playerNode = playerNode->Next();
}
if (numPlayers) {
return (unsigned short)(totalPoints / numPlayers);
}
return 0;
}
//------------------------------------------------------------------------------------
int cPlayerManager::Get_Average_Games_Played(void)
{
int numPlayers = 0;
int totalPlayed = 0;
SList<cPlayer>* playerList = Get_Player_Object_List();
SLNode<cPlayer>* playerNode = playerList->Head();
while (playerNode) {
cPlayer* player = playerNode->Data();
if (player && player->Get_Is_Active().Is_True() && player->Is_Human()) {
++numPlayers;
totalPlayed += player->Get_Num_Wol_Games();
}
playerNode = playerNode->Next();
}
if (numPlayers) {
return (totalPlayed / numPlayers);
}
return 0;
}
//------------------------------------------------------------------------------------
int cPlayerManager::Get_Average_Ping(void)
{
int numPlayers = 0;
int totalPing = 0;
SList<cPlayer>* playerList = Get_Player_Object_List();
SLNode<cPlayer>* playerNode = playerList->Head();
while (playerNode) {
cPlayer* player = playerNode->Data();
//if (player && player->Get_Is_Active().Is_True() && player->Is_Human()) {
if (player && player->Get_Is_Active().Is_True() && player->Is_Human() && player->Get_Ping() >= 0) {
++numPlayers;
//totalPing += player->Get_Avg_Ping();
totalPing += player->Get_Ping();
}
playerNode = playerNode->Next();
}
if (numPlayers) {
return totalPing / numPlayers;
}
return 0;
}
//------------------------------------------------------------------------------------
int cPlayerManager::Get_Average_FPS(void)
{
int numPlayers = 0;
int totalFPS = 0;
SList<cPlayer>* playerList = Get_Player_Object_List();
SLNode<cPlayer>* playerNode = playerList->Head();
while (playerNode) {
cPlayer* player = playerNode->Data();
if (player && player->Get_Is_Active().Is_True() && player->Is_Human()) {
++numPlayers;
totalFPS += player->Get_Fps();
}
playerNode = playerNode->Next();
}
if (numPlayers) {
return totalFPS / numPlayers;
}
return 0;
}
//------------------------------------------------------------------------------------
static int Sum_Positions(int position)
{
WWASSERT(position >= 1);
int retval = 0;
for (int i = 0; i < position; i++) {
retval += i;
}
return retval;
}
//------------------------------------------------------------------------------------
void cPlayerManager::Compute_Ladder_Points(int winning_team)
{
WWASSERT(cNetwork::I_Am_Server());
WWASSERT(PTheGameData != NULL);
if (The_Game()->IsLaddered.Is_False()) {
return;
}
cPlayerManager::Sort_Players(false);
DWORD min_qualifying_time_ms = The_Game()->Get_Min_Qualifying_Time_Minutes() * 60 * 1000;
int i = 0;
//
// Count qualifying winners and losers
//
int num_winners = 0;
int num_losers = 0;
for (i = 0; i < MAX_PLAYERS; i++) {
if (Player_Array[i] == NULL ||
Player_Array[i]->Get_Total_Time() < min_qualifying_time_ms) {
continue;
}
if (Player_Array[i]->Get_Player_Type() == winning_team) {
num_winners++;
} else {
num_losers++;
}
}
int win_pos = num_winners;
int lose_pos = 1;
for (i = 0; i < MAX_PLAYERS; i++) {
if (Player_Array[i] == NULL ||
Player_Array[i]->Get_Total_Time() < min_qualifying_time_ms) {
continue;
}
if (Player_Array[i]->Get_Player_Type() == winning_team) {
Player_Array[i]->Set_Ladder_Points(Sum_Positions(win_pos));
win_pos--;
WWASSERT(win_pos >= 0);
} else {
Player_Array[i]->Set_Ladder_Points(-Sum_Positions(lose_pos));
lose_pos++;
}
}
//
// TSS121501
// Additional step. Let's now post-multiply ladder points by the fraction of
// gametime that each player was present.
//
float game_duration_s = The_Game()->Get_Game_Duration_S();
//WWASSERT(game_duration_s > 0);
if (game_duration_s == 0)
{
game_duration_s = 1;
}
for (i = 0; i < MAX_PLAYERS; i++) {
if (Player_Array[i] == NULL ||
Player_Array[i]->Get_Total_Time() < min_qualifying_time_ms) {
continue;
}
float player_duration_s = Player_Array[i]->Get_Total_Time() / 1000.0;
float ratio_present = player_duration_s / game_duration_s;
float ladder_points = ratio_present * Player_Array[i]->Get_Ladder_Points();
/*
WWDEBUG_SAY(("%5.2f * %-4d = %5.2f (%d)\n",
ratio_present,
Player_Array[i]->Get_Ladder_Points(),
ladder_points,
cMathUtil::Round(ladder_points)
));
*/
Player_Array[i]->Set_Ladder_Points(cMathUtil::Round(ladder_points));
}
//
// Do another sort so that the list is sorted on these newly calculated ladder points
//
cPlayerManager::Sort_Players(false);
}
//------------------------------------------------------------------------------------
void cPlayerManager::Increment_Player_Times(void)
{
WWASSERT(cNetwork::I_Am_Server());
//
// Increment the participation time of all active players
//
for (int i = 0; i < MAX_PLAYERS; i++) {
if (Player_Array[i] != NULL &&
Player_Array[i]->Is_Active()) {
Player_Array[i]->Increment_Total_Time();
}
}
}
//------------------------------------------------------------------------------------
WideStringClass cPlayerManager::Determine_Mvp_Name(void)
{
WWASSERT(cNetwork::I_Am_Server());
WideStringClass mvp_name;
WWASSERT(The_Game() != NULL);
DWORD min_qualifying_time_ms = The_Game()->Get_Min_Qualifying_Time_Minutes() * 60 * 1000;
//
// Find the top active, time-qualifying player.
// Player_Array has already been sorted on score.
//
for (int i = 0; i < MAX_PLAYERS; i++) {
if (Player_Array[i] != NULL) {
//Player_Array[i]->Increment_Total_Time();
if (Player_Array[i]->Is_Active() &&
Player_Array[i]->Get_Total_Time() > min_qualifying_time_ms) {
mvp_name = Player_Array[i]->Get_Name();
break;
}
}
}
return mvp_name;
}
//------------------------------------------------------------------------------------
int cPlayerManager::Compute_Full_Player_List_Height(void)
{
bool show_inactive = false;
#ifdef WWDEBUG
if (cDevOptions::ShowInactivePlayers.Is_True()) {
show_inactive = true;
}
#endif // WWDEBUG
int count = 0;
if (show_inactive) {
count = PlayerList.Get_Count();
} else {
count = Count();
}
WWASSERT(PFont != NULL);
int height = (int)((count + 1) * PFont->Char_Height() * Y_INCREMENT_FACTOR);
return height;
}
//------------------------------------------------------------------------------------
void cPlayerManager::Remove_Inactive(void)
{
for (
SLNode<cPlayer> * objnode = PlayerList.Head();
objnode != NULL;)
{
cPlayer * p_player = objnode->Data();
WWASSERT(p_player != NULL);
objnode = objnode->Next();
if (p_player->Get_Is_Active().Is_False())
{
PlayerList.Remove(p_player);
delete p_player;
}
}
}
//------------------------------------------------------------------------------------
void cPlayerManager::Remove_All(void)
{
WWDEBUG_SAY(("cPlayerManager::Remove_All\n"));
for (SLNode<cPlayer> * objnode = PlayerList.Head(); objnode != NULL;) {
cPlayer * p_player = objnode->Data();
WWASSERT(p_player != NULL);
objnode = objnode->Next();
PlayerList.Remove(p_player);
delete p_player;
}
WWASSERT(PlayerList.Get_Count() == 0);
//Remove_Inactive();
}
//-----------------------------------------------------------------------------
int cPlayerManager::Count(void)
{
int count = 0;
for (
SLNode<cPlayer> * objnode = PlayerList.Head();
objnode != NULL;
objnode = objnode->Next()) {
cPlayer * p_player = objnode->Data();
WWASSERT(p_player != NULL);
if (p_player->Get_Is_Active().Is_True())
{
count++;
}
}
return count;
}
//-----------------------------------------------------------------------------
void cPlayerManager::Reset_Players(void)
{
//
// Call reset on active players.
// Remove all inactive players.
//
Remove_Inactive();
for (
SLNode<cPlayer> * objnode = PlayerList.Head();
objnode != NULL;
objnode = objnode->Next()) {
cPlayer * p_player = objnode->Data();
WWASSERT(p_player != NULL);
p_player->Reset_Player();
}
}
//-----------------------------------------------------------------------------
int cPlayerManager::Tally_Team_Size(int type)
{
WWROOTPROFILE("Tally_Team_Size");
//WWASSERT(team >= 0 && team < MAX_TEAMS);
//WWASSERT(The_Game()->Is_Team_Game());
int tally = 0;
SLNode<cPlayer> * objnode;
cPlayer * p_player = NULL;
for (objnode = PlayerList.Head(); objnode != NULL; objnode = objnode->Next()) {
p_player = objnode->Data();
WWASSERT(p_player != NULL);
if (p_player->Get_Is_Active().Is_True() &&
p_player->Get_Player_Type() == type) {
tally++;
}
}
PlayerInfoLog::Report_Tally_Size(type,tally);
return tally;
}
//-----------------------------------------------------------------------------
bool cPlayerManager::Is_Kill_Treasonous(cPlayer * p_killer, cPlayer * p_victim)
{
return (
//The_Game()->Is_Team_Game() &&
//The_Game()->Is_Team_Game() &&
p_killer != NULL &&
p_victim != NULL &&
p_killer != p_victim &&
p_killer->Get_Player_Type() == p_victim->Get_Player_Type());
}
//-----------------------------------------------------------------------------
void cPlayerManager::Sort_Players(bool fast_sort)
{
WWPROFILE("cPlayerManager::Sort_Players");
ZeroMemory(Player_Array, sizeof(Player_Array));
//
// Copy non-spectators from SList into array usable by qsort
//
int num_players = 0;
//int active_players = 0;
cPlayer * p_player;
SLNode<cPlayer> * objnode;
for (objnode = Get_Player_Object_List()->Head(); objnode; objnode = objnode->Next()) {
p_player = objnode->Data();
WWASSERT(p_player != NULL);
//if (p_player->Is_Living()) {
//active_players++;
//}
WWASSERT(num_players < MAX_PLAYERS);
Player_Array[num_players] = p_player;
num_players++;
}
if (fast_sort) {
//
// If we're sorting the list in-game, use the fast sort
//
for (int i=0; i<num_players; i++) {
Player_Array[i]->Set_Fast_Sort_Key(Compute_Fast_Sort_Key(Player_Array[i]));
}
qsort(&Player_Array[0], num_players, sizeof(cPlayer *), &Fast_Player_Compare);
} else {
//
// Otherwise use the full-blown Player_Compare sort
//
qsort(&Player_Array[0], num_players, sizeof(cPlayer *), &Player_Compare);
}
//
// Set rungs. Only increment for active players.
//
/*
for (int index = 0; index < num_players; index ++) {
Player_Array[index]->Set_Rung (index + 1);
}
*/
int rung = 1;
for (int index = 0; index < num_players; index ++) {
Player_Array[index]->Set_Rung(rung);
if (Player_Array[index]->IsActive.Is_True()) {
rung++;
}
}
}
//-----------------------------------------------------------------------------
int cPlayerManager::Player_Compare(const void * elem1, const void * elem2)
{
//
// Used by qsort
// (gth) Note, this function is very expensive due to the number of
// data-safe variables being accessed.
//
WWASSERT(elem1 != NULL);
WWASSERT(elem2 != NULL);
cPlayer * p_player1 = *((cPlayer **)elem1);
cPlayer * p_player2 = *((cPlayer **)elem2);
WWASSERT(p_player1 != NULL);
WWASSERT(p_player2 != NULL);
int result;
int p1_type = p_player1->Get_Player_Type();
if (p1_type > PLAYERTYPE_NOD) {
p1_type = PLAYERTYPE_NOD;
}
int p2_type = p_player2->Get_Player_Type();
if (p2_type > PLAYERTYPE_NOD) {
p2_type = PLAYERTYPE_NOD;
}
//
// Sort on Ladder Points
//
if (p_player1->Get_Ladder_Points() > p_player2->Get_Ladder_Points()) {
result = -1;
} else if (p_player1->Get_Ladder_Points() == p_player2->Get_Ladder_Points()) {
//
// Sort on Score
//
if (p_player1->Get_Score() > p_player2->Get_Score()) {
result = -1;
} else if (p_player1->Get_Score() == p_player2->Get_Score()) {
//
// Sort on KTD ratio
//
//if (p_player1->Get_Kill_To_Death_Ratio() > p_player2->Get_Kill_To_Death_Ratio()) {
// result = -1;
//} else if (p_player1->Get_Kill_To_Death_Ratio() == p_player2->Get_Kill_To_Death_Ratio()) {
//
// Sort on Kills
//
if (p_player1->Get_Kills() > p_player2->Get_Kills()) {
result = -1;
} else if (p_player1->Get_Kills() == p_player2->Get_Kills()) {
//
// Sort on Deaths (less is better)
//
if (p_player1->Get_Deaths() < p_player2->Get_Deaths()) {
result = -1;
} else if (p_player1->Get_Deaths() == p_player2->Get_Deaths()) {
//
// Sort on team number. For non-teammembers this is a
// redundant but harmless comparison
//
if (p_player1->Get_Player_Type() < p_player2->Get_Player_Type()) {
result = -1;
} else if (p_player1->Get_Player_Type() == p_player2->Get_Player_Type()) {
//
// Sort lexicographically on name
//
//if (stricmp(p_player1->Get_Name(), p_player2->Get_Name()) < 0) {
if (p_player1->Get_Name() < p_player2->Get_Name()) {
result = -1;
//} else if (stricmp(p_player1->Get_Name(), p_player2->Get_Name()) == 0) {
} else if (p_player1->Get_Name() == p_player2->Get_Name()) {
//
// Enough!
//
result = 0;
} else {
result = 1;
}
} else {
result = 1;
}
} else {
result = 1;
}
} else {
result = 1;
}
//} else {
// result = 1;
//}
} else {
result = 1;
}
} else {
result = 1;
}
return result;
}
//-----------------------------------------------------------------------------
int cPlayerManager::Fast_Player_Compare(const void * elem1, const void * elem2)
{
//
// Used by qsort
//
WWASSERT(elem1 != NULL);
WWASSERT(elem2 != NULL);
cPlayer * p_player1 = *((cPlayer **)elem1);
cPlayer * p_player2 = *((cPlayer **)elem2);
WWASSERT(p_player1 != NULL);
WWASSERT(p_player2 != NULL);
if (p_player1->Get_Fast_Sort_Key() > p_player2->Get_Fast_Sort_Key()) {
return -1;
} else {
return 1;
}
}
//-----------------------------------------------------------------------------
int cPlayerManager::Compute_Fast_Sort_Key(cPlayer * player)
{
int key = 0;
key = player->Get_Score();
return key;
}
//-----------------------------------------------------------------------------
void cPlayerManager::Construct_Heading(WideStringClass & string, bool force_verbose)
{
string.Format(L"");
WWASSERT(PTheGameData != NULL);
//bool is_verbose = force_verbose || The_Game()->IsIntermission.Get() || MultiHUDClass::Get_Verbose_Lists();
bool is_verbose = force_verbose ||
The_Game()->IsIntermission.Is_True() ||
//MultiHUDClass::Get_Verbose_Lists();
(MultiHUDClass::Get_Playerlist_Format() == PLAYERLIST_FORMAT_FULL);
WideStringClass substring(0,true);
//
// Standing
//
substring.Format(L"%-5s", L"");
string += substring;
//
// Name
//
//GAMESPY
//substring.Format(L"%-11s", TRANSLATION(IDS_MP_PLAYER));
if (cGameSpyAdmin::Is_Gamespy_Game()) {
substring.Format(L"%-36s", TRANSLATION(IDS_MP_PLAYER));
} else {
substring.Format(L"%-11s", TRANSLATION(IDS_MP_PLAYER));
}
string += substring;
//
// Kills
//
if (is_verbose) {
substring.Format(L"%-8s", TRANSLATION(IDS_MP_KILLS));
string += substring;
}
//
// Deaths
//
if (is_verbose) {
substring.Format(L"%-8s", TRANSLATION(IDS_MP_DEATHS));
string += substring;
}
//
// Kill to Death ratio
//
if (is_verbose) {
substring.Format(L"%-8s", TRANSLATION(IDS_MP_KILL_TO_DEATH_RATIO));
string += substring;
}
//
// Money
//
if ((The_Game()->Is_Cnc() || The_Game()->Is_Skirmish()) && is_verbose) {
substring.Format(L"%-8s", TRANSLATION(IDS_MP_MONEY));
string += substring;
}
//
// Score
//
substring.Format(L"%-8s", TRANSLATION(IDS_MP_SCORE));
string += substring;
//
// Ladder Points
//
if (force_verbose && The_Game()->IsLaddered.Is_True()) {
substring.Format(L"%-8s", TRANSLATION(IDS_MP_LADDER));
string += substring;
}
//
// WOL rank
//
if (GameModeManager::Find("WOL")->Is_Active() && is_verbose) {
substring.Format(L"%-8s", TRANSLATION(IDS_MP_RANK));
string += substring;
}
#ifdef WWDEBUG
//
// Ping
//
if (cDevOptions::ShowPing.Is_True()) {
substring.Format(L"%-8s", L"Ping");
string += substring;
}
//
// Player Id
//
if (cDevOptions::ShowId.Is_True()) {
substring.Format(L"%-8s", L"Id");
string += substring;
}
//
// Fps
//
if (cNetwork::I_Am_Server() && cDevOptions::ShowClientFps.Is_True()) {
substring.Format(L"%-8s", L"Fps");
string += substring;
}
//
// GameSpy auth. state
//
if (cNetwork::I_Am_Server() && cGameSpyAdmin::Is_Gamespy_Game() &&
cDevOptions::ShowGameSpyAuthState.Is_True()) {
substring.Format(L"%-12s", L"GS_AUTH");
string += substring;
}
//
// IP Address
//
if (cNetwork::I_Am_Server() && cDevOptions::ShowIpAddresses.Is_True()) {
substring.Format(L"%-30s", L"IP Address");
string += substring;
}
#endif // WWDEBUG
}
//-----------------------------------------------------------------------------
void cPlayerManager::List_Print(WideStringClass & text, Vector3 color)
{
//WWASSERT(text != NULL);
//WWASSERT(::strlen(text) > 0);
if (PTextRenderer == NULL) {
return;
}
WWASSERT(PTextRenderer != NULL);
PTextRenderer->Set_Location(Vector2(cMathUtil::Round(XPos), cMathUtil::Round(YPos)));
int c = ((int)(color[0]*255)&0xFF) << 16 | ((int)(color[1]*255)&0xFF) << 8 | ((int)(color[2]*255)&0xFF) << 0 | 0xFF000000;
PTextRenderer->Draw_Text(text, c);
WWASSERT(PFont != NULL);
YPos += PFont->Char_Height() * Y_INCREMENT_FACTOR;
}
//-----------------------------------------------------------------------------
void cPlayerManager::Line(float x, float length, int line_color)
{
WWASSERT(length > 0);
if (PTextRenderer == NULL) {
return;
}
WWASSERT(PTextRenderer != NULL);
float y = YPos + PTextRenderer->Peek_Font()->Char_Height() / 2.0f;
PTextRenderer->Draw_Block(RectClass(x, y, x + length, y + 1), line_color);
}
//-----------------------------------------------------------------------------
void cPlayerManager::Render_Player_List(void)
{
//
// Format:
// AAAAAAAAAxxNNNNxxNNNxNNNxNNN.NxxNNNxxNNNNN, e.g:
// Charlie 220 25 13 5.3 3 12
//
// Fields are name, ping, kills, death, kills-to-deaths, num-lives, score
//
if (PTextRenderer == NULL) {
return;
}
WWASSERT(PTheGameData != NULL);
if (GameModeManager::Find("Combat") == NULL ||
!GameModeManager::Find("Combat")->Is_Active() ||
The_Game()->IsIntermission.Is_True()) {
PTextRenderer->Reset();
return;
}
//bool is_verbose = The_Game()->IsIntermission.Get() || MultiHUDClass::Get_Verbose_Lists();
bool is_verbose = The_Game()->IsIntermission.Get() || (MultiHUDClass::Get_Playerlist_Format() == PLAYERLIST_FORMAT_FULL);
bool changed=false;
static WideStringClass renderer_player_heading;
static WideStringClass renderer_player_strings[MAX_PLAYERS];
static Vector3 renderer_player_colors[MAX_PLAYERS];
static int renderer_player_count;
static int renderer_displayed_player_count;
static int renderer_star_index = -1;
static float renderer_star_x;
static float renderer_star_y;
static bool renderer_is_intermission_true;
static int LastSortedSecond = 0;
//
// Sort the list of players once a second
//
bool sort=is_verbose;
//bool sort = false;//XXX
int seconds = cMathUtil::Round(TimeManager::Get_Seconds());
if (!sort) {
if (seconds != LastSortedSecond) {
LastSortedSecond = seconds;
sort=true;
}
}
sort=true;//XXX
if (sort) {
WWPROFILE("Team & Player sorts");
cTeamManager::Sort_Teams();
cPlayerManager::Sort_Players(true);
}
//
// Build heading
//
WideStringClass heading(0,true);
Construct_Heading(heading);
if (heading!=renderer_player_heading) {
renderer_player_heading=heading;
changed=true;
}
if (The_Game()->IsIntermission.Is_True()!=renderer_is_intermission_true) {
changed=true;
renderer_is_intermission_true=The_Game()->IsIntermission.Is_True();
}
// Build player list
//int current_count = Count();
int current_count = PlayerList.Get_Count();
if (renderer_player_count!= current_count) {
renderer_player_count = current_count;
changed=true;
}
LPCSTR star_text = "*";
int displayed_player_count=0;
int count = current_count;
bool show_inactive = false;
#ifdef WWDEBUG
if (cDevOptions::ShowInactivePlayers.Is_True()) {
show_inactive = true;
}
#endif // WWDEBUG
for (int j = 0; j < count; j++) {
WWASSERT(j < MAX_PLAYERS);
cPlayer * p_player = Player_Array[j];
if (!p_player) continue;
//
// In the abbreviated form we only show the score leader and ourself.
// 09/26/01 And the guy at the bottom of the list !
// 10/29/01 Now we just show me.
//
bool is_me = cNetwork::I_Am_Client() && (p_player->Get_Id() == cNetwork::Get_My_Id());
//if (!is_verbose && (j != 0) && (j != count - 1) && !is_me) continue;
//if (!is_verbose && !is_me) {
if ((MultiHUDClass::Get_Playerlist_Format() == PLAYERLIST_FORMAT_TINY) && !is_me) {
continue;
}
if (p_player->Get_Is_Active().Is_False() && !show_inactive) {
continue;
}
if (changed) {
p_player->Get_Player_String(j + 1, renderer_player_strings[displayed_player_count]);
renderer_player_colors[displayed_player_count]=p_player->Get_Color();
}
else {
WideStringClass player_string(0,true);
p_player->Get_Player_String(j + 1, player_string);
if (player_string!=renderer_player_strings[displayed_player_count]) {
renderer_player_strings[displayed_player_count]=player_string;
changed=true;
}
// If list hasn't changed so far, check if the player color is the same
if (renderer_player_colors[displayed_player_count]!=p_player->Get_Color()) {
renderer_player_colors[displayed_player_count]=p_player->Get_Color();
changed=true;
}
}
if (cNetwork::I_Am_Client() && p_player->Get_Id() == cNetwork::Get_My_Id()) {
//
// Put a symbol next to my name so that it stands out
//
if (renderer_star_index!=displayed_player_count) {
renderer_star_index=displayed_player_count;
changed=true;
}
}
displayed_player_count++;
}
if (renderer_displayed_player_count!=displayed_player_count) {
renderer_displayed_player_count=displayed_player_count;
changed=true;
}
if (!changed) return;
PTextRenderer->Reset();
WWASSERT(PFont != NULL);
WWASSERT(PTextRenderer != NULL);
DEMO_SECURITY_CHECK;
//
// Determine left edge placement
//
XPos = 0;
float text_len = PFont->String_Width(heading);
if (The_Game()->IsIntermission.Is_True()) {
XPos = Render2DClass::Get_Screen_Resolution().Center().X - text_len / 2.0f;
} else {
XPos = Render2DClass::Get_Screen_Resolution().Right - 20 - text_len;
}
//
// Top placement
//
if (The_Game()->IsIntermission.Is_True()) {
//YPos = 70;
int combined_height =
cTeamManager::Compute_Team_List_Height() +
cPlayerManager::Compute_Full_Player_List_Height() +
2 * PFont->Char_Height() * Y_INCREMENT_FACTOR;
YPos = (int)(Render2DClass::Get_Screen_Resolution().Height() / 2.0 - combined_height / 2.0);
if (YPos < 10) {
YPos = 10;
}
/*
if (The_Game()->Is_Team_Game()) {
YPos += 2 * PFont->Char_Height() * Y_INCREMENT_FACTOR;
YPos += cTeamManager::Compute_Team_List_Height();
} else {
//
// Show the win text
//
WideStringClass text(cGameData::Get_Win_Text(),true);
float x = Render2DClass::Get_Screen_Resolution().Center().X -
PFont->String_Width(text) / 2.0f;
float y = YPos;
PTextRenderer->Set_Location(Vector2(cMathUtil::Round(x), cMathUtil::Round(y)));
PTextRenderer->Draw_Text(text);
YPos += 2 * PFont->Char_Height() * Y_INCREMENT_FACTOR;
}
*/
YPos += 2 * PFont->Char_Height() * Y_INCREMENT_FACTOR;
YPos += cTeamManager::Compute_Team_List_Height();
} else {
//YPos = 50;
/*
YPos = 10;
if (The_Game()->Is_Team_Game()) {
//YPos += 4 * PFont->Char_Height() * Y_INCREMENT_FACTOR;
YPos += cTeamManager::Compute_Team_List_Height();
}
*/
YPos = 10 + cTeamManager::Compute_Team_List_Height();
}
//
// Draw the heading
//
/*
if (The_Game()->Is_Team_Game()) {
List_Print(heading, Vector3(1, 1, 1));
} else {
List_Print(heading, Vector3(1, 1, 0));
}
*/
List_Print(heading, Vector3(1, 1, 1));
for (j = 0; j < renderer_displayed_player_count; j++) {
//
// Put a symbol next to my name so that it stands out
//
//if (is_verbose && renderer_star_index==j) {
if ((MultiHUDClass::Get_Playerlist_Format() != PLAYERLIST_FORMAT_TINY) &&
(renderer_star_index == j)) {
float x = XPos - 2 * PFont->String_Width(star_text);
float y = YPos;
PTextRenderer->Set_Location(Vector2(cMathUtil::Round(x), cMathUtil::Round(y)));
PTextRenderer->Draw_Text(star_text);
}
List_Print(renderer_player_strings[j], renderer_player_colors[j]);
}
}
//-----------------------------------------------------------------------------
void cPlayerManager::Log_Player_List(void)
{
WWDEBUG_SAY(("cPlayerManager::Log_Player_List\n"));
StringClass results_filename;
results_filename.Format("results%d.txt", cUserOptions::ResultsLogNumber.Get());
FILE * file = ::fopen(results_filename, "at");
if (file != NULL) {
char line[2000] = "";
WideStringClass wide_text(0, true);
Construct_Heading(wide_text, true);
StringClass text;
wide_text.Convert_To(text);
::sprintf(line, "%s\n", text.Peek_Buffer());
::fwrite(line, 1, ::strlen(line), file);
for (int j = 0; j < MAX_PLAYERS; j++) {
cPlayer * p_player = Player_Array[j];
if (p_player != NULL) {
p_player->Get_Player_String(j + 1, wide_text, true);
wide_text.Convert_To(text);
::sprintf(line, "%s\n", text.Peek_Buffer());
::fwrite(line, 1, ::strlen(line), file);
}
}
::sprintf(line, "\n");
::fwrite(line, 1, ::strlen(line), file);
::fclose(file);
}
}
//-----------------------------------------------------------------------------
enum {
CHUNKID_PLAYERLIST = 1232345,
CHUNKID_PLAYER,
};
//-----------------------------------------------------------------------------
bool cPlayerManager::Save(ChunkSaveClass & csave)
{
csave.Begin_Chunk(CHUNKID_PLAYERLIST);
SLNode<cPlayer> * objnode;
for (objnode = PlayerList.Head(); objnode; objnode = objnode->Next()) {
cPlayer * p_player = objnode->Data();
WWASSERT(p_player != NULL);
csave.Begin_Chunk(CHUNKID_PLAYER);
p_player->Save(csave);
csave.End_Chunk();
}
csave.End_Chunk();
return true;
}
//-----------------------------------------------------------------------------
bool cPlayerManager::Load(ChunkLoadClass &cload)
{
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PLAYERLIST:
//Remove_All(); // TSS091401
//TSS110101. WWASSERT(PlayerList.Get_Count() == 0);
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_PLAYER:
cPlayer * p_player;
p_player = new cPlayer();
p_player->Load(cload);
/*
if (!The_Game()->Is_Valid_Player_Type(p_player->Get_Player_Type())) {
Debug_Say(("* Removing loaded player due to invalid playertype for this game.\n"));
Remove(p_player);
}
*/
//
// TSS110101.
// Do not perturb the save/load code, but nullify it's effect.
//
if ( !IS_MISSION ) {
Remove(p_player);
}
break;
default:
Debug_Say(( "Unrecognized cPlayerManager chunkID\n" ));
break;
}
cload.Close_Chunk();
}
break;
default:
Debug_Say(( "Unrecognized cPlayerManager chunkID\n" ));
break;
}
cload.Close_Chunk();
}
return true;
}
//-----------------------------------------------------------------------------