SidesList.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158
  1. /*
  2. ** Command & Conquer Generals(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. static Bool ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  389. {
  390. Int numNames = file.readInt();
  391. Int i;
  392. for (i=0; i<numNames; i++) {
  393. if (i>=MAX_PLAYER_COUNT) break;
  394. static_readPlayerNames[i] = file.readAsciiString();
  395. }
  396. DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over."));
  397. return true;
  398. }
  399. /**
  400. * ParseTeamsDataChunk - read teams data chunk.
  401. * Format is the newer CHUNKY format.
  402. * Input: DataChunkInput
  403. *
  404. */
  405. static Bool ParseTeamsDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  406. {
  407. SidesList *sides = (SidesList *)userData;
  408. while (!file.atEndOfChunk()) {
  409. Dict teamDict = file.readDict();
  410. AsciiString teamName = teamDict.getAsciiString(TheKey_teamName);
  411. AsciiString player = teamDict.getAsciiString(TheKey_teamOwner);
  412. if (sides->findSkirmishSideInfo(player)) {
  413. // player exists, so just add it.
  414. sides->addSkirmishTeam(&teamDict);
  415. //DEBUG_LOG(("Adding team %s\n", teamName.str()));
  416. } else {
  417. //DEBUG_LOG(("Couldn't add team %s, no player %s\n", teamName.str(), player.str()));
  418. }
  419. }
  420. DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over."));
  421. return true;
  422. }
  423. void SidesList::prepareForMP_or_Skirmish(void)
  424. {
  425. m_skirmishTeamrec.clear();
  426. Int i;
  427. for (i = 0; i < getNumTeams(); i++)
  428. {
  429. Dict *tdict = getTeamInfo(i)->getDict();
  430. m_skirmishTeamrec.addTeam(tdict);
  431. }
  432. m_teamrec.clear();
  433. for (i = 0; i < MAX_PLAYER_COUNT; i++) {
  434. m_skirmishSides[i].clear();
  435. }
  436. m_numSkirmishSides = 0;
  437. for (i = 0; i < m_numSides; i++)
  438. {
  439. m_skirmishSides[m_numSkirmishSides] = m_sides[i];
  440. m_numSkirmishSides++;
  441. if (m_sides[i].getDict()->getAsciiString(TheKey_playerFaction) == "FactionCivilian") {
  442. // Don't remove FactionCivilian.
  443. continue;
  444. }
  445. if (m_numSides == 1) break; // can't remove the last side.
  446. removeSide(i);
  447. i--;
  448. }
  449. Bool gotScripts = false;
  450. for (i=0; i<m_numSkirmishSides; i++) {
  451. if (m_skirmishSides[i].getDict()->getAsciiString(TheKey_playerFaction) == "FactionCivilian") {
  452. // Don't consider FactionCivilian.
  453. continue;
  454. }
  455. if (m_skirmishSides[i].getScriptList()==NULL) continue;
  456. if (m_skirmishSides[i].getScriptList()->getScript() != NULL ||
  457. m_skirmishSides[i].getScriptList()->getScriptGroup()!=NULL) {
  458. gotScripts = true;
  459. }
  460. }
  461. if (!gotScripts) {
  462. AsciiString path = "data\\Scripts\\SkirmishScripts.scb";
  463. DEBUG_LOG(("Skirmish map using standard scripts\n"));
  464. m_skirmishTeamrec.clear();
  465. CachedFileInputStream theInputStream;
  466. if (theInputStream.open(path)) {
  467. ChunkInputStream *pStrm = &theInputStream;
  468. DataChunkInput file( pStrm );
  469. file.registerParser( AsciiString("PlayerScriptsList"), AsciiString::TheEmptyString, ScriptList::ParseScriptsDataChunk );
  470. file.registerParser( AsciiString("ScriptsPlayers"), AsciiString::TheEmptyString, ParsePlayersDataChunk );
  471. file.registerParser( AsciiString("ScriptTeams"), AsciiString::TheEmptyString, ParseTeamsDataChunk );
  472. if (!file.parse(this)) {
  473. DEBUG_LOG(("ERROR - Unable to read in skirmish scripts.\n"));
  474. return;
  475. }
  476. ScriptList *scripts[MAX_PLAYER_COUNT];
  477. Int count = ScriptList::getReadScripts(scripts);
  478. Int i;
  479. for (i=0; i<count; i++) {
  480. Int curSide = -1;
  481. Int j;
  482. for (j=0; j<m_numSkirmishSides; j++) {
  483. AsciiString name = getSkirmishSideInfo(j)->getDict()->getAsciiString(TheKey_playerName);
  484. if (name == static_readPlayerNames[i]) {
  485. curSide = j;
  486. break;
  487. }
  488. }
  489. if (curSide == -1) continue;
  490. ScriptList *pSL = getSkirmishSideInfo(curSide)->getScriptList();
  491. getSkirmishSideInfo(curSide)->setScriptList(scripts[i]);
  492. scripts[i] = NULL;
  493. if (pSL)
  494. pSL->deleteInstance();
  495. scripts[i] = NULL;
  496. }
  497. for (i=0; i<MAX_PLAYER_COUNT; i++) {
  498. static_readPlayerNames[i].clear();
  499. }
  500. }
  501. }
  502. }
  503. Bool SidesList::isPlayerDefaultTeam(TeamsInfo *t)
  504. {
  505. // if our name is "teamfoo" and there is a player named "foo", we are a player-default team.
  506. AsciiString tname = t->getDict()->getAsciiString(TheKey_teamName);
  507. if (tname.startsWith("team"))
  508. {
  509. const char* rest = tname.str() + 4;
  510. for (int j = 0; j < m_numSides; j++)
  511. {
  512. AsciiString pname = m_sides[j].getDict()->getAsciiString(TheKey_playerName);
  513. if (strcmp(pname.str(), rest) == 0)
  514. {
  515. return true;
  516. }
  517. }
  518. }
  519. return false;
  520. }
  521. void SidesList::emptySides()
  522. {
  523. Int i;
  524. m_numSides = 0;
  525. m_numSkirmishSides = 0;
  526. for (i = 0; i < MAX_PLAYER_COUNT; i++) {
  527. m_sides[i].clear();
  528. m_skirmishSides[i].clear();
  529. }
  530. }
  531. void SidesList::emptyTeams()
  532. {
  533. m_teamrec.clear();
  534. m_skirmishTeamrec.clear();
  535. }
  536. void SidesList::addSide(const Dict* d)
  537. {
  538. DEBUG_ASSERTCRASH(m_numSides < MAX_PLAYER_COUNT, ("too many players"));
  539. if (m_numSides < MAX_PLAYER_COUNT)
  540. m_sides[m_numSides++].init(d);
  541. }
  542. void SidesList::addTeam(const Dict* d)
  543. {
  544. m_teamrec.addTeam(d);
  545. }
  546. void SidesList::addSkirmishTeam(const Dict* d)
  547. {
  548. m_skirmishTeamrec.addTeam(d);
  549. }
  550. void SidesList::removeSide(Int i)
  551. {
  552. if (i < 0 || i >= m_numSides || m_numSides <= 1)
  553. return;
  554. for ( ; i < m_numSides-1; i++)
  555. m_sides[i] = m_sides[i+1];
  556. for ( ; i < MAX_PLAYER_COUNT; i++)
  557. m_sides[i].clear();
  558. --m_numSides;
  559. }
  560. void SidesList::removeTeam(Int i)
  561. {
  562. m_teamrec.removeTeam(i);
  563. }
  564. Bool SidesList::validateAllyEnemyList(const AsciiString& tname, AsciiString& allies)
  565. {
  566. // owners/allies/enemies must be players.
  567. Bool modified = false;
  568. AsciiString str, newstr, token;
  569. str = allies;
  570. newstr.clear();
  571. while (str.nextToken(&token))
  572. {
  573. if (token == tname)
  574. {
  575. modified = true;
  576. continue; // no allies/enemies with self
  577. }
  578. SidesInfo *si = findSideInfo(token);
  579. if (!si)
  580. {
  581. modified = true;
  582. continue; // player not found.
  583. }
  584. if (!newstr.isEmpty())
  585. newstr.concat(" ");
  586. newstr.concat(token);
  587. }
  588. allies = newstr;
  589. return modified;
  590. }
  591. void SidesList::addPlayerByTemplate(AsciiString playerTemplateName)
  592. {
  593. AsciiString playerName;
  594. UnicodeString playerDisplayName;
  595. Bool isHuman = false;
  596. if (playerTemplateName.isEmpty())
  597. {
  598. playerName.set(""); // magic code for "neutral"
  599. playerDisplayName = L"Neutral";
  600. isHuman = false;
  601. }
  602. else
  603. {
  604. playerName.set("Plyr");
  605. if (playerTemplateName.startsWith("Faction"))
  606. {
  607. playerName.concat(playerTemplateName.str() + 7);
  608. }
  609. else
  610. {
  611. playerName.concat(playerTemplateName);
  612. }
  613. playerDisplayName.translate(playerName);
  614. isHuman = true;
  615. // special-case "civilian"...
  616. if (playerName == "PlyrCivilian")
  617. isHuman = false;
  618. }
  619. Dict d;
  620. d.clear();
  621. d.setAsciiString(TheKey_playerName, playerName);
  622. d.setBool(TheKey_playerIsHuman, isHuman);
  623. d.setUnicodeString(TheKey_playerDisplayName, playerDisplayName);
  624. d.setAsciiString(TheKey_playerFaction, playerTemplateName);
  625. d.setAsciiString(TheKey_playerAllies, AsciiString::TheEmptyString);
  626. d.setAsciiString(TheKey_playerEnemies, AsciiString::TheEmptyString);
  627. addSide(&d);
  628. AsciiString playerTeamName;
  629. playerTeamName.set("team");
  630. playerTeamName.concat(playerName);
  631. d.clear();
  632. d.setAsciiString(TheKey_teamName, playerTeamName);
  633. d.setAsciiString(TheKey_teamOwner, playerName);
  634. d.setBool(TheKey_teamIsSingleton, true);
  635. addTeam(&d);
  636. }
  637. Bool SidesList::validateSides()
  638. {
  639. Bool modified = false;
  640. // ensure we have at least one player, and at least one neutral player.
  641. Int i;
  642. Int neutral = -1;
  643. Int num = getNumSides();
  644. for (i = 0; i < num; i++)
  645. {
  646. if (getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName).isEmpty())
  647. {
  648. neutral = i;
  649. break;
  650. }
  651. }
  652. if (neutral == -1)
  653. {
  654. addPlayerByTemplate(AsciiString::TheEmptyString);
  655. modified = true;
  656. }
  657. // now ensure that every player has a proper "default team"
  658. for (i = 0; i < getNumSides(); i++)
  659. {
  660. Dict *pdict = getSideInfo(i)->getDict();
  661. AsciiString pname = pdict->getAsciiString(TheKey_playerName);
  662. AsciiString tname("team");
  663. tname.concat(pname);
  664. TeamsInfo *ti = findTeamInfo(tname);
  665. if (ti)
  666. {
  667. // make sure the team owner points back to the player.
  668. if (ti->getDict()->getAsciiString(TheKey_teamOwner) != pname)
  669. {
  670. DEBUG_CRASH(("hmm, team owner mismatch (%s) (%s), this should not normally be possible",ti->getDict()->getAsciiString(TheKey_teamOwner).str(), pname.str()));
  671. ti->getDict()->setAsciiString(TheKey_teamOwner, pname);
  672. modified = true;
  673. }
  674. // default teams are always singletons.
  675. if (!ti->getDict()->getBool(TheKey_teamIsSingleton))
  676. {
  677. DEBUG_CRASH(("hmm, this should not normally be possible"));
  678. ti->getDict()->setBool(TheKey_teamIsSingleton, true);
  679. modified = true;
  680. }
  681. }
  682. else
  683. {
  684. DEBUG_LOG(("*** default team for player %s missing (should not be possible), adding it...\n",tname.str()));
  685. Dict d;
  686. d.setAsciiString(TheKey_teamName, tname);
  687. d.setAsciiString(TheKey_teamOwner, pname);
  688. d.setBool(TheKey_teamIsSingleton, true);
  689. addTeam(&d);
  690. modified = true;
  691. }
  692. AsciiString allies = pdict->getAsciiString(TheKey_playerAllies);
  693. AsciiString enemies = pdict->getAsciiString(TheKey_playerEnemies);
  694. // ensure all teams have valid allies & enemies.
  695. // (note that owners can be teams or players, but allies/enemies can only be teams.)
  696. if (validateAllyEnemyList(pname, allies))
  697. {
  698. DEBUG_LOG(("bad allies...\n"));
  699. pdict->setAsciiString(TheKey_playerAllies, allies);
  700. modified = true;
  701. }
  702. if (validateAllyEnemyList(pname, enemies))
  703. {
  704. DEBUG_LOG(("bad enemies...\n"));
  705. pdict->setAsciiString(TheKey_playerEnemies, enemies);
  706. modified = true;
  707. }
  708. }
  709. // ensure there's no overlap between team names and player names.
  710. // (if there is, the player wins and the team is whacked.)
  711. validate_team_names:
  712. for (i = 0; i < getNumTeams(); i++)
  713. {
  714. Dict *tdict = getTeamInfo(i)->getDict();
  715. AsciiString tname = tdict->getAsciiString(TheKey_teamName);
  716. if (findSideInfo(tname))
  717. {
  718. DEBUG_CRASH(("name %s is duplicate between player and team, removing...\n",tname.str()));
  719. removeTeam(i);
  720. modified = true;
  721. goto validate_team_names;
  722. }
  723. }
  724. for (i = 0; i < getNumTeams(); i++)
  725. {
  726. Dict *tdict = getTeamInfo(i)->getDict();
  727. AsciiString tname = tdict->getAsciiString(TheKey_teamName);
  728. AsciiString towner = tdict->getAsciiString(TheKey_teamOwner);
  729. SidesInfo* si = findSideInfo(towner);
  730. if (si == NULL || towner == tname)
  731. {
  732. DEBUG_LOG(("bad owner %s; reparenting to neutral...\n",towner.str()));
  733. tdict->setAsciiString(TheKey_teamOwner, AsciiString::TheEmptyString);
  734. modified = true;
  735. }
  736. // if (tdict->getType(NAMEKEY(AsciiString("teamAllies"))) != Dict::DICT_NONE)
  737. // tdict->remove(NAMEKEY(AsciiString("teamAllies")));
  738. // if (tdict->getType(NAMEKEY(AsciiString("teamEnemies"))) != Dict::DICT_NONE)
  739. // tdict->remove(NAMEKEY(AsciiString("teamEnemies")));
  740. }
  741. return modified;
  742. }
  743. // ------------------------------------------------------------------------------------------------
  744. /** CRC */
  745. // ------------------------------------------------------------------------------------------------
  746. void SidesList::crc( Xfer *xfer )
  747. {
  748. } // end crc
  749. // ------------------------------------------------------------------------------------------------
  750. /** Xfer method
  751. * Version Info:
  752. * 1: Initial version */
  753. // ------------------------------------------------------------------------------------------------
  754. void SidesList::xfer( Xfer *xfer )
  755. {
  756. // version
  757. XferVersion currentVersion = 1;
  758. XferVersion version = currentVersion;
  759. xfer->xferVersion( &version, currentVersion );
  760. // xfer num sides
  761. Int sideCount = getNumSides();
  762. xfer->xferInt( &sideCount );
  763. if( sideCount != getNumSides() )
  764. {
  765. 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" ));
  766. throw SC_INVALID_DATA;
  767. } // end if
  768. // side data
  769. ScriptList *scriptList;
  770. Bool scriptListPresent;
  771. for( Int i = 0; i < sideCount; ++i )
  772. {
  773. // xfer script list data that can change
  774. scriptList = getSideInfo( i )->getScriptList();
  775. scriptListPresent = scriptList ? TRUE : FALSE;
  776. xfer->xferBool( &scriptListPresent );
  777. if( (scriptList == NULL && scriptListPresent == TRUE) ||
  778. (scriptList != NULL && scriptListPresent == FALSE) )
  779. {
  780. DEBUG_CRASH(( "SidesList::xfer - script list missing/present mismatch\n" ));
  781. throw SC_INVALID_DATA;
  782. } // end if
  783. if( scriptListPresent )
  784. xfer->xferSnapshot( scriptList );
  785. } // end for i
  786. } // end xfer
  787. // ------------------------------------------------------------------------------------------------
  788. /** Load post process */
  789. // ------------------------------------------------------------------------------------------------
  790. void SidesList::loadPostProcess( void )
  791. {
  792. } // end loadPostProcess
  793. /* ********* BuildListInfo class ****************************/
  794. /**
  795. BuildListInfo - Constructor.
  796. */
  797. BuildListInfo::BuildListInfo(void) :
  798. m_nextBuildList(NULL),
  799. m_renderObj(NULL),
  800. m_shadowObj(NULL),
  801. m_isInitiallyBuilt(false),
  802. m_numRebuilds(0),
  803. m_angle(0),
  804. m_script(AsciiString::TheEmptyString),
  805. m_health(100),
  806. m_whiner(true),
  807. m_unsellable(false),
  808. m_repairable(true),
  809. m_objectID(INVALID_ID),
  810. m_objectTimestamp(0),
  811. m_underConstruction(false),
  812. m_isSupplyBuilding(false),
  813. m_desiredGatherers(0),
  814. m_currentGatherers(0),
  815. m_automaticallyBuild(true),
  816. m_priorityBuild(false),
  817. m_buildingName(AsciiString::TheEmptyString)
  818. {
  819. // Added by Sadullah Nader
  820. // these initialized values are necessary!!!
  821. m_location.zero();
  822. m_rallyPointOffset.x = 0.0f;
  823. m_rallyPointOffset.y = 0.0f;
  824. m_selected = FALSE;
  825. Int i;
  826. for (i=0; i<MAX_RESOURCE_GATHERERS; i++)
  827. {
  828. m_resourceGatherers[i] = INVALID_ID;
  829. }
  830. }
  831. /**
  832. BuildListInfo - Destructor - note - if linked, deletes linked items.
  833. */
  834. BuildListInfo::~BuildListInfo(void)
  835. {
  836. if (m_nextBuildList) {
  837. BuildListInfo *cur = m_nextBuildList;
  838. BuildListInfo *next;
  839. while (cur) {
  840. next = cur->getNext();
  841. cur->setNextBuildList(NULL); // prevents recursion.
  842. cur->deleteInstance();
  843. cur = next;
  844. }
  845. }
  846. }
  847. void BuildListInfo::parseStructure(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
  848. {
  849. const char* c = ini->getNextToken();
  850. AsciiString tTemplateName(c);
  851. static const FieldParse myFieldParse[] =
  852. {
  853. { "Name", INI::parseAsciiString, NULL, offsetof( BuildListInfo, m_buildingName ) },
  854. { "Location", INI::parseCoord2D, NULL, offsetof( BuildListInfo, m_location ) },
  855. { "Rebuilds", INI::parseInt, NULL, offsetof( BuildListInfo, m_numRebuilds ) },
  856. { "Angle", INI::parseAngleReal, NULL, offsetof( BuildListInfo, m_angle ) },
  857. { "InitiallyBuilt", INI::parseBool, NULL, offsetof( BuildListInfo, m_isInitiallyBuilt ) },
  858. { "RallyPointOffset", INI::parseCoord2D, NULL, offsetof( BuildListInfo, m_rallyPointOffset ) },
  859. { "AutomaticallyBuild", INI::parseBool, NULL, offsetof( BuildListInfo, m_automaticallyBuild ) },
  860. { NULL, NULL, NULL, 0 } // keep this last
  861. };
  862. BuildListInfo *buildInfo = newInstance( BuildListInfo );
  863. buildInfo->setTemplateName(tTemplateName);
  864. ini->initFromINI(buildInfo, myFieldParse);
  865. ((AISideBuildList*)instance)->addInfo(buildInfo);
  866. }
  867. /**
  868. BuildListInfo - Duplicate - note - if linked, duplicates linked items.
  869. */
  870. BuildListInfo *BuildListInfo::duplicate(void)
  871. {
  872. BuildListInfo *first = newInstance( BuildListInfo );
  873. *first = *this;
  874. first->m_nextBuildList = NULL;
  875. BuildListInfo *next = this->m_nextBuildList;
  876. BuildListInfo *cur = first;
  877. while (next) {
  878. BuildListInfo *link = newInstance( BuildListInfo );
  879. *link = *next;
  880. link->m_nextBuildList = NULL;
  881. cur->m_nextBuildList = link;
  882. cur = link;
  883. next = next->m_nextBuildList;
  884. }
  885. return first;
  886. }
  887. // ------------------------------------------------------------------------------------------------
  888. /** CRC */
  889. // ------------------------------------------------------------------------------------------------
  890. void BuildListInfo::crc( Xfer *xfer )
  891. {
  892. } // end crc
  893. // ------------------------------------------------------------------------------------------------
  894. /** Xfer method
  895. * Version Info:
  896. * 1: Initial version */
  897. // ------------------------------------------------------------------------------------------------
  898. void BuildListInfo::xfer( Xfer *xfer )
  899. {
  900. // version
  901. XferVersion currentVersion = 2;
  902. XferVersion version = currentVersion;
  903. xfer->xferVersion( &version, currentVersion );
  904. xfer->xferAsciiString( &m_buildingName );
  905. xfer->xferAsciiString( &m_templateName );
  906. xfer->xferCoord3D( &m_location );
  907. xfer->xferCoord2D( &m_rallyPointOffset );
  908. xfer->xferReal( &m_angle );
  909. xfer->xferBool( &m_isInitiallyBuilt );
  910. xfer->xferUnsignedInt( &m_numRebuilds );
  911. xfer->xferAsciiString( &m_script );
  912. xfer->xferInt( &m_health );
  913. xfer->xferBool( &m_whiner );
  914. xfer->xferBool( &m_unsellable );
  915. xfer->xferBool( &m_repairable );
  916. xfer->xferBool( &m_automaticallyBuild );
  917. // m_renderObj we don't need to xfer this, its for the editor only
  918. // m_shadowObj we don't need to xfer this, its for the editor only
  919. // m_selected we don't need to xfer this, its for the editor only
  920. xfer->xferObjectID( &m_objectID );
  921. xfer->xferUnsignedInt( &m_objectTimestamp );
  922. xfer->xferBool( &m_underConstruction );
  923. xfer->xferUser( m_resourceGatherers, sizeof( ObjectID ) * MAX_RESOURCE_GATHERERS );
  924. xfer->xferBool( &m_isSupplyBuilding );
  925. xfer->xferInt( &m_desiredGatherers );
  926. xfer->xferBool( &m_priorityBuild );
  927. if (version>=2) {
  928. xfer->xferInt(&m_currentGatherers);
  929. }
  930. } // end xfer
  931. // ------------------------------------------------------------------------------------------------
  932. /** Load post process */
  933. // ------------------------------------------------------------------------------------------------
  934. void BuildListInfo::loadPostProcess( void )
  935. {
  936. } // end loadPostProcess
  937. /* ********* TeamsInfoRec class ****************************/
  938. TeamsInfoRec::TeamsInfoRec() :
  939. m_numTeams(0), m_numTeamsAllocated(0), m_teams(NULL)
  940. {
  941. }
  942. TeamsInfoRec::TeamsInfoRec(const TeamsInfoRec& thatref) :
  943. m_numTeams(0), m_numTeamsAllocated(0), m_teams(NULL)
  944. {
  945. *this = thatref;
  946. }
  947. TeamsInfoRec::~TeamsInfoRec()
  948. {
  949. clear();
  950. }
  951. // ug, I hate having to overload stuff, but this makes it a lot easier to make copies safely
  952. TeamsInfoRec& TeamsInfoRec::operator=(const TeamsInfoRec& thatref)
  953. {
  954. const TeamsInfoRec* that = &thatref;
  955. if (this != that)
  956. {
  957. this->clear();
  958. for (int i = 0; i < that->m_numTeams; i++)
  959. {
  960. this->addTeam(that->m_teams[i].getDict());
  961. }
  962. }
  963. return *this;
  964. }
  965. void TeamsInfoRec::clear()
  966. {
  967. Int i;
  968. for (i = 0; i < m_numTeamsAllocated; i++)
  969. m_teams[i].clear();
  970. m_numTeams = 0;
  971. m_numTeamsAllocated = 0;
  972. delete [] m_teams;
  973. m_teams = NULL;
  974. }
  975. TeamsInfo *TeamsInfoRec::findTeamInfo(AsciiString name, Int* index /*= NULL*/)
  976. {
  977. for (int i = 0; i < m_numTeams; i++)
  978. {
  979. if (m_teams[i].getDict()->getAsciiString(TheKey_teamName) == name)
  980. {
  981. if (index)
  982. *index = i;
  983. return &m_teams[i];
  984. }
  985. }
  986. return NULL;
  987. }
  988. void TeamsInfoRec::addTeam(const Dict* d)
  989. {
  990. enum
  991. {
  992. TEAM_ALLOC_CHUNK = 8 ///< how many teams to alloc at a time
  993. };
  994. DEBUG_ASSERTCRASH(m_numTeams < 1024, ("hmm, seems like an awful lot of teams..."));
  995. if (m_numTeams >= m_numTeamsAllocated)
  996. {
  997. // pool[]ify
  998. TeamsInfo* nti = NEW TeamsInfo[m_numTeamsAllocated + TEAM_ALLOC_CHUNK]; // throws on failure
  999. Int i;
  1000. for (i = 0; i < m_numTeams; i++)
  1001. nti[i] = m_teams[i];
  1002. for ( ; i < m_numTeamsAllocated + TEAM_ALLOC_CHUNK; i++)
  1003. nti[i].clear();
  1004. delete [] m_teams;
  1005. m_teams = nti;
  1006. m_numTeamsAllocated += TEAM_ALLOC_CHUNK;
  1007. }
  1008. m_teams[m_numTeams++].init(d);
  1009. }
  1010. void TeamsInfoRec::removeTeam(Int i)
  1011. {
  1012. if (i < 0 || i >= m_numTeams || m_numTeams <= 1)
  1013. return;
  1014. for ( ; i < m_numTeams-1; i++)
  1015. m_teams[i] = m_teams[i+1];
  1016. for ( ; i < m_numTeamsAllocated; i++)
  1017. m_teams[i].clear();
  1018. --m_numTeams;
  1019. }