SidesList.cpp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: SidesList.cpp /////////////////////////////////////////////////////////
  24. //-----------------------------------------------------------------------------
  25. //
  26. // Westwood Studios Pacific.
  27. //
  28. // Confidential Information
  29. // Copyright (C) 2001 - All Rights Reserved
  30. //
  31. //-----------------------------------------------------------------------------
  32. //
  33. // Project: RTS3
  34. //
  35. // File name: SidesList.cpp
  36. //
  37. // Created: John Ahlquist, Nov 2001
  38. //
  39. // Desc: Contains the information describing Sides (player, ai, neutral etc.)
  40. // in a scenario, including build lists for non-player sides.
  41. //
  42. //-----------------------------------------------------------------------------
  43. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  44. #include "Common/DataChunk.h"
  45. #include "Common/GameState.h"
  46. #include "Common/PlayerTemplate.h"
  47. #include "Common/WellKnownKeys.h"
  48. #include "Common/Xfer.h"
  49. #include "GameLogic/AI.h"
  50. #include "GameLogic/Scripts.h"
  51. #include "GameLogic/SidesList.h"
  52. static const Int K_SIDES_DATA_VERSION_1 = 1;
  53. static const Int K_SIDES_DATA_VERSION_2 = 2; // includes Team list.
  54. static const Int K_SIDES_DATA_VERSION_3 = 3; // includes Team list.
  55. /* ********* SidesInfo class ****************************/
  56. /**
  57. SidesInfo - Constructor.
  58. */
  59. SidesInfo::SidesInfo(void) :
  60. m_pBuildList(NULL),
  61. m_scripts(NULL)
  62. {
  63. }
  64. SidesInfo::SidesInfo(const SidesInfo& thatref) :
  65. m_pBuildList(NULL),
  66. m_scripts(NULL)
  67. {
  68. *this = thatref;
  69. }
  70. /**
  71. SidesInfo - Destructor -
  72. */
  73. SidesInfo::~SidesInfo(void)
  74. {
  75. clear();
  76. }
  77. void SidesInfo::init(const Dict* d)
  78. {
  79. m_pBuildList->deleteInstance();
  80. m_pBuildList = NULL;
  81. m_dict.clear();
  82. if (m_scripts)
  83. m_scripts->deleteInstance();
  84. m_scripts = NULL;
  85. if (d)
  86. m_dict = *d;
  87. }
  88. // ug, I hate having to overload stuff, but this makes it a lot easier to make copies safely
  89. SidesInfo& SidesInfo::operator=(const SidesInfo& that)
  90. {
  91. if (this != &that)
  92. {
  93. this->clear();
  94. this->m_dict = that.m_dict;
  95. BuildListInfo* thisBLTail = NULL;
  96. for (BuildListInfo* thatBL = that.m_pBuildList; thatBL; thatBL = thatBL->getNext())
  97. {
  98. BuildListInfo* thisBL = newInstance( BuildListInfo );
  99. *thisBL = *thatBL;
  100. thisBL->setNextBuildList(NULL);
  101. if (thisBLTail)
  102. thisBLTail->setNextBuildList(thisBL);
  103. else
  104. this->m_pBuildList = thisBL;
  105. thisBLTail = thisBL;
  106. }
  107. if (that.m_scripts)
  108. this->m_scripts = that.m_scripts->duplicate();
  109. else
  110. this->m_scripts = NULL;
  111. }
  112. return *this;
  113. }
  114. /**
  115. * SidesInfo::addToBuildList - Adds a build list entry as the nth entry.
  116. *
  117. */
  118. void SidesInfo::addToBuildList(BuildListInfo *pBuildList, Int position)
  119. {
  120. DEBUG_ASSERTLOG(pBuildList->getNext()==NULL, ("WARNING***Adding already linked element."));
  121. BuildListInfo *pCur = NULL;
  122. while (position) {
  123. position--;
  124. if (pCur==NULL) {
  125. pCur = m_pBuildList;
  126. } else {
  127. if (pCur->getNext()) {
  128. pCur = pCur->getNext();
  129. } else {
  130. break; // at end of list.
  131. }
  132. }
  133. }
  134. if (pCur==NULL) {
  135. // add to front of list.
  136. pBuildList->setNextBuildList(m_pBuildList);
  137. m_pBuildList = pBuildList;
  138. } else {
  139. pBuildList->setNextBuildList(pCur->getNext());
  140. pCur->setNextBuildList(pBuildList);
  141. }
  142. }
  143. /**
  144. * SidesInfo::reorderInBuildList - Reorders a build list entry as the nth entry.
  145. *
  146. */
  147. void SidesInfo::reorderInBuildList(BuildListInfo *pBuildList, Int newPosition)
  148. {
  149. /*Int oldPos =*/ removeFromBuildList(pBuildList);
  150. addToBuildList(pBuildList, newPosition);
  151. }
  152. /**
  153. * SidesInfo::removeFromBuildList - Removes a build list entry.
  154. * Returns the position in the list that the item occupied.
  155. *
  156. */
  157. Int SidesInfo::removeFromBuildList(BuildListInfo *pBuildList)
  158. {
  159. DEBUG_ASSERTCRASH(pBuildList, ("Removing NULL list."));
  160. if (pBuildList==NULL) return 0;
  161. Int position = 0;
  162. if (pBuildList == m_pBuildList) {
  163. // First item in list, so update head.
  164. m_pBuildList = pBuildList->getNext();
  165. } else {
  166. position = 1;
  167. // Not the first item, so find the preceeding list element.
  168. BuildListInfo *pPrev = m_pBuildList;
  169. while (pPrev && (pPrev->getNext()!=pBuildList) ) {
  170. pPrev = pPrev->getNext();
  171. position++;
  172. }
  173. DEBUG_ASSERTCRASH(pPrev, ("Removing item not in list."));
  174. if (pPrev) {
  175. pPrev->setNextBuildList(pBuildList->getNext());
  176. }
  177. }
  178. pBuildList->setNextBuildList(NULL);
  179. return position;
  180. }
  181. /* ********* SidesList class ****************************/
  182. /*extern*/ SidesList *TheSidesList = NULL; ///< singleton instance of SidesList
  183. /**
  184. SidesList - Constructor.
  185. */
  186. SidesList::SidesList(void) : m_numSides(0), m_numSkirmishSides(0)
  187. {
  188. }
  189. /**
  190. SidesList - Destructor -
  191. */
  192. SidesList::~SidesList(void)
  193. {
  194. }
  195. /**
  196. SidesList - reset -
  197. */
  198. void SidesList::reset(void)
  199. {
  200. clear();
  201. }
  202. /**
  203. SidesList - clear -
  204. */
  205. void SidesList::clear(void)
  206. {
  207. emptySides();
  208. emptyTeams();
  209. }
  210. /**
  211. * SidesList::ParseSidesDataChunk - read a Sides chunk.
  212. * Format is the newer CHUNKY format.
  213. * See SidesList::WriteSidesDataChunk for the writer.
  214. * Input: DataChunkInput
  215. *
  216. */
  217. Bool SidesList::ParseSidesDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  218. {
  219. DEBUG_ASSERTCRASH(TheSidesList, ("TheSidesList is null"));
  220. if (TheSidesList==NULL)
  221. return false;
  222. TheSidesList->clear();
  223. Int count = file.readInt();
  224. Int i, j;
  225. TheSidesList->emptySides();
  226. for (i=0; i<count; i++)
  227. {
  228. if (i >= MAX_PLAYER_COUNT) break;
  229. Dict d = file.readDict();
  230. TheSidesList->addSide(&d);
  231. BuildListInfo* pBuildList;
  232. Int count = file.readInt();
  233. for (j=0; j<count; j++)
  234. {
  235. pBuildList = newInstance( BuildListInfo );
  236. pBuildList->setBuildingName(file.readAsciiString());
  237. pBuildList->setTemplateName(file.readAsciiString());
  238. Coord3D loc;
  239. loc.x = file.readReal();
  240. loc.y = file.readReal();
  241. loc.z = file.readReal();
  242. loc.z = 0; // force to ground level
  243. pBuildList->setLocation(loc);
  244. pBuildList->setAngle(file.readReal());
  245. pBuildList->setInitiallyBuilt(file.readByte());
  246. pBuildList->setNumRebuilds(file.readInt());
  247. if (info->version >= K_SIDES_DATA_VERSION_3)
  248. {
  249. pBuildList->setScript(file.readAsciiString());
  250. pBuildList->setHealth(file.readInt());
  251. pBuildList->setWhiner(file.readByte());
  252. pBuildList->setUnsellable(file.readByte());
  253. pBuildList->setRepairable(file.readByte());
  254. }
  255. TheSidesList->getSideInfo(i)->addToBuildList(pBuildList, j);
  256. }
  257. }
  258. if (info->version >= K_SIDES_DATA_VERSION_2)
  259. {
  260. count = file.readInt();
  261. TheSidesList->emptyTeams();
  262. for (i=0; i<count; i++)
  263. {
  264. Dict d = file.readDict();
  265. TheSidesList->addTeam(&d);
  266. }
  267. }
  268. file.registerParser( AsciiString("PlayerScriptsList"), info->label, ScriptList::ParseScriptsDataChunk );
  269. if (!file.parse(NULL)) {
  270. throw(ERROR_CORRUPT_FILE_FORMAT);
  271. }
  272. ScriptList *scripts[MAX_PLAYER_COUNT];
  273. count = ScriptList::getReadScripts(scripts);
  274. for (i=0; i<count; i++) {
  275. if (i<TheSidesList->getNumSides()) {
  276. ScriptList *pSL = TheSidesList->getSideInfo(i)->getScriptList();
  277. pSL->deleteInstance();
  278. TheSidesList->getSideInfo(i)->setScriptList(scripts[i]);
  279. scripts[i] = NULL;
  280. } else {
  281. // Read in more players worth than we have.
  282. scripts[i]->deleteInstance();
  283. scripts[i] = NULL;
  284. }
  285. }
  286. TheSidesList->validateSides();
  287. DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Incorrect data file length."));
  288. return true;
  289. }
  290. /**
  291. * SidesList::WriteSidesDataChunk - Writes a Sides chunk.
  292. * Format is the newer CHUNKY format.
  293. * See SidesList::ParseSidesDataChunk for the reader.
  294. * Input: DataChunkInput
  295. *
  296. */
  297. void SidesList::WriteSidesDataChunk(DataChunkOutput &chunkWriter)
  298. {
  299. DEBUG_ASSERTCRASH(TheSidesList, ("TheSidesList is null"));
  300. if (TheSidesList==NULL)
  301. return;
  302. /**********HEIGHT MAP DATA ***********************/
  303. chunkWriter.openDataChunk("SidesList", K_SIDES_DATA_VERSION_3);
  304. chunkWriter.writeInt(TheSidesList->getNumSides());
  305. Int i;
  306. for (i=0; i<TheSidesList->getNumSides(); i++) {
  307. chunkWriter.writeDict(*TheSidesList->getSideInfo(i)->getDict());
  308. BuildListInfo* pBuildList = TheSidesList->getSideInfo(i)->getBuildList();
  309. Int count = 0;
  310. while (pBuildList) {
  311. count++;
  312. pBuildList = pBuildList->getNext();
  313. }
  314. chunkWriter.writeInt(count);
  315. pBuildList = TheSidesList->getSideInfo(i)->getBuildList();
  316. while (pBuildList) {
  317. chunkWriter.writeAsciiString(pBuildList->getBuildingName());
  318. chunkWriter.writeAsciiString(pBuildList->getTemplateName());
  319. chunkWriter.writeReal(pBuildList->getLocation()->x);
  320. chunkWriter.writeReal(pBuildList->getLocation()->y);
  321. chunkWriter.writeReal(pBuildList->getLocation()->z);
  322. chunkWriter.writeReal(pBuildList->getAngle());
  323. chunkWriter.writeByte(pBuildList->isInitiallyBuilt());
  324. chunkWriter.writeInt(pBuildList->getNumRebuilds());
  325. // BEGIN stuff new to K_SIDES_DATA_VERSION_3
  326. chunkWriter.writeAsciiString(pBuildList->getScript());
  327. chunkWriter.writeInt(pBuildList->getHealth());
  328. chunkWriter.writeByte(pBuildList->getWhiner());
  329. chunkWriter.writeByte(pBuildList->getUnsellable());
  330. chunkWriter.writeByte(pBuildList->getRepairable());
  331. // END stuff new to K_SIDES_DATA_VERSION_3
  332. pBuildList = pBuildList->getNext();
  333. }
  334. }
  335. // BEGIN stuff new to K_SIDES_DATA_VERSION_2
  336. chunkWriter.writeInt(TheSidesList->getNumTeams());
  337. for (i=0; i<TheSidesList->getNumTeams(); i++) {
  338. chunkWriter.writeDict(*TheSidesList->getTeamInfo(i)->getDict());
  339. }
  340. // END stuff new to K_SIDES_DATA_VERSION_2
  341. ScriptList *scripts[MAX_PLAYER_COUNT];
  342. for (i=0; i<TheSidesList->getNumSides(); i++) {
  343. scripts[i] = TheSidesList->getSideInfo(i)->getScriptList();
  344. }
  345. ScriptList::WriteScriptsDataChunk(chunkWriter, scripts, TheSidesList->getNumSides());
  346. chunkWriter.closeDataChunk();
  347. Bool modified = TheSidesList->validateSides();
  348. DEBUG_ASSERTLOG(!modified, ("*** had to clean up sideslist on read"));
  349. modified = false; // silence compiler warnings in release build
  350. }
  351. TeamsInfo *SidesList::findTeamInfo(AsciiString name, Int* index /*= NULL*/)
  352. {
  353. return m_teamrec.findTeamInfo(name, index);
  354. }
  355. SidesInfo *SidesList::findSideInfo(AsciiString name, Int* index /*= NULL*/)
  356. {
  357. for (int i = 0; i < m_numSides; i++)
  358. {
  359. if (m_sides[i].getDict()->getAsciiString(TheKey_playerName) == name)
  360. {
  361. if (index)
  362. *index = i;
  363. return &m_sides[i];
  364. }
  365. }
  366. return NULL;
  367. }
  368. SidesInfo *SidesList::findSkirmishSideInfo(AsciiString name, Int* index /*= NULL*/)
  369. {
  370. for (int i = 0; i < m_numSkirmishSides; i++)
  371. {
  372. if (m_skirmishSides[i].getDict()->getAsciiString(TheKey_playerName) == name)
  373. {
  374. if (index)
  375. *index = i;
  376. return &m_skirmishSides[i];
  377. }
  378. }
  379. return NULL;
  380. }
  381. static AsciiString static_readPlayerNames[MAX_PLAYER_COUNT];
  382. /**
  383. * ParsePlayersDataChunk - read players names data chunk.
  384. * Format is the newer CHUNKY format.
  385. * Input: DataChunkInput
  386. *
  387. */
  388. #define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_1 1
  389. #define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2 2
  390. static Bool ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  391. {
  392. Int readDicts = 0;
  393. if (info->version >= K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2) {
  394. readDicts = file.readInt();
  395. }
  396. Int numNames = file.readInt();
  397. Int i;
  398. for (i=0; i<numNames; i++) {
  399. if (i>=MAX_PLAYER_COUNT) break;
  400. static_readPlayerNames[i] = file.readAsciiString();
  401. if (readDicts) {
  402. Dict sideDict = file.readDict();
  403. }
  404. }
  405. DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over."));
  406. return true;
  407. }
  408. /**
  409. * ParseTeamsDataChunk - read teams data chunk.
  410. * Format is the newer CHUNKY format.
  411. * Input: DataChunkInput
  412. *
  413. */
  414. static Bool ParseTeamsDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  415. {
  416. SidesList *sides = (SidesList *)userData;
  417. while (!file.atEndOfChunk()) {
  418. Dict teamDict = file.readDict();
  419. AsciiString teamName = teamDict.getAsciiString(TheKey_teamName);
  420. AsciiString player = teamDict.getAsciiString(TheKey_teamOwner);
  421. if (sides->findSkirmishSideInfo(player)) {
  422. // player exists, so just add it.
  423. sides->addSkirmishTeam(&teamDict);
  424. //DEBUG_LOG(("Adding team %s\n", teamName.str()));
  425. } else {
  426. //DEBUG_LOG(("Couldn't add team %s, no player %s\n", teamName.str(), player.str()));
  427. }
  428. }
  429. DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over."));
  430. return true;
  431. }
  432. void SidesList::prepareForMP_or_Skirmish(void)
  433. {
  434. m_skirmishTeamrec.clear();
  435. Int i;
  436. for (i = 0; i < getNumTeams(); i++)
  437. {
  438. Dict *tdict = getTeamInfo(i)->getDict();
  439. m_skirmishTeamrec.addTeam(tdict);
  440. }
  441. m_teamrec.clear();
  442. for (i = 0; i < MAX_PLAYER_COUNT; i++) {
  443. m_skirmishSides[i].clear();
  444. }
  445. m_numSkirmishSides = 0;
  446. for (i = 0; i < m_numSides; i++)
  447. {
  448. m_skirmishSides[m_numSkirmishSides] = m_sides[i];
  449. m_numSkirmishSides++;
  450. if (m_sides[i].getDict()->getAsciiString(TheKey_playerFaction) == "FactionCivilian") {
  451. // Don't remove FactionCivilian.
  452. continue;
  453. }
  454. if (m_numSides == 1) break; // can't remove the last side.
  455. removeSide(i);
  456. i--;
  457. }
  458. Bool gotScripts = false;
  459. for (i=0; i<m_numSkirmishSides; i++) {
  460. if (m_skirmishSides[i].getDict()->getAsciiString(TheKey_playerFaction) == "FactionCivilian") {
  461. // Don't consider FactionCivilian.
  462. continue;
  463. }
  464. if (m_skirmishSides[i].getScriptList()==NULL) continue;
  465. if (m_skirmishSides[i].getScriptList()->getScript() != NULL ||
  466. m_skirmishSides[i].getScriptList()->getScriptGroup()!=NULL) {
  467. gotScripts = true;
  468. }
  469. }
  470. if (!gotScripts) {
  471. AsciiString path = "data\\Scripts\\SkirmishScripts.scb";
  472. DEBUG_LOG(("Skirmish map using standard scripts\n"));
  473. m_skirmishTeamrec.clear();
  474. CachedFileInputStream theInputStream;
  475. if (theInputStream.open(path)) {
  476. ChunkInputStream *pStrm = &theInputStream;
  477. DataChunkInput file( pStrm );
  478. file.registerParser( AsciiString("PlayerScriptsList"), AsciiString::TheEmptyString, ScriptList::ParseScriptsDataChunk );
  479. file.registerParser( AsciiString("ScriptsPlayers"), AsciiString::TheEmptyString, ParsePlayersDataChunk );
  480. file.registerParser( AsciiString("ScriptTeams"), AsciiString::TheEmptyString, ParseTeamsDataChunk );
  481. if (!file.parse(this)) {
  482. DEBUG_LOG(("ERROR - Unable to read in skirmish scripts.\n"));
  483. return;
  484. }
  485. ScriptList *scripts[MAX_PLAYER_COUNT];
  486. Int count = ScriptList::getReadScripts(scripts);
  487. Int i;
  488. for (i=0; i<count; i++) {
  489. Int curSide = -1;
  490. Int j;
  491. for (j=0; j<m_numSkirmishSides; j++) {
  492. AsciiString name = getSkirmishSideInfo(j)->getDict()->getAsciiString(TheKey_playerName);
  493. if (name == static_readPlayerNames[i]) {
  494. curSide = j;
  495. break;
  496. }
  497. }
  498. if (curSide == -1) continue;
  499. ScriptList *pSL = getSkirmishSideInfo(curSide)->getScriptList();
  500. getSkirmishSideInfo(curSide)->setScriptList(scripts[i]);
  501. scripts[i] = NULL;
  502. if (pSL)
  503. pSL->deleteInstance();
  504. scripts[i] = NULL;
  505. }
  506. for (i=0; i<MAX_PLAYER_COUNT; i++) {
  507. static_readPlayerNames[i].clear();
  508. }
  509. }
  510. }
  511. }
  512. Bool SidesList::isPlayerDefaultTeam(TeamsInfo *t)
  513. {
  514. // if our name is "teamfoo" and there is a player named "foo", we are a player-default team.
  515. AsciiString tname = t->getDict()->getAsciiString(TheKey_teamName);
  516. if (tname.startsWith("team"))
  517. {
  518. const char* rest = tname.str() + 4;
  519. for (int j = 0; j < m_numSides; j++)
  520. {
  521. AsciiString pname = m_sides[j].getDict()->getAsciiString(TheKey_playerName);
  522. if (strcmp(pname.str(), rest) == 0)
  523. {
  524. return true;
  525. }
  526. }
  527. }
  528. return false;
  529. }
  530. void SidesList::emptySides()
  531. {
  532. Int i;
  533. m_numSides = 0;
  534. m_numSkirmishSides = 0;
  535. for (i = 0; i < MAX_PLAYER_COUNT; i++) {
  536. m_sides[i].clear();
  537. m_skirmishSides[i].clear();
  538. }
  539. }
  540. void SidesList::emptyTeams()
  541. {
  542. m_teamrec.clear();
  543. m_skirmishTeamrec.clear();
  544. }
  545. void SidesList::addSide(const Dict* d)
  546. {
  547. DEBUG_ASSERTCRASH(m_numSides < MAX_PLAYER_COUNT, ("too many players"));
  548. if (m_numSides < MAX_PLAYER_COUNT)
  549. m_sides[m_numSides++].init(d);
  550. }
  551. void SidesList::addTeam(const Dict* d)
  552. {
  553. m_teamrec.addTeam(d);
  554. }
  555. void SidesList::addSkirmishTeam(const Dict* d)
  556. {
  557. m_skirmishTeamrec.addTeam(d);
  558. }
  559. void SidesList::removeSide(Int i)
  560. {
  561. if (i < 0 || i >= m_numSides || m_numSides <= 1)
  562. return;
  563. for ( ; i < m_numSides-1; i++)
  564. m_sides[i] = m_sides[i+1];
  565. for ( ; i < MAX_PLAYER_COUNT; i++)
  566. m_sides[i].clear();
  567. --m_numSides;
  568. }
  569. void SidesList::removeTeam(Int i)
  570. {
  571. m_teamrec.removeTeam(i);
  572. }
  573. Bool SidesList::validateAllyEnemyList(const AsciiString& tname, AsciiString& allies)
  574. {
  575. // owners/allies/enemies must be players.
  576. Bool modified = false;
  577. AsciiString str, newstr, token;
  578. str = allies;
  579. newstr.clear();
  580. while (str.nextToken(&token))
  581. {
  582. if (token == tname)
  583. {
  584. modified = true;
  585. continue; // no allies/enemies with self
  586. }
  587. SidesInfo *si = findSideInfo(token);
  588. if (!si)
  589. {
  590. modified = true;
  591. continue; // player not found.
  592. }
  593. if (!newstr.isEmpty())
  594. newstr.concat(" ");
  595. newstr.concat(token);
  596. }
  597. allies = newstr;
  598. return modified;
  599. }
  600. void SidesList::addPlayerByTemplate(AsciiString playerTemplateName)
  601. {
  602. AsciiString playerName;
  603. UnicodeString playerDisplayName;
  604. Bool isHuman = false;
  605. if (playerTemplateName.isEmpty())
  606. {
  607. playerName.set(""); // magic code for "neutral"
  608. playerDisplayName = L"Neutral";
  609. isHuman = false;
  610. }
  611. else
  612. {
  613. playerName.set("Plyr");
  614. if (playerTemplateName.startsWith("Faction"))
  615. {
  616. playerName.concat(playerTemplateName.str() + 7);
  617. }
  618. else
  619. {
  620. playerName.concat(playerTemplateName);
  621. }
  622. playerDisplayName.translate(playerName);
  623. isHuman = true;
  624. // special-case "civilian"...
  625. if (playerName == "PlyrCivilian")
  626. isHuman = false;
  627. }
  628. Dict d;
  629. d.clear();
  630. d.setAsciiString(TheKey_playerName, playerName);
  631. d.setBool(TheKey_playerIsHuman, isHuman);
  632. d.setUnicodeString(TheKey_playerDisplayName, playerDisplayName);
  633. d.setAsciiString(TheKey_playerFaction, playerTemplateName);
  634. d.setAsciiString(TheKey_playerAllies, AsciiString::TheEmptyString);
  635. d.setAsciiString(TheKey_playerEnemies, AsciiString::TheEmptyString);
  636. addSide(&d);
  637. AsciiString playerTeamName;
  638. playerTeamName.set("team");
  639. playerTeamName.concat(playerName);
  640. d.clear();
  641. d.setAsciiString(TheKey_teamName, playerTeamName);
  642. d.setAsciiString(TheKey_teamOwner, playerName);
  643. d.setBool(TheKey_teamIsSingleton, true);
  644. addTeam(&d);
  645. }
  646. Bool SidesList::validateSides()
  647. {
  648. Bool modified = false;
  649. // ensure we have at least one player, and at least one neutral player.
  650. Int i;
  651. Int neutral = -1;
  652. Int num = getNumSides();
  653. for (i = 0; i < num; i++)
  654. {
  655. if (getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName).isEmpty())
  656. {
  657. neutral = i;
  658. break;
  659. }
  660. }
  661. if (neutral == -1)
  662. {
  663. addPlayerByTemplate(AsciiString::TheEmptyString);
  664. modified = true;
  665. }
  666. // now ensure that every player has a proper "default team"
  667. for (i = 0; i < getNumSides(); i++)
  668. {
  669. Dict *pdict = getSideInfo(i)->getDict();
  670. AsciiString pname = pdict->getAsciiString(TheKey_playerName);
  671. AsciiString tname("team");
  672. tname.concat(pname);
  673. TeamsInfo *ti = findTeamInfo(tname);
  674. if (ti)
  675. {
  676. // make sure the team owner points back to the player.
  677. if (ti->getDict()->getAsciiString(TheKey_teamOwner) != pname)
  678. {
  679. DEBUG_CRASH(("hmm, team owner mismatch (%s) (%s), this should not normally be possible",ti->getDict()->getAsciiString(TheKey_teamOwner).str(), pname.str()));
  680. ti->getDict()->setAsciiString(TheKey_teamOwner, pname);
  681. modified = true;
  682. }
  683. // default teams are always singletons.
  684. if (!ti->getDict()->getBool(TheKey_teamIsSingleton))
  685. {
  686. DEBUG_CRASH(("hmm, this should not normally be possible"));
  687. ti->getDict()->setBool(TheKey_teamIsSingleton, true);
  688. modified = true;
  689. }
  690. }
  691. else
  692. {
  693. DEBUG_LOG(("*** default team for player %s missing (should not be possible), adding it...\n",tname.str()));
  694. Dict d;
  695. d.setAsciiString(TheKey_teamName, tname);
  696. d.setAsciiString(TheKey_teamOwner, pname);
  697. d.setBool(TheKey_teamIsSingleton, true);
  698. addTeam(&d);
  699. modified = true;
  700. }
  701. AsciiString allies = pdict->getAsciiString(TheKey_playerAllies);
  702. AsciiString enemies = pdict->getAsciiString(TheKey_playerEnemies);
  703. // ensure all teams have valid allies & enemies.
  704. // (note that owners can be teams or players, but allies/enemies can only be teams.)
  705. if (validateAllyEnemyList(pname, allies))
  706. {
  707. DEBUG_LOG(("bad allies...\n"));
  708. pdict->setAsciiString(TheKey_playerAllies, allies);
  709. modified = true;
  710. }
  711. if (validateAllyEnemyList(pname, enemies))
  712. {
  713. DEBUG_LOG(("bad enemies...\n"));
  714. pdict->setAsciiString(TheKey_playerEnemies, enemies);
  715. modified = true;
  716. }
  717. }
  718. // ensure there's no overlap between team names and player names.
  719. // (if there is, the player wins and the team is whacked.)
  720. validate_team_names:
  721. for (i = 0; i < getNumTeams(); i++)
  722. {
  723. Dict *tdict = getTeamInfo(i)->getDict();
  724. AsciiString tname = tdict->getAsciiString(TheKey_teamName);
  725. if (findSideInfo(tname))
  726. {
  727. DEBUG_CRASH(("name %s is duplicate between player and team, removing...\n",tname.str()));
  728. removeTeam(i);
  729. modified = true;
  730. goto validate_team_names;
  731. }
  732. }
  733. for (i = 0; i < getNumTeams(); i++)
  734. {
  735. Dict *tdict = getTeamInfo(i)->getDict();
  736. AsciiString tname = tdict->getAsciiString(TheKey_teamName);
  737. AsciiString towner = tdict->getAsciiString(TheKey_teamOwner);
  738. SidesInfo* si = findSideInfo(towner);
  739. if (si == NULL || towner == tname)
  740. {
  741. DEBUG_LOG(("bad owner %s; reparenting to neutral...\n",towner.str()));
  742. tdict->setAsciiString(TheKey_teamOwner, AsciiString::TheEmptyString);
  743. modified = true;
  744. }
  745. // if (tdict->getType(NAMEKEY(AsciiString("teamAllies"))) != Dict::DICT_NONE)
  746. // tdict->remove(NAMEKEY(AsciiString("teamAllies")));
  747. // if (tdict->getType(NAMEKEY(AsciiString("teamEnemies"))) != Dict::DICT_NONE)
  748. // tdict->remove(NAMEKEY(AsciiString("teamEnemies")));
  749. }
  750. return modified;
  751. }
  752. // ------------------------------------------------------------------------------------------------
  753. /** CRC */
  754. // ------------------------------------------------------------------------------------------------
  755. void SidesList::crc( Xfer *xfer )
  756. {
  757. } // end crc
  758. // ------------------------------------------------------------------------------------------------
  759. /** Xfer method
  760. * Version Info:
  761. * 1: Initial version */
  762. // ------------------------------------------------------------------------------------------------
  763. void SidesList::xfer( Xfer *xfer )
  764. {
  765. // version
  766. XferVersion currentVersion = 1;
  767. XferVersion version = currentVersion;
  768. xfer->xferVersion( &version, currentVersion );
  769. // xfer num sides
  770. Int sideCount = getNumSides();
  771. xfer->xferInt( &sideCount );
  772. if( sideCount != getNumSides() )
  773. {
  774. DEBUG_CRASH(( "SidesList::xfer - The sides list size has changed, this was not supposed to happen, you must version this method and figure out how to translate between old and new versions now\n" ));
  775. throw SC_INVALID_DATA;
  776. } // end if
  777. // side data
  778. ScriptList *scriptList;
  779. Bool scriptListPresent;
  780. for( Int i = 0; i < sideCount; ++i )
  781. {
  782. // xfer script list data that can change
  783. scriptList = getSideInfo( i )->getScriptList();
  784. scriptListPresent = scriptList ? TRUE : FALSE;
  785. xfer->xferBool( &scriptListPresent );
  786. if( (scriptList == NULL && scriptListPresent == TRUE) ||
  787. (scriptList != NULL && scriptListPresent == FALSE) )
  788. {
  789. DEBUG_CRASH(( "SidesList::xfer - script list missing/present mismatch\n" ));
  790. throw SC_INVALID_DATA;
  791. } // end if
  792. if( scriptListPresent )
  793. xfer->xferSnapshot( scriptList );
  794. } // end for i
  795. } // end xfer
  796. // ------------------------------------------------------------------------------------------------
  797. /** Load post process */
  798. // ------------------------------------------------------------------------------------------------
  799. void SidesList::loadPostProcess( void )
  800. {
  801. } // end loadPostProcess
  802. /* ********* BuildListInfo class ****************************/
  803. /**
  804. BuildListInfo - Constructor.
  805. */
  806. BuildListInfo::BuildListInfo(void) :
  807. m_nextBuildList(NULL),
  808. m_renderObj(NULL),
  809. m_shadowObj(NULL),
  810. m_isInitiallyBuilt(false),
  811. m_numRebuilds(0),
  812. m_angle(0),
  813. m_script(AsciiString::TheEmptyString),
  814. m_health(100),
  815. m_whiner(true),
  816. m_unsellable(false),
  817. m_repairable(true),
  818. m_objectID(INVALID_ID),
  819. m_objectTimestamp(0),
  820. m_underConstruction(false),
  821. m_isSupplyBuilding(false),
  822. m_desiredGatherers(0),
  823. m_currentGatherers(0),
  824. m_automaticallyBuild(true),
  825. m_priorityBuild(false),
  826. m_buildingName(AsciiString::TheEmptyString)
  827. {
  828. // Added by Sadullah Nader
  829. // these initialized values are necessary!!!
  830. m_location.zero();
  831. m_rallyPointOffset.x = 0.0f;
  832. m_rallyPointOffset.y = 0.0f;
  833. m_selected = FALSE;
  834. Int i;
  835. for (i=0; i<MAX_RESOURCE_GATHERERS; i++)
  836. {
  837. m_resourceGatherers[i] = INVALID_ID;
  838. }
  839. }
  840. /**
  841. BuildListInfo - Destructor - note - if linked, deletes linked items.
  842. */
  843. BuildListInfo::~BuildListInfo(void)
  844. {
  845. if (m_nextBuildList) {
  846. BuildListInfo *cur = m_nextBuildList;
  847. BuildListInfo *next;
  848. while (cur) {
  849. next = cur->getNext();
  850. cur->setNextBuildList(NULL); // prevents recursion.
  851. cur->deleteInstance();
  852. cur = next;
  853. }
  854. }
  855. }
  856. void BuildListInfo::parseStructure(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
  857. {
  858. const char* c = ini->getNextToken();
  859. AsciiString tTemplateName(c);
  860. static const FieldParse myFieldParse[] =
  861. {
  862. { "Name", INI::parseAsciiString, NULL, offsetof( BuildListInfo, m_buildingName ) },
  863. { "Location", INI::parseCoord2D, NULL, offsetof( BuildListInfo, m_location ) },
  864. { "Rebuilds", INI::parseInt, NULL, offsetof( BuildListInfo, m_numRebuilds ) },
  865. { "Angle", INI::parseAngleReal, NULL, offsetof( BuildListInfo, m_angle ) },
  866. { "InitiallyBuilt", INI::parseBool, NULL, offsetof( BuildListInfo, m_isInitiallyBuilt ) },
  867. { "RallyPointOffset", INI::parseCoord2D, NULL, offsetof( BuildListInfo, m_rallyPointOffset ) },
  868. { "AutomaticallyBuild", INI::parseBool, NULL, offsetof( BuildListInfo, m_automaticallyBuild ) },
  869. { NULL, NULL, NULL, 0 } // keep this last
  870. };
  871. BuildListInfo *buildInfo = newInstance( BuildListInfo );
  872. buildInfo->setTemplateName(tTemplateName);
  873. ini->initFromINI(buildInfo, myFieldParse);
  874. ((AISideBuildList*)instance)->addInfo(buildInfo);
  875. }
  876. /**
  877. BuildListInfo - Duplicate - note - if linked, duplicates linked items.
  878. */
  879. BuildListInfo *BuildListInfo::duplicate(void)
  880. {
  881. BuildListInfo *first = newInstance( BuildListInfo );
  882. *first = *this;
  883. first->m_nextBuildList = NULL;
  884. BuildListInfo *next = this->m_nextBuildList;
  885. BuildListInfo *cur = first;
  886. while (next) {
  887. BuildListInfo *link = newInstance( BuildListInfo );
  888. *link = *next;
  889. link->m_nextBuildList = NULL;
  890. cur->m_nextBuildList = link;
  891. cur = link;
  892. next = next->m_nextBuildList;
  893. }
  894. return first;
  895. }
  896. // ------------------------------------------------------------------------------------------------
  897. /** CRC */
  898. // ------------------------------------------------------------------------------------------------
  899. void BuildListInfo::crc( Xfer *xfer )
  900. {
  901. } // end crc
  902. // ------------------------------------------------------------------------------------------------
  903. /** Xfer method
  904. * Version Info:
  905. * 1: Initial version */
  906. // ------------------------------------------------------------------------------------------------
  907. void BuildListInfo::xfer( Xfer *xfer )
  908. {
  909. // version
  910. XferVersion currentVersion = 2;
  911. XferVersion version = currentVersion;
  912. xfer->xferVersion( &version, currentVersion );
  913. xfer->xferAsciiString( &m_buildingName );
  914. xfer->xferAsciiString( &m_templateName );
  915. xfer->xferCoord3D( &m_location );
  916. xfer->xferCoord2D( &m_rallyPointOffset );
  917. xfer->xferReal( &m_angle );
  918. xfer->xferBool( &m_isInitiallyBuilt );
  919. xfer->xferUnsignedInt( &m_numRebuilds );
  920. xfer->xferAsciiString( &m_script );
  921. xfer->xferInt( &m_health );
  922. xfer->xferBool( &m_whiner );
  923. xfer->xferBool( &m_unsellable );
  924. xfer->xferBool( &m_repairable );
  925. xfer->xferBool( &m_automaticallyBuild );
  926. // m_renderObj we don't need to xfer this, its for the editor only
  927. // m_shadowObj we don't need to xfer this, its for the editor only
  928. // m_selected we don't need to xfer this, its for the editor only
  929. xfer->xferObjectID( &m_objectID );
  930. xfer->xferUnsignedInt( &m_objectTimestamp );
  931. xfer->xferBool( &m_underConstruction );
  932. xfer->xferUser( m_resourceGatherers, sizeof( ObjectID ) * MAX_RESOURCE_GATHERERS );
  933. xfer->xferBool( &m_isSupplyBuilding );
  934. xfer->xferInt( &m_desiredGatherers );
  935. xfer->xferBool( &m_priorityBuild );
  936. if (version>=2) {
  937. xfer->xferInt(&m_currentGatherers);
  938. }
  939. } // end xfer
  940. // ------------------------------------------------------------------------------------------------
  941. /** Load post process */
  942. // ------------------------------------------------------------------------------------------------
  943. void BuildListInfo::loadPostProcess( void )
  944. {
  945. } // end loadPostProcess
  946. /* ********* TeamsInfoRec class ****************************/
  947. TeamsInfoRec::TeamsInfoRec() :
  948. m_numTeams(0), m_numTeamsAllocated(0), m_teams(NULL)
  949. {
  950. }
  951. TeamsInfoRec::TeamsInfoRec(const TeamsInfoRec& thatref) :
  952. m_numTeams(0), m_numTeamsAllocated(0), m_teams(NULL)
  953. {
  954. *this = thatref;
  955. }
  956. TeamsInfoRec::~TeamsInfoRec()
  957. {
  958. clear();
  959. }
  960. // ug, I hate having to overload stuff, but this makes it a lot easier to make copies safely
  961. TeamsInfoRec& TeamsInfoRec::operator=(const TeamsInfoRec& thatref)
  962. {
  963. const TeamsInfoRec* that = &thatref;
  964. if (this != that)
  965. {
  966. this->clear();
  967. for (int i = 0; i < that->m_numTeams; i++)
  968. {
  969. this->addTeam(that->m_teams[i].getDict());
  970. }
  971. }
  972. return *this;
  973. }
  974. void TeamsInfoRec::clear()
  975. {
  976. Int i;
  977. for (i = 0; i < m_numTeamsAllocated; i++)
  978. m_teams[i].clear();
  979. m_numTeams = 0;
  980. m_numTeamsAllocated = 0;
  981. delete [] m_teams;
  982. m_teams = NULL;
  983. }
  984. TeamsInfo *TeamsInfoRec::findTeamInfo(AsciiString name, Int* index /*= NULL*/)
  985. {
  986. for (int i = 0; i < m_numTeams; i++)
  987. {
  988. if (m_teams[i].getDict()->getAsciiString(TheKey_teamName) == name)
  989. {
  990. if (index)
  991. *index = i;
  992. return &m_teams[i];
  993. }
  994. }
  995. return NULL;
  996. }
  997. void TeamsInfoRec::addTeam(const Dict* d)
  998. {
  999. enum
  1000. {
  1001. TEAM_ALLOC_CHUNK = 8 ///< how many teams to alloc at a time
  1002. };
  1003. DEBUG_ASSERTCRASH(m_numTeams < 2048, ("%d teams have been allocated (so far). This seems excessive.", m_numTeams ));
  1004. if (m_numTeams >= m_numTeamsAllocated)
  1005. {
  1006. // pool[]ify
  1007. TeamsInfo* nti = NEW TeamsInfo[m_numTeamsAllocated + TEAM_ALLOC_CHUNK]; // throws on failure
  1008. Int i;
  1009. for (i = 0; i < m_numTeams; i++)
  1010. nti[i] = m_teams[i];
  1011. for ( ; i < m_numTeamsAllocated + TEAM_ALLOC_CHUNK; i++)
  1012. nti[i].clear();
  1013. delete [] m_teams;
  1014. m_teams = nti;
  1015. m_numTeamsAllocated += TEAM_ALLOC_CHUNK;
  1016. }
  1017. m_teams[m_numTeams++].init(d);
  1018. }
  1019. void TeamsInfoRec::removeTeam(Int i)
  1020. {
  1021. if (i < 0 || i >= m_numTeams || m_numTeams <= 1)
  1022. return;
  1023. for ( ; i < m_numTeams-1; i++)
  1024. m_teams[i] = m_teams[i+1];
  1025. for ( ; i < m_numTeamsAllocated; i++)
  1026. m_teams[i].clear();
  1027. --m_numTeams;
  1028. }