teammanager.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Commando *
  23. * *
  24. * $Archive:: /Commando/Code/Commando/teammanager.cpp $*
  25. * *
  26. * $Author:: Tom_s $*
  27. * *
  28. * $Modtime:: 2/22/02 11:29a $*
  29. * *
  30. * $Revision:: 73 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "teammanager.h" // I WANNA BE FIRST!
  36. #include <stdio.h>
  37. #include "miscutil.h"
  38. #include "_globals.h"
  39. #include "assets.h"
  40. #include "cnetwork.h"
  41. //#include "gamesettings.h"
  42. #include "multihud.h"
  43. #include <win.h>
  44. #include "player.h"
  45. #include "font3d.h"
  46. #include "gamedata.h"
  47. #include "useroptions.h"
  48. #include "smartgameobj.h"
  49. #include "playertype.h"
  50. #include "colors.h"
  51. #include "render2d.h"
  52. #include "wwprofile.h"
  53. #include "gametype.h"
  54. #include "playermanager.h"
  55. #include "translatedb.h"
  56. #include "string_ids.h"
  57. #include "consolemode.h"
  58. #include "gamespyadmin.h"
  59. //
  60. // Class statics
  61. //
  62. SList<cTeam> cTeamManager::TeamList;
  63. cTeam * cTeamManager::Team_Array[];
  64. float cTeamManager::TeamListHeight = 0;
  65. Render2DTextClass * cTeamManager::PTextRenderer = NULL;
  66. Font3DInstanceClass * cTeamManager::PFont = NULL;
  67. const float cTeamManager::Y_INCREMENT_FACTOR = 1.2f;
  68. int cTeamManager::XPos = 0;
  69. int cTeamManager::YPos = 0;
  70. static WideStringClass renderer_team_heading;
  71. static WideStringClass renderer_team_strings[MAX_TEAMS];
  72. static Vector3 renderer_team_colors[MAX_TEAMS];
  73. static bool renderer_is_intermission_true;
  74. //------------------------------------------------------------------------------------
  75. void cTeamManager::Onetime_Init(void)
  76. {
  77. if (!ConsoleBox.Is_Exclusive()) {
  78. WWASSERT(PFont == NULL);
  79. WWASSERT(WW3DAssetManager::Get_Instance() != NULL);
  80. PFont = WW3DAssetManager::Get_Instance()->Get_Font3DInstance("FONT6x8.TGA");
  81. WWASSERT(PFont != NULL);
  82. PFont->Set_Mono_Spaced();
  83. SET_REF_OWNER(PFont);
  84. WWASSERT(PTextRenderer == NULL);
  85. PTextRenderer = new Render2DTextClass(PFont);
  86. WWASSERT(PTextRenderer != NULL);
  87. PTextRenderer->Set_Coordinate_Range(Render2DClass::Get_Screen_Resolution());
  88. }
  89. renderer_team_heading=L"";
  90. renderer_is_intermission_true=false;
  91. }
  92. //------------------------------------------------------------------------------------
  93. void cTeamManager::Onetime_Shutdown(void)
  94. {
  95. if (!ConsoleBox.Is_Exclusive()) {
  96. WWASSERT(PTextRenderer != NULL);
  97. delete PTextRenderer;
  98. PTextRenderer = NULL;
  99. WWASSERT(PFont != NULL);
  100. PFont->Release_Ref();
  101. PFont = NULL;
  102. }
  103. }
  104. //------------------------------------------------------------------------------------
  105. void cTeamManager::Think(void)
  106. {
  107. WWPROFILE( "cTeamManager::Think" );
  108. if (PTextRenderer == NULL) {
  109. return;
  110. }
  111. if (MultiHUDClass::Is_On()) {
  112. WWASSERT(PTextRenderer != NULL);
  113. Render_Team_List();
  114. }
  115. else {
  116. // This will make sure the team list disappears if it is not displayed
  117. if (PTextRenderer) PTextRenderer->Reset();
  118. }
  119. }
  120. //-----------------------------------------------------------------------------------
  121. void cTeamManager::Render(void)
  122. {
  123. WWPROFILE("cTeamManager::Render");
  124. if (PTextRenderer == NULL) {
  125. return;
  126. }
  127. WWASSERT(PTextRenderer != NULL);
  128. PTextRenderer->Render();
  129. }
  130. //------------------------------------------------------------------------------------
  131. cTeam * cTeamManager::Find_Team(int team_number)
  132. {
  133. WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);
  134. SLNode<cTeam> * objnode;
  135. cTeam *obj;
  136. for (objnode = TeamList.Head(); objnode; objnode = objnode->Next()) {
  137. obj = objnode->Data();
  138. WWASSERT(obj != NULL);
  139. if (obj->Get_Id() == team_number) {
  140. return obj; // found it
  141. }
  142. }
  143. return NULL; // Not found
  144. }
  145. cTeam* cTeamManager::Find_Empty_Team(void)
  146. {
  147. SLNode<cTeam>* node = TeamList.Head();
  148. while (node) {
  149. cTeam* team = node->Data();
  150. if (team && (team->Tally_Size() == 0)) {
  151. return team;
  152. }
  153. node = node->Next();
  154. }
  155. return NULL;
  156. }
  157. //------------------------------------------------------------------------------------
  158. void cTeamManager::Add(cTeam * p_team)
  159. {
  160. WWASSERT(p_team != NULL);
  161. TeamList.Add_Tail(p_team);
  162. }
  163. //------------------------------------------------------------------------------------
  164. void cTeamManager::Remove(cTeam * p_team)
  165. {
  166. WWASSERT(p_team != NULL);
  167. TeamList.Remove(p_team);
  168. delete p_team;
  169. }
  170. //------------------------------------------------------------------------------------
  171. void cTeamManager::Remove(int team_number)
  172. {
  173. WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);
  174. cTeam * p_team = Find_Team(team_number);
  175. if (p_team != NULL) {
  176. Remove(p_team);
  177. }
  178. }
  179. //------------------------------------------------------------------------------------
  180. void cTeamManager::Remove_All(void)
  181. {
  182. for (SLNode<cTeam> * objnode = TeamList.Head(); objnode != NULL;) {
  183. cTeam * p_team = objnode->Data();
  184. objnode = objnode->Next();
  185. Remove(p_team);
  186. }
  187. }
  188. //------------------------------------------------------------------------------------
  189. int cTeamManager::Compute_Team_List_Height(void)
  190. {
  191. /*
  192. int height = 0;
  193. if (The_Game()->Is_Team_Game()) {
  194. WWASSERT(PFont != NULL);
  195. height = (int)(4 * PFont->Char_Height() * Y_INCREMENT_FACTOR);
  196. }
  197. */
  198. WWASSERT(PFont != NULL);
  199. int height = (int)(4 * PFont->Char_Height() * Y_INCREMENT_FACTOR);
  200. return height;
  201. }
  202. //-----------------------------------------------------------------------------
  203. void cTeamManager::Log_Team_List(void)
  204. {
  205. WWDEBUG_SAY(("cTeamManager::Log_Team_List\n"));
  206. StringClass results_filename;
  207. results_filename.Format("results%d.txt", cUserOptions::ResultsLogNumber.Get());
  208. FILE * file = ::fopen(results_filename, "wt");
  209. if (file != NULL) {
  210. char line[2000] = "";
  211. WideStringClass wide_text(0, true);
  212. Construct_Heading(wide_text);//, true);
  213. StringClass text;
  214. wide_text.Convert_To(text);
  215. ::sprintf(line, "%s\n", text.Peek_Buffer());
  216. ::fwrite(line, 1, ::strlen(line), file);
  217. for (int j = 0; j < MAX_TEAMS; j++) {
  218. cTeam * p_team = Team_Array[j];
  219. if (p_team != NULL) {
  220. //p_team->Get_Player_String(j + 1, wide_text, true);
  221. p_team->Get_Team_String(j + 1, wide_text);
  222. wide_text.Convert_To(text);
  223. ::sprintf(line, "%s\n", text.Peek_Buffer());
  224. ::fwrite(line, 1, ::strlen(line), file);
  225. }
  226. }
  227. ::sprintf(line, "\n");
  228. ::fwrite(line, 1, ::strlen(line), file);
  229. ::fclose(file);
  230. }
  231. }
  232. //-----------------------------------------------------------------------------
  233. WideStringClass cTeamManager::Get_Team_Name(int team_number)
  234. {
  235. WWASSERT(team_number >= 0 && team_number < MAX_TEAMS);
  236. cTeam * p_team = cTeamManager::Find_Team(team_number);
  237. WWASSERT(p_team != NULL);
  238. return p_team->Get_Name();
  239. }
  240. //-----------------------------------------------------------------------------
  241. void cTeamManager::Reset_Teams(void)
  242. {
  243. SLNode<cTeam> * objnode;
  244. cTeam * p_team = NULL;
  245. for (objnode = TeamList.Head(); objnode != NULL; objnode = objnode->Next()) {
  246. p_team = objnode->Data();
  247. WWASSERT(p_team != NULL);
  248. p_team->Reset();
  249. }
  250. }
  251. //-----------------------------------------------------------------------------
  252. int cTeamManager::Get_Non_Empty_Team_Count(void)
  253. {
  254. int num_teams = 0;
  255. cTeam * p_team;
  256. for (SLNode<cTeam> * objnode = TeamList.Head(); objnode != NULL; objnode = objnode->Next()) {
  257. p_team = objnode->Data();
  258. WWASSERT(p_team != NULL);
  259. if (p_team->Tally_Size() > 0) {
  260. num_teams++;
  261. }
  262. }
  263. return num_teams;
  264. }
  265. //-----------------------------------------------------------------------------
  266. void cTeamManager::Sort_Teams(void)
  267. {
  268. WWPROFILE("cTeamManager::Sort_Teams");
  269. ZeroMemory(Team_Array, sizeof(Team_Array));
  270. int num_teams = 0;
  271. cTeam * p_team;
  272. for (SLNode<cTeam> * objnode = TeamList.Head(); objnode != NULL; objnode = objnode->Next()) {
  273. p_team = objnode->Data();
  274. WWASSERT(p_team != NULL);
  275. WWASSERT(num_teams < MAX_TEAMS);
  276. Team_Array[num_teams] = p_team;
  277. num_teams++;
  278. }
  279. qsort(&Team_Array[0], num_teams, sizeof(cTeam *), &Team_Compare);
  280. }
  281. //-----------------------------------------------------------------------------
  282. int cTeamManager::Get_Leaders_Id(void)
  283. {
  284. WWASSERT(Team_Array[0] != NULL);
  285. return Team_Array[0]->Get_Id();
  286. }
  287. //-----------------------------------------------------------------------------
  288. int cTeamManager::Sort_Score_Ktd_Kills(cTeam * p_team1, cTeam * p_team2)
  289. {
  290. WWASSERT(p_team1 != NULL);
  291. WWASSERT(p_team2 != NULL);
  292. int result;
  293. if (p_team1->Get_Score() > p_team2->Get_Score()) {
  294. result = -1;
  295. } else if (p_team1->Get_Score() == p_team2->Get_Score()) {
  296. //if (p_team1->Get_Kill_To_Death_Ratio() > p_team2->Get_Kill_To_Death_Ratio()) {
  297. // result = -1;
  298. //} else if (p_team1->Get_Kill_To_Death_Ratio() == p_team2->Get_Kill_To_Death_Ratio()) {
  299. if (p_team1->Get_Kills() > p_team2->Get_Kills()) {
  300. result = -1;
  301. } else if (p_team1->Get_Kills() == p_team2->Get_Kills()) {
  302. //
  303. // OK, give up, they are equal
  304. //
  305. result = 0;
  306. } else {
  307. result = 1;
  308. }
  309. //} else {
  310. // result = 1;
  311. //}
  312. } else {
  313. result = 1;
  314. }
  315. return result;
  316. }
  317. //-----------------------------------------------------------------------------
  318. int cTeamManager::Team_Compare(const void * elem1, const void * elem2)
  319. {
  320. //
  321. // Used by qsort
  322. //
  323. WWASSERT(elem1 != NULL);
  324. WWASSERT(elem2 != NULL);
  325. cTeam * p_team1 = *((cTeam **)elem1);
  326. cTeam * p_team2 = *((cTeam **)elem2);
  327. WWASSERT(p_team1 != NULL);
  328. WWASSERT(p_team2 != NULL);
  329. //
  330. // Sort order:
  331. // 1. If there is a life limit, sort on lives.
  332. // 2. Sort on score.
  333. // 3. Sort on KTD ratio.
  334. // 4. Sort on Kills
  335. //
  336. int result = Sort_Score_Ktd_Kills(p_team1, p_team2);
  337. return result;
  338. }
  339. //-----------------------------------------------------------------------------
  340. void cTeamManager::List_Print(WideStringClass & text, Vector3 color)
  341. {
  342. //WWASSERT(text != NULL);
  343. //WWASSERT(::strlen(text) > 0);
  344. if (PTextRenderer == NULL) {
  345. return;
  346. }
  347. WWASSERT(PTextRenderer != NULL);
  348. PTextRenderer->Set_Location(Vector2(cMathUtil::Round(XPos), cMathUtil::Round(YPos)));
  349. int c = ((int)(color[0]*255)&0xFF) << 16 | ((int)(color[1]*255)&0xFF) << 8 | ((int)(color[2]*255)&0xFF) << 0 | 0xFF000000;
  350. PTextRenderer->Draw_Text(text, c);
  351. WWASSERT(PFont != NULL);
  352. YPos += PFont->Char_Height() * Y_INCREMENT_FACTOR;
  353. }
  354. //-----------------------------------------------------------------------------
  355. void cTeamManager::Construct_Heading(WideStringClass & string)
  356. {
  357. string.Format(L"");
  358. WWASSERT(PTheGameData != NULL);
  359. //bool is_verbose = The_Game()->IsIntermission.Get() || MultiHUDClass::Get_Verbose_Lists();
  360. bool is_verbose = //force_verbose ||
  361. The_Game()->IsIntermission.Is_True() ||
  362. //MultiHUDClass::Get_Verbose_Lists();
  363. (MultiHUDClass::Get_Playerlist_Format() == PLAYERLIST_FORMAT_FULL);
  364. WideStringClass substring(0,true);
  365. //
  366. // Num Players
  367. //
  368. substring.Format(L"%-3s", L"");
  369. string += substring;
  370. /*
  371. //
  372. // Standing
  373. //
  374. if (is_verbose) {
  375. substring.Format(L"%-5s", L"");
  376. string += substring;
  377. }
  378. */
  379. //
  380. // "Team" or "Clan"
  381. //
  382. //GAMESPY
  383. //substring.Format(L"%-11s", The_Game()->Get_Team_Word());
  384. if (cGameSpyAdmin::Is_Gamespy_Game()) {
  385. substring.Format(L"%-36s", The_Game()->Get_Team_Word());
  386. } else {
  387. substring.Format(L"%-11s", The_Game()->Get_Team_Word());
  388. }
  389. string += substring;
  390. //
  391. // Kills
  392. //
  393. if (is_verbose) {
  394. substring.Format(L"%-8s", TRANSLATION(IDS_MP_KILLS));
  395. string += substring;
  396. }
  397. //
  398. // Deaths
  399. //
  400. if (is_verbose) {
  401. substring.Format(L"%-8s", TRANSLATION(IDS_MP_DEATHS));
  402. string += substring;
  403. }
  404. //
  405. // Kill to Death ratio
  406. //
  407. if (is_verbose) {
  408. substring.Format(L"%-8s", TRANSLATION(IDS_MP_KILL_TO_DEATH_RATIO));
  409. string += substring;
  410. }
  411. /**/
  412. //
  413. // Money
  414. //
  415. if ((The_Game()->Is_Cnc() || The_Game()->Is_Skirmish()) && is_verbose) {
  416. substring.Format(L"%-8s", TRANSLATION(IDS_MP_MONEY));
  417. string += substring;
  418. }
  419. /**/
  420. //
  421. // Score
  422. //
  423. substring.Format(L"%-8s", TRANSLATION(IDS_MP_SCORE));
  424. string += substring;
  425. }
  426. //-----------------------------------------------------------------------------
  427. void cTeamManager::Render_Team_List(void)
  428. {
  429. if (PTextRenderer == NULL) {
  430. return;
  431. }
  432. WWASSERT(PFont != NULL);
  433. WWASSERT(PTextRenderer != NULL);
  434. WWASSERT(PTheGameData != NULL);
  435. if (GameModeManager::Find("Combat") == NULL ||
  436. !GameModeManager::Find("Combat")->Is_Active() ||
  437. The_Game()->IsIntermission.Is_True()) {
  438. PTextRenderer->Reset();
  439. return;
  440. }
  441. //
  442. // Format:
  443. // AAAAAAAAAxxNNNNxxNNNxNNNxNNNxNxxNNNxxNNNNN, e.g:
  444. // Charlie 5 25 13 1.5 12 1203
  445. //
  446. // Fields are name, num players, kills, death, kills-to-deaths, num-lives, score
  447. //
  448. /*
  449. if (!The_Game()->Is_Team_Game()) {
  450. PTextRenderer->Reset();
  451. return;
  452. }
  453. */
  454. /*
  455. //
  456. // Count the non-empty teams
  457. //
  458. int num_teams = Get_Non_Empty_Team_Count();
  459. if (num_teams == 0) {
  460. return;
  461. }
  462. */
  463. bool changed=false;
  464. //
  465. // Build heading
  466. //
  467. WideStringClass heading(0,true);
  468. Construct_Heading(heading);
  469. if (heading!=renderer_team_heading) {
  470. renderer_team_heading=heading;
  471. changed=true;
  472. }
  473. // Verify that the team list is valid
  474. for (int j = 0; j < MAX_TEAMS; j++) {
  475. if (!Team_Array[j]) {
  476. Sort_Teams();
  477. break;
  478. }
  479. }
  480. for (j = 0; j < MAX_TEAMS; j++) {
  481. cTeam * p_team = Team_Array[j];
  482. WWASSERT(p_team != NULL);
  483. renderer_team_colors[j]=p_team->Get_Color();
  484. if (changed) {
  485. p_team->Get_Team_String(j + 1, renderer_team_strings[j]);
  486. }
  487. else {
  488. WideStringClass team_string(0,true);
  489. p_team->Get_Team_String(j + 1, team_string);
  490. if (team_string!=renderer_team_strings[j]) {
  491. changed=true;
  492. renderer_team_strings[j]=team_string;
  493. }
  494. }
  495. }
  496. WWASSERT(PTheGameData != NULL);
  497. if (The_Game()->IsIntermission.Is_True()!=renderer_is_intermission_true) {
  498. renderer_is_intermission_true=The_Game()->IsIntermission.Is_True();
  499. changed=true;
  500. }
  501. if (!changed) return;
  502. PTextRenderer->Reset();
  503. //
  504. // Determine left edge placement
  505. //
  506. XPos = 0;
  507. float text_len = PFont->String_Width(heading);
  508. if (The_Game()->IsIntermission.Is_True()) {
  509. XPos = Render2DClass::Get_Screen_Resolution().Center().X - text_len / 2.0f;
  510. } else {
  511. XPos = Render2DClass::Get_Screen_Resolution().Right - 20 - text_len;
  512. }
  513. YPos = 10;
  514. //
  515. // Show Win text
  516. //
  517. WWASSERT(PTheGameData != NULL);
  518. if (The_Game()->IsIntermission.Is_True()) {
  519. int combined_height =
  520. Compute_Team_List_Height() +
  521. cPlayerManager::Compute_Full_Player_List_Height() +
  522. 2 * PFont->Char_Height() * Y_INCREMENT_FACTOR;
  523. YPos = (int)(Render2DClass::Get_Screen_Resolution().Height() / 2.0 - combined_height / 2.0);
  524. if (YPos < 10) {
  525. YPos = 10;
  526. }
  527. WideStringClass text(cGameData::Get_Win_Text(),true);
  528. float x = Render2DClass::Get_Screen_Resolution().Center().X -
  529. PFont->String_Width(text) / 2.0f;
  530. float y = YPos;
  531. PTextRenderer->Set_Location(Vector2(cMathUtil::Round(x), cMathUtil::Round(y)));
  532. PTextRenderer->Draw_Text(text);
  533. YPos += 2 * PFont->Char_Height() * Y_INCREMENT_FACTOR;
  534. }
  535. //
  536. // Draw the teamlist heading
  537. //
  538. List_Print(heading, Vector3(1, 1, 1));
  539. for (j = 0; j < MAX_TEAMS; j++) {
  540. List_Print(renderer_team_strings[j], renderer_team_colors[j]);
  541. }
  542. }