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/teammanager.cpp

661 lines
17 KiB
C++

/*
** 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/teammanager.cpp $*
* *
* $Author:: Tom_s $*
* *
* $Modtime:: 2/22/02 11:29a $*
* *
* $Revision:: 73 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "teammanager.h" // I WANNA BE FIRST!
#include <stdio.h>
#include "miscutil.h"
#include "_globals.h"
#include "assets.h"
#include "cnetwork.h"
//#include "gamesettings.h"
#include "multihud.h"
#include <win.h>
#include "player.h"
#include "font3d.h"
#include "gamedata.h"
#include "useroptions.h"
#include "smartgameobj.h"
#include "playertype.h"
#include "colors.h"
#include "render2d.h"
#include "wwprofile.h"
#include "gametype.h"
#include "playermanager.h"
#include "translatedb.h"
#include "string_ids.h"
#include "consolemode.h"
#include "gamespyadmin.h"
//
// Class statics
//
SList<cTeam> cTeamManager::TeamList;
cTeam * cTeamManager::Team_Array[];
float cTeamManager::TeamListHeight = 0;
Render2DTextClass * cTeamManager::PTextRenderer = NULL;
Font3DInstanceClass * cTeamManager::PFont = NULL;
const float cTeamManager::Y_INCREMENT_FACTOR = 1.2f;
int cTeamManager::XPos = 0;
int cTeamManager::YPos = 0;
static WideStringClass renderer_team_heading;
static WideStringClass renderer_team_strings[MAX_TEAMS];
static Vector3 renderer_team_colors[MAX_TEAMS];
static bool renderer_is_intermission_true;
//------------------------------------------------------------------------------------
void cTeamManager::Onetime_Init(void)
{
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());
}
renderer_team_heading=L"";
renderer_is_intermission_true=false;
}
//------------------------------------------------------------------------------------
void cTeamManager::Onetime_Shutdown(void)
{
if (!ConsoleBox.Is_Exclusive()) {
WWASSERT(PTextRenderer != NULL);
delete PTextRenderer;
PTextRenderer = NULL;
WWASSERT(PFont != NULL);
PFont->Release_Ref();
PFont = NULL;
}
}
//------------------------------------------------------------------------------------
void cTeamManager::Think(void)
{
WWPROFILE( "cTeamManager::Think" );
if (PTextRenderer == NULL) {
return;
}
if (MultiHUDClass::Is_On()) {
WWASSERT(PTextRenderer != NULL);
Render_Team_List();
}
else {
// This will make sure the team list disappears if it is not displayed
if (PTextRenderer) PTextRenderer->Reset();
}
}
//-----------------------------------------------------------------------------------
void cTeamManager::Render(void)
{
WWPROFILE("cTeamManager::Render");
if (PTextRenderer == NULL) {
return;
}
WWASSERT(PTextRenderer != NULL);
PTextRenderer->Render();
}
//------------------------------------------------------------------------------------
cTeam * cTeamManager::Find_Team(int team_number)
{
WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);
SLNode<cTeam> * objnode;
cTeam *obj;
for (objnode = TeamList.Head(); objnode; objnode = objnode->Next()) {
obj = objnode->Data();
WWASSERT(obj != NULL);
if (obj->Get_Id() == team_number) {
return obj; // found it
}
}
return NULL; // Not found
}
cTeam* cTeamManager::Find_Empty_Team(void)
{
SLNode<cTeam>* node = TeamList.Head();
while (node) {
cTeam* team = node->Data();
if (team && (team->Tally_Size() == 0)) {
return team;
}
node = node->Next();
}
return NULL;
}
//------------------------------------------------------------------------------------
void cTeamManager::Add(cTeam * p_team)
{
WWASSERT(p_team != NULL);
TeamList.Add_Tail(p_team);
}
//------------------------------------------------------------------------------------
void cTeamManager::Remove(cTeam * p_team)
{
WWASSERT(p_team != NULL);
TeamList.Remove(p_team);
delete p_team;
}
//------------------------------------------------------------------------------------
void cTeamManager::Remove(int team_number)
{
WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);
cTeam * p_team = Find_Team(team_number);
if (p_team != NULL) {
Remove(p_team);
}
}
//------------------------------------------------------------------------------------
void cTeamManager::Remove_All(void)
{
for (SLNode<cTeam> * objnode = TeamList.Head(); objnode != NULL;) {
cTeam * p_team = objnode->Data();
objnode = objnode->Next();
Remove(p_team);
}
}
//------------------------------------------------------------------------------------
int cTeamManager::Compute_Team_List_Height(void)
{
/*
int height = 0;
if (The_Game()->Is_Team_Game()) {
WWASSERT(PFont != NULL);
height = (int)(4 * PFont->Char_Height() * Y_INCREMENT_FACTOR);
}
*/
WWASSERT(PFont != NULL);
int height = (int)(4 * PFont->Char_Height() * Y_INCREMENT_FACTOR);
return height;
}
//-----------------------------------------------------------------------------
void cTeamManager::Log_Team_List(void)
{
WWDEBUG_SAY(("cTeamManager::Log_Team_List\n"));
StringClass results_filename;
results_filename.Format("results%d.txt", cUserOptions::ResultsLogNumber.Get());
FILE * file = ::fopen(results_filename, "wt");
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_TEAMS; j++) {
cTeam * p_team = Team_Array[j];
if (p_team != NULL) {
//p_team->Get_Player_String(j + 1, wide_text, true);
p_team->Get_Team_String(j + 1, wide_text);
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);
}
}
//-----------------------------------------------------------------------------
WideStringClass cTeamManager::Get_Team_Name(int team_number)
{
WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);
cTeam * p_team = cTeamManager::Find_Team(team_number);
WWASSERT(p_team != NULL);
return p_team->Get_Name();
}
//-----------------------------------------------------------------------------
void cTeamManager::Reset_Teams(void)
{
SLNode<cTeam> * objnode;
cTeam * p_team = NULL;
for (objnode = TeamList.Head(); objnode != NULL; objnode = objnode->Next()) {
p_team = objnode->Data();
WWASSERT(p_team != NULL);
p_team->Reset();
}
}
//-----------------------------------------------------------------------------
int cTeamManager::Get_Non_Empty_Team_Count(void)
{
int num_teams = 0;
cTeam * p_team;
for (SLNode<cTeam> * objnode = TeamList.Head(); objnode != NULL; objnode = objnode->Next()) {
p_team = objnode->Data();
WWASSERT(p_team != NULL);
if (p_team->Tally_Size() > 0) {
num_teams++;
}
}
return num_teams;
}
//-----------------------------------------------------------------------------
void cTeamManager::Sort_Teams(void)
{
WWPROFILE("cTeamManager::Sort_Teams");
ZeroMemory(Team_Array, sizeof(Team_Array));
int num_teams = 0;
cTeam * p_team;
for (SLNode<cTeam> * objnode = TeamList.Head(); objnode != NULL; objnode = objnode->Next()) {
p_team = objnode->Data();
WWASSERT(p_team != NULL);
WWASSERT(num_teams < MAX_TEAMS);
Team_Array[num_teams] = p_team;
num_teams++;
}
qsort(&Team_Array[0], num_teams, sizeof(cTeam *), &Team_Compare);
}
//-----------------------------------------------------------------------------
int cTeamManager::Get_Leaders_Id(void)
{
WWASSERT(Team_Array[0] != NULL);
return Team_Array[0]->Get_Id();
}
//-----------------------------------------------------------------------------
int cTeamManager::Sort_Score_Ktd_Kills(cTeam * p_team1, cTeam * p_team2)
{
WWASSERT(p_team1 != NULL);
WWASSERT(p_team2 != NULL);
int result;
if (p_team1->Get_Score() > p_team2->Get_Score()) {
result = -1;
} else if (p_team1->Get_Score() == p_team2->Get_Score()) {
//if (p_team1->Get_Kill_To_Death_Ratio() > p_team2->Get_Kill_To_Death_Ratio()) {
// result = -1;
//} else if (p_team1->Get_Kill_To_Death_Ratio() == p_team2->Get_Kill_To_Death_Ratio()) {
if (p_team1->Get_Kills() > p_team2->Get_Kills()) {
result = -1;
} else if (p_team1->Get_Kills() == p_team2->Get_Kills()) {
//
// OK, give up, they are equal
//
result = 0;
} else {
result = 1;
}
//} else {
// result = 1;
//}
} else {
result = 1;
}
return result;
}
//-----------------------------------------------------------------------------
int cTeamManager::Team_Compare(const void * elem1, const void * elem2)
{
//
// Used by qsort
//
WWASSERT(elem1 != NULL);
WWASSERT(elem2 != NULL);
cTeam * p_team1 = *((cTeam **)elem1);
cTeam * p_team2 = *((cTeam **)elem2);
WWASSERT(p_team1 != NULL);
WWASSERT(p_team2 != NULL);
//
// Sort order:
// 1. If there is a life limit, sort on lives.
// 2. Sort on score.
// 3. Sort on KTD ratio.
// 4. Sort on Kills
//
int result = Sort_Score_Ktd_Kills(p_team1, p_team2);
return result;
}
//-----------------------------------------------------------------------------
void cTeamManager::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 cTeamManager::Construct_Heading(WideStringClass & string)
{
string.Format(L"");
WWASSERT(PTheGameData != NULL);
//bool is_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);
//
// Num Players
//
substring.Format(L"%-3s", L"");
string += substring;
/*
//
// Standing
//
if (is_verbose) {
substring.Format(L"%-5s", L"");
string += substring;
}
*/
//
// "Team" or "Clan"
//
//GAMESPY
//substring.Format(L"%-11s", The_Game()->Get_Team_Word());
if (cGameSpyAdmin::Is_Gamespy_Game()) {
substring.Format(L"%-36s", The_Game()->Get_Team_Word());
} else {
substring.Format(L"%-11s", The_Game()->Get_Team_Word());
}
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;
}
//-----------------------------------------------------------------------------
void cTeamManager::Render_Team_List(void)
{
if (PTextRenderer == NULL) {
return;
}
WWASSERT(PFont != NULL);
WWASSERT(PTextRenderer != NULL);
WWASSERT(PTheGameData != NULL);
if (GameModeManager::Find("Combat") == NULL ||
!GameModeManager::Find("Combat")->Is_Active() ||
The_Game()->IsIntermission.Is_True()) {
PTextRenderer->Reset();
return;
}
//
// Format:
// AAAAAAAAAxxNNNNxxNNNxNNNxNNNxNxxNNNxxNNNNN, e.g:
// Charlie 5 25 13 1.5 12 1203
//
// Fields are name, num players, kills, death, kills-to-deaths, num-lives, score
//
/*
if (!The_Game()->Is_Team_Game()) {
PTextRenderer->Reset();
return;
}
*/
/*
//
// Count the non-empty teams
//
int num_teams = Get_Non_Empty_Team_Count();
if (num_teams == 0) {
return;
}
*/
bool changed=false;
//
// Build heading
//
WideStringClass heading(0,true);
Construct_Heading(heading);
if (heading!=renderer_team_heading) {
renderer_team_heading=heading;
changed=true;
}
// Verify that the team list is valid
for (int j = 0; j < MAX_TEAMS; j++) {
if (!Team_Array[j]) {
Sort_Teams();
break;
}
}
for (j = 0; j < MAX_TEAMS; j++) {
cTeam * p_team = Team_Array[j];
WWASSERT(p_team != NULL);
renderer_team_colors[j]=p_team->Get_Color();
if (changed) {
p_team->Get_Team_String(j + 1, renderer_team_strings[j]);
}
else {
WideStringClass team_string(0,true);
p_team->Get_Team_String(j + 1, team_string);
if (team_string!=renderer_team_strings[j]) {
changed=true;
renderer_team_strings[j]=team_string;
}
}
}
WWASSERT(PTheGameData != NULL);
if (The_Game()->IsIntermission.Is_True()!=renderer_is_intermission_true) {
renderer_is_intermission_true=The_Game()->IsIntermission.Is_True();
changed=true;
}
if (!changed) return;
PTextRenderer->Reset();
//
// 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;
}
YPos = 10;
//
// Show Win text
//
WWASSERT(PTheGameData != NULL);
if (The_Game()->IsIntermission.Is_True()) {
int combined_height =
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;
}
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;
}
//
// Draw the teamlist heading
//
List_Print(heading, Vector3(1, 1, 1));
for (j = 0; j < MAX_TEAMS; j++) {
List_Print(renderer_team_strings[j], renderer_team_colors[j]);
}
}