/* ** 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 . */ /*********************************************************************************************** *** 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 #include #include #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 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 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 * 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 * 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 * 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 * 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 * 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 * 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* 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* playerList = Get_Player_Object_List(); SLNode* 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* playerList = Get_Player_Object_List(); SLNode* 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* playerList = Get_Player_Object_List(); SLNode* 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* playerList = Get_Player_Object_List(); SLNode* 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* playerList = Get_Player_Object_List(); SLNode* 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 * 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 * 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 * 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 * 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 * 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 * 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; iSet_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 * 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; } //-----------------------------------------------------------------------------