/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: SidesList.cpp ///////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Westwood Studios Pacific. // // Confidential Information // Copyright (C) 2001 - All Rights Reserved // //----------------------------------------------------------------------------- // // Project: RTS3 // // File name: SidesList.cpp // // Created: John Ahlquist, Nov 2001 // // Desc: Contains the information describing Sides (player, ai, neutral etc.) // in a scenario, including build lists for non-player sides. // //----------------------------------------------------------------------------- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/DataChunk.h" #include "Common/GameState.h" #include "Common/PlayerTemplate.h" #include "Common/WellKnownKeys.h" #include "Common/Xfer.h" #include "GameLogic/AI.h" #include "GameLogic/Scripts.h" #include "GameLogic/SidesList.h" static const Int K_SIDES_DATA_VERSION_1 = 1; static const Int K_SIDES_DATA_VERSION_2 = 2; // includes Team list. static const Int K_SIDES_DATA_VERSION_3 = 3; // includes Team list. /* ********* SidesInfo class ****************************/ /** SidesInfo - Constructor. */ SidesInfo::SidesInfo(void) : m_pBuildList(NULL), m_scripts(NULL) { } SidesInfo::SidesInfo(const SidesInfo& thatref) : m_pBuildList(NULL), m_scripts(NULL) { *this = thatref; } /** SidesInfo - Destructor - */ SidesInfo::~SidesInfo(void) { clear(); } void SidesInfo::init(const Dict* d) { m_pBuildList->deleteInstance(); m_pBuildList = NULL; m_dict.clear(); if (m_scripts) m_scripts->deleteInstance(); m_scripts = NULL; if (d) m_dict = *d; } // ug, I hate having to overload stuff, but this makes it a lot easier to make copies safely SidesInfo& SidesInfo::operator=(const SidesInfo& that) { if (this != &that) { this->clear(); this->m_dict = that.m_dict; BuildListInfo* thisBLTail = NULL; for (BuildListInfo* thatBL = that.m_pBuildList; thatBL; thatBL = thatBL->getNext()) { BuildListInfo* thisBL = newInstance( BuildListInfo ); *thisBL = *thatBL; thisBL->setNextBuildList(NULL); if (thisBLTail) thisBLTail->setNextBuildList(thisBL); else this->m_pBuildList = thisBL; thisBLTail = thisBL; } if (that.m_scripts) this->m_scripts = that.m_scripts->duplicate(); else this->m_scripts = NULL; } return *this; } /** * SidesInfo::addToBuildList - Adds a build list entry as the nth entry. * */ void SidesInfo::addToBuildList(BuildListInfo *pBuildList, Int position) { DEBUG_ASSERTLOG(pBuildList->getNext()==NULL, ("WARNING***Adding already linked element.")); BuildListInfo *pCur = NULL; while (position) { position--; if (pCur==NULL) { pCur = m_pBuildList; } else { if (pCur->getNext()) { pCur = pCur->getNext(); } else { break; // at end of list. } } } if (pCur==NULL) { // add to front of list. pBuildList->setNextBuildList(m_pBuildList); m_pBuildList = pBuildList; } else { pBuildList->setNextBuildList(pCur->getNext()); pCur->setNextBuildList(pBuildList); } } /** * SidesInfo::reorderInBuildList - Reorders a build list entry as the nth entry. * */ void SidesInfo::reorderInBuildList(BuildListInfo *pBuildList, Int newPosition) { /*Int oldPos =*/ removeFromBuildList(pBuildList); addToBuildList(pBuildList, newPosition); } /** * SidesInfo::removeFromBuildList - Removes a build list entry. * Returns the position in the list that the item occupied. * */ Int SidesInfo::removeFromBuildList(BuildListInfo *pBuildList) { DEBUG_ASSERTCRASH(pBuildList, ("Removing NULL list.")); if (pBuildList==NULL) return 0; Int position = 0; if (pBuildList == m_pBuildList) { // First item in list, so update head. m_pBuildList = pBuildList->getNext(); } else { position = 1; // Not the first item, so find the preceeding list element. BuildListInfo *pPrev = m_pBuildList; while (pPrev && (pPrev->getNext()!=pBuildList) ) { pPrev = pPrev->getNext(); position++; } DEBUG_ASSERTCRASH(pPrev, ("Removing item not in list.")); if (pPrev) { pPrev->setNextBuildList(pBuildList->getNext()); } } pBuildList->setNextBuildList(NULL); return position; } /* ********* SidesList class ****************************/ /*extern*/ SidesList *TheSidesList = NULL; ///< singleton instance of SidesList /** SidesList - Constructor. */ SidesList::SidesList(void) : m_numSides(0), m_numSkirmishSides(0) { } /** SidesList - Destructor - */ SidesList::~SidesList(void) { } /** SidesList - reset - */ void SidesList::reset(void) { clear(); } /** SidesList - clear - */ void SidesList::clear(void) { emptySides(); emptyTeams(); } /** * SidesList::ParseSidesDataChunk - read a Sides chunk. * Format is the newer CHUNKY format. * See SidesList::WriteSidesDataChunk for the writer. * Input: DataChunkInput * */ Bool SidesList::ParseSidesDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData) { DEBUG_ASSERTCRASH(TheSidesList, ("TheSidesList is null")); if (TheSidesList==NULL) return false; TheSidesList->clear(); Int count = file.readInt(); Int i, j; TheSidesList->emptySides(); for (i=0; i= MAX_PLAYER_COUNT) break; Dict d = file.readDict(); TheSidesList->addSide(&d); BuildListInfo* pBuildList; Int count = file.readInt(); for (j=0; jsetBuildingName(file.readAsciiString()); pBuildList->setTemplateName(file.readAsciiString()); Coord3D loc; loc.x = file.readReal(); loc.y = file.readReal(); loc.z = file.readReal(); loc.z = 0; // force to ground level pBuildList->setLocation(loc); pBuildList->setAngle(file.readReal()); pBuildList->setInitiallyBuilt(file.readByte()); pBuildList->setNumRebuilds(file.readInt()); if (info->version >= K_SIDES_DATA_VERSION_3) { pBuildList->setScript(file.readAsciiString()); pBuildList->setHealth(file.readInt()); pBuildList->setWhiner(file.readByte()); pBuildList->setUnsellable(file.readByte()); pBuildList->setRepairable(file.readByte()); } TheSidesList->getSideInfo(i)->addToBuildList(pBuildList, j); } } if (info->version >= K_SIDES_DATA_VERSION_2) { count = file.readInt(); TheSidesList->emptyTeams(); for (i=0; iaddTeam(&d); } } file.registerParser( AsciiString("PlayerScriptsList"), info->label, ScriptList::ParseScriptsDataChunk ); if (!file.parse(NULL)) { throw(ERROR_CORRUPT_FILE_FORMAT); } ScriptList *scripts[MAX_PLAYER_COUNT]; count = ScriptList::getReadScripts(scripts); for (i=0; igetNumSides()) { ScriptList *pSL = TheSidesList->getSideInfo(i)->getScriptList(); pSL->deleteInstance(); TheSidesList->getSideInfo(i)->setScriptList(scripts[i]); scripts[i] = NULL; } else { // Read in more players worth than we have. scripts[i]->deleteInstance(); scripts[i] = NULL; } } TheSidesList->validateSides(); DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Incorrect data file length.")); return true; } /** * SidesList::WriteSidesDataChunk - Writes a Sides chunk. * Format is the newer CHUNKY format. * See SidesList::ParseSidesDataChunk for the reader. * Input: DataChunkInput * */ void SidesList::WriteSidesDataChunk(DataChunkOutput &chunkWriter) { DEBUG_ASSERTCRASH(TheSidesList, ("TheSidesList is null")); if (TheSidesList==NULL) return; /**********HEIGHT MAP DATA ***********************/ chunkWriter.openDataChunk("SidesList", K_SIDES_DATA_VERSION_3); chunkWriter.writeInt(TheSidesList->getNumSides()); Int i; for (i=0; igetNumSides(); i++) { chunkWriter.writeDict(*TheSidesList->getSideInfo(i)->getDict()); BuildListInfo* pBuildList = TheSidesList->getSideInfo(i)->getBuildList(); Int count = 0; while (pBuildList) { count++; pBuildList = pBuildList->getNext(); } chunkWriter.writeInt(count); pBuildList = TheSidesList->getSideInfo(i)->getBuildList(); while (pBuildList) { chunkWriter.writeAsciiString(pBuildList->getBuildingName()); chunkWriter.writeAsciiString(pBuildList->getTemplateName()); chunkWriter.writeReal(pBuildList->getLocation()->x); chunkWriter.writeReal(pBuildList->getLocation()->y); chunkWriter.writeReal(pBuildList->getLocation()->z); chunkWriter.writeReal(pBuildList->getAngle()); chunkWriter.writeByte(pBuildList->isInitiallyBuilt()); chunkWriter.writeInt(pBuildList->getNumRebuilds()); // BEGIN stuff new to K_SIDES_DATA_VERSION_3 chunkWriter.writeAsciiString(pBuildList->getScript()); chunkWriter.writeInt(pBuildList->getHealth()); chunkWriter.writeByte(pBuildList->getWhiner()); chunkWriter.writeByte(pBuildList->getUnsellable()); chunkWriter.writeByte(pBuildList->getRepairable()); // END stuff new to K_SIDES_DATA_VERSION_3 pBuildList = pBuildList->getNext(); } } // BEGIN stuff new to K_SIDES_DATA_VERSION_2 chunkWriter.writeInt(TheSidesList->getNumTeams()); for (i=0; igetNumTeams(); i++) { chunkWriter.writeDict(*TheSidesList->getTeamInfo(i)->getDict()); } // END stuff new to K_SIDES_DATA_VERSION_2 ScriptList *scripts[MAX_PLAYER_COUNT]; for (i=0; igetNumSides(); i++) { scripts[i] = TheSidesList->getSideInfo(i)->getScriptList(); } ScriptList::WriteScriptsDataChunk(chunkWriter, scripts, TheSidesList->getNumSides()); chunkWriter.closeDataChunk(); Bool modified = TheSidesList->validateSides(); DEBUG_ASSERTLOG(!modified, ("*** had to clean up sideslist on read")); modified = false; // silence compiler warnings in release build } TeamsInfo *SidesList::findTeamInfo(AsciiString name, Int* index /*= NULL*/) { return m_teamrec.findTeamInfo(name, index); } SidesInfo *SidesList::findSideInfo(AsciiString name, Int* index /*= NULL*/) { for (int i = 0; i < m_numSides; i++) { if (m_sides[i].getDict()->getAsciiString(TheKey_playerName) == name) { if (index) *index = i; return &m_sides[i]; } } return NULL; } SidesInfo *SidesList::findSkirmishSideInfo(AsciiString name, Int* index /*= NULL*/) { for (int i = 0; i < m_numSkirmishSides; i++) { if (m_skirmishSides[i].getDict()->getAsciiString(TheKey_playerName) == name) { if (index) *index = i; return &m_skirmishSides[i]; } } return NULL; } static AsciiString static_readPlayerNames[MAX_PLAYER_COUNT]; /** * ParsePlayersDataChunk - read players names data chunk. * Format is the newer CHUNKY format. * Input: DataChunkInput * */ static Bool ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData) { Int numNames = file.readInt(); Int i; for (i=0; i=MAX_PLAYER_COUNT) break; static_readPlayerNames[i] = file.readAsciiString(); } DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over.")); return true; } /** * ParseTeamsDataChunk - read teams data chunk. * Format is the newer CHUNKY format. * Input: DataChunkInput * */ static Bool ParseTeamsDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData) { SidesList *sides = (SidesList *)userData; while (!file.atEndOfChunk()) { Dict teamDict = file.readDict(); AsciiString teamName = teamDict.getAsciiString(TheKey_teamName); AsciiString player = teamDict.getAsciiString(TheKey_teamOwner); if (sides->findSkirmishSideInfo(player)) { // player exists, so just add it. sides->addSkirmishTeam(&teamDict); //DEBUG_LOG(("Adding team %s\n", teamName.str())); } else { //DEBUG_LOG(("Couldn't add team %s, no player %s\n", teamName.str(), player.str())); } } DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over.")); return true; } void SidesList::prepareForMP_or_Skirmish(void) { m_skirmishTeamrec.clear(); Int i; for (i = 0; i < getNumTeams(); i++) { Dict *tdict = getTeamInfo(i)->getDict(); m_skirmishTeamrec.addTeam(tdict); } m_teamrec.clear(); for (i = 0; i < MAX_PLAYER_COUNT; i++) { m_skirmishSides[i].clear(); } m_numSkirmishSides = 0; for (i = 0; i < m_numSides; i++) { m_skirmishSides[m_numSkirmishSides] = m_sides[i]; m_numSkirmishSides++; if (m_sides[i].getDict()->getAsciiString(TheKey_playerFaction) == "FactionCivilian") { // Don't remove FactionCivilian. continue; } if (m_numSides == 1) break; // can't remove the last side. removeSide(i); i--; } Bool gotScripts = false; for (i=0; igetAsciiString(TheKey_playerFaction) == "FactionCivilian") { // Don't consider FactionCivilian. continue; } if (m_skirmishSides[i].getScriptList()==NULL) continue; if (m_skirmishSides[i].getScriptList()->getScript() != NULL || m_skirmishSides[i].getScriptList()->getScriptGroup()!=NULL) { gotScripts = true; } } if (!gotScripts) { AsciiString path = "data\\Scripts\\SkirmishScripts.scb"; DEBUG_LOG(("Skirmish map using standard scripts\n")); m_skirmishTeamrec.clear(); CachedFileInputStream theInputStream; if (theInputStream.open(path)) { ChunkInputStream *pStrm = &theInputStream; DataChunkInput file( pStrm ); file.registerParser( AsciiString("PlayerScriptsList"), AsciiString::TheEmptyString, ScriptList::ParseScriptsDataChunk ); file.registerParser( AsciiString("ScriptsPlayers"), AsciiString::TheEmptyString, ParsePlayersDataChunk ); file.registerParser( AsciiString("ScriptTeams"), AsciiString::TheEmptyString, ParseTeamsDataChunk ); if (!file.parse(this)) { DEBUG_LOG(("ERROR - Unable to read in skirmish scripts.\n")); return; } ScriptList *scripts[MAX_PLAYER_COUNT]; Int count = ScriptList::getReadScripts(scripts); Int i; for (i=0; igetDict()->getAsciiString(TheKey_playerName); if (name == static_readPlayerNames[i]) { curSide = j; break; } } if (curSide == -1) continue; ScriptList *pSL = getSkirmishSideInfo(curSide)->getScriptList(); getSkirmishSideInfo(curSide)->setScriptList(scripts[i]); scripts[i] = NULL; if (pSL) pSL->deleteInstance(); scripts[i] = NULL; } for (i=0; igetDict()->getAsciiString(TheKey_teamName); if (tname.startsWith("team")) { const char* rest = tname.str() + 4; for (int j = 0; j < m_numSides; j++) { AsciiString pname = m_sides[j].getDict()->getAsciiString(TheKey_playerName); if (strcmp(pname.str(), rest) == 0) { return true; } } } return false; } void SidesList::emptySides() { Int i; m_numSides = 0; m_numSkirmishSides = 0; for (i = 0; i < MAX_PLAYER_COUNT; i++) { m_sides[i].clear(); m_skirmishSides[i].clear(); } } void SidesList::emptyTeams() { m_teamrec.clear(); m_skirmishTeamrec.clear(); } void SidesList::addSide(const Dict* d) { DEBUG_ASSERTCRASH(m_numSides < MAX_PLAYER_COUNT, ("too many players")); if (m_numSides < MAX_PLAYER_COUNT) m_sides[m_numSides++].init(d); } void SidesList::addTeam(const Dict* d) { m_teamrec.addTeam(d); } void SidesList::addSkirmishTeam(const Dict* d) { m_skirmishTeamrec.addTeam(d); } void SidesList::removeSide(Int i) { if (i < 0 || i >= m_numSides || m_numSides <= 1) return; for ( ; i < m_numSides-1; i++) m_sides[i] = m_sides[i+1]; for ( ; i < MAX_PLAYER_COUNT; i++) m_sides[i].clear(); --m_numSides; } void SidesList::removeTeam(Int i) { m_teamrec.removeTeam(i); } Bool SidesList::validateAllyEnemyList(const AsciiString& tname, AsciiString& allies) { // owners/allies/enemies must be players. Bool modified = false; AsciiString str, newstr, token; str = allies; newstr.clear(); while (str.nextToken(&token)) { if (token == tname) { modified = true; continue; // no allies/enemies with self } SidesInfo *si = findSideInfo(token); if (!si) { modified = true; continue; // player not found. } if (!newstr.isEmpty()) newstr.concat(" "); newstr.concat(token); } allies = newstr; return modified; } void SidesList::addPlayerByTemplate(AsciiString playerTemplateName) { AsciiString playerName; UnicodeString playerDisplayName; Bool isHuman = false; if (playerTemplateName.isEmpty()) { playerName.set(""); // magic code for "neutral" playerDisplayName = L"Neutral"; isHuman = false; } else { playerName.set("Plyr"); if (playerTemplateName.startsWith("Faction")) { playerName.concat(playerTemplateName.str() + 7); } else { playerName.concat(playerTemplateName); } playerDisplayName.translate(playerName); isHuman = true; // special-case "civilian"... if (playerName == "PlyrCivilian") isHuman = false; } Dict d; d.clear(); d.setAsciiString(TheKey_playerName, playerName); d.setBool(TheKey_playerIsHuman, isHuman); d.setUnicodeString(TheKey_playerDisplayName, playerDisplayName); d.setAsciiString(TheKey_playerFaction, playerTemplateName); d.setAsciiString(TheKey_playerAllies, AsciiString::TheEmptyString); d.setAsciiString(TheKey_playerEnemies, AsciiString::TheEmptyString); addSide(&d); AsciiString playerTeamName; playerTeamName.set("team"); playerTeamName.concat(playerName); d.clear(); d.setAsciiString(TheKey_teamName, playerTeamName); d.setAsciiString(TheKey_teamOwner, playerName); d.setBool(TheKey_teamIsSingleton, true); addTeam(&d); } Bool SidesList::validateSides() { Bool modified = false; // ensure we have at least one player, and at least one neutral player. Int i; Int neutral = -1; Int num = getNumSides(); for (i = 0; i < num; i++) { if (getSideInfo(i)->getDict()->getAsciiString(TheKey_playerName).isEmpty()) { neutral = i; break; } } if (neutral == -1) { addPlayerByTemplate(AsciiString::TheEmptyString); modified = true; } // now ensure that every player has a proper "default team" for (i = 0; i < getNumSides(); i++) { Dict *pdict = getSideInfo(i)->getDict(); AsciiString pname = pdict->getAsciiString(TheKey_playerName); AsciiString tname("team"); tname.concat(pname); TeamsInfo *ti = findTeamInfo(tname); if (ti) { // make sure the team owner points back to the player. if (ti->getDict()->getAsciiString(TheKey_teamOwner) != pname) { DEBUG_CRASH(("hmm, team owner mismatch (%s) (%s), this should not normally be possible",ti->getDict()->getAsciiString(TheKey_teamOwner).str(), pname.str())); ti->getDict()->setAsciiString(TheKey_teamOwner, pname); modified = true; } // default teams are always singletons. if (!ti->getDict()->getBool(TheKey_teamIsSingleton)) { DEBUG_CRASH(("hmm, this should not normally be possible")); ti->getDict()->setBool(TheKey_teamIsSingleton, true); modified = true; } } else { DEBUG_LOG(("*** default team for player %s missing (should not be possible), adding it...\n",tname.str())); Dict d; d.setAsciiString(TheKey_teamName, tname); d.setAsciiString(TheKey_teamOwner, pname); d.setBool(TheKey_teamIsSingleton, true); addTeam(&d); modified = true; } AsciiString allies = pdict->getAsciiString(TheKey_playerAllies); AsciiString enemies = pdict->getAsciiString(TheKey_playerEnemies); // ensure all teams have valid allies & enemies. // (note that owners can be teams or players, but allies/enemies can only be teams.) if (validateAllyEnemyList(pname, allies)) { DEBUG_LOG(("bad allies...\n")); pdict->setAsciiString(TheKey_playerAllies, allies); modified = true; } if (validateAllyEnemyList(pname, enemies)) { DEBUG_LOG(("bad enemies...\n")); pdict->setAsciiString(TheKey_playerEnemies, enemies); modified = true; } } // ensure there's no overlap between team names and player names. // (if there is, the player wins and the team is whacked.) validate_team_names: for (i = 0; i < getNumTeams(); i++) { Dict *tdict = getTeamInfo(i)->getDict(); AsciiString tname = tdict->getAsciiString(TheKey_teamName); if (findSideInfo(tname)) { DEBUG_CRASH(("name %s is duplicate between player and team, removing...\n",tname.str())); removeTeam(i); modified = true; goto validate_team_names; } } for (i = 0; i < getNumTeams(); i++) { Dict *tdict = getTeamInfo(i)->getDict(); AsciiString tname = tdict->getAsciiString(TheKey_teamName); AsciiString towner = tdict->getAsciiString(TheKey_teamOwner); SidesInfo* si = findSideInfo(towner); if (si == NULL || towner == tname) { DEBUG_LOG(("bad owner %s; reparenting to neutral...\n",towner.str())); tdict->setAsciiString(TheKey_teamOwner, AsciiString::TheEmptyString); modified = true; } // if (tdict->getType(NAMEKEY(AsciiString("teamAllies"))) != Dict::DICT_NONE) // tdict->remove(NAMEKEY(AsciiString("teamAllies"))); // if (tdict->getType(NAMEKEY(AsciiString("teamEnemies"))) != Dict::DICT_NONE) // tdict->remove(NAMEKEY(AsciiString("teamEnemies"))); } return modified; } // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void SidesList::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void SidesList::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // xfer num sides Int sideCount = getNumSides(); xfer->xferInt( &sideCount ); if( sideCount != getNumSides() ) { 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" )); throw SC_INVALID_DATA; } // end if // side data ScriptList *scriptList; Bool scriptListPresent; for( Int i = 0; i < sideCount; ++i ) { // xfer script list data that can change scriptList = getSideInfo( i )->getScriptList(); scriptListPresent = scriptList ? TRUE : FALSE; xfer->xferBool( &scriptListPresent ); if( (scriptList == NULL && scriptListPresent == TRUE) || (scriptList != NULL && scriptListPresent == FALSE) ) { DEBUG_CRASH(( "SidesList::xfer - script list missing/present mismatch\n" )); throw SC_INVALID_DATA; } // end if if( scriptListPresent ) xfer->xferSnapshot( scriptList ); } // end for i } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void SidesList::loadPostProcess( void ) { } // end loadPostProcess /* ********* BuildListInfo class ****************************/ /** BuildListInfo - Constructor. */ BuildListInfo::BuildListInfo(void) : m_nextBuildList(NULL), m_renderObj(NULL), m_shadowObj(NULL), m_isInitiallyBuilt(false), m_numRebuilds(0), m_angle(0), m_script(AsciiString::TheEmptyString), m_health(100), m_whiner(true), m_unsellable(false), m_repairable(true), m_objectID(INVALID_ID), m_objectTimestamp(0), m_underConstruction(false), m_isSupplyBuilding(false), m_desiredGatherers(0), m_currentGatherers(0), m_automaticallyBuild(true), m_priorityBuild(false), m_buildingName(AsciiString::TheEmptyString) { // Added by Sadullah Nader // these initialized values are necessary!!! m_location.zero(); m_rallyPointOffset.x = 0.0f; m_rallyPointOffset.y = 0.0f; m_selected = FALSE; Int i; for (i=0; igetNext(); cur->setNextBuildList(NULL); // prevents recursion. cur->deleteInstance(); cur = next; } } } void BuildListInfo::parseStructure(INI *ini, void *instance, void* /*store*/, const void* /*userData*/) { const char* c = ini->getNextToken(); AsciiString tTemplateName(c); static const FieldParse myFieldParse[] = { { "Name", INI::parseAsciiString, NULL, offsetof( BuildListInfo, m_buildingName ) }, { "Location", INI::parseCoord2D, NULL, offsetof( BuildListInfo, m_location ) }, { "Rebuilds", INI::parseInt, NULL, offsetof( BuildListInfo, m_numRebuilds ) }, { "Angle", INI::parseAngleReal, NULL, offsetof( BuildListInfo, m_angle ) }, { "InitiallyBuilt", INI::parseBool, NULL, offsetof( BuildListInfo, m_isInitiallyBuilt ) }, { "RallyPointOffset", INI::parseCoord2D, NULL, offsetof( BuildListInfo, m_rallyPointOffset ) }, { "AutomaticallyBuild", INI::parseBool, NULL, offsetof( BuildListInfo, m_automaticallyBuild ) }, { NULL, NULL, NULL, 0 } // keep this last }; BuildListInfo *buildInfo = newInstance( BuildListInfo ); buildInfo->setTemplateName(tTemplateName); ini->initFromINI(buildInfo, myFieldParse); ((AISideBuildList*)instance)->addInfo(buildInfo); } /** BuildListInfo - Duplicate - note - if linked, duplicates linked items. */ BuildListInfo *BuildListInfo::duplicate(void) { BuildListInfo *first = newInstance( BuildListInfo ); *first = *this; first->m_nextBuildList = NULL; BuildListInfo *next = this->m_nextBuildList; BuildListInfo *cur = first; while (next) { BuildListInfo *link = newInstance( BuildListInfo ); *link = *next; link->m_nextBuildList = NULL; cur->m_nextBuildList = link; cur = link; next = next->m_nextBuildList; } return first; } // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void BuildListInfo::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void BuildListInfo::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 2; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); xfer->xferAsciiString( &m_buildingName ); xfer->xferAsciiString( &m_templateName ); xfer->xferCoord3D( &m_location ); xfer->xferCoord2D( &m_rallyPointOffset ); xfer->xferReal( &m_angle ); xfer->xferBool( &m_isInitiallyBuilt ); xfer->xferUnsignedInt( &m_numRebuilds ); xfer->xferAsciiString( &m_script ); xfer->xferInt( &m_health ); xfer->xferBool( &m_whiner ); xfer->xferBool( &m_unsellable ); xfer->xferBool( &m_repairable ); xfer->xferBool( &m_automaticallyBuild ); // m_renderObj we don't need to xfer this, its for the editor only // m_shadowObj we don't need to xfer this, its for the editor only // m_selected we don't need to xfer this, its for the editor only xfer->xferObjectID( &m_objectID ); xfer->xferUnsignedInt( &m_objectTimestamp ); xfer->xferBool( &m_underConstruction ); xfer->xferUser( m_resourceGatherers, sizeof( ObjectID ) * MAX_RESOURCE_GATHERERS ); xfer->xferBool( &m_isSupplyBuilding ); xfer->xferInt( &m_desiredGatherers ); xfer->xferBool( &m_priorityBuild ); if (version>=2) { xfer->xferInt(&m_currentGatherers); } } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void BuildListInfo::loadPostProcess( void ) { } // end loadPostProcess /* ********* TeamsInfoRec class ****************************/ TeamsInfoRec::TeamsInfoRec() : m_numTeams(0), m_numTeamsAllocated(0), m_teams(NULL) { } TeamsInfoRec::TeamsInfoRec(const TeamsInfoRec& thatref) : m_numTeams(0), m_numTeamsAllocated(0), m_teams(NULL) { *this = thatref; } TeamsInfoRec::~TeamsInfoRec() { clear(); } // ug, I hate having to overload stuff, but this makes it a lot easier to make copies safely TeamsInfoRec& TeamsInfoRec::operator=(const TeamsInfoRec& thatref) { const TeamsInfoRec* that = &thatref; if (this != that) { this->clear(); for (int i = 0; i < that->m_numTeams; i++) { this->addTeam(that->m_teams[i].getDict()); } } return *this; } void TeamsInfoRec::clear() { Int i; for (i = 0; i < m_numTeamsAllocated; i++) m_teams[i].clear(); m_numTeams = 0; m_numTeamsAllocated = 0; delete [] m_teams; m_teams = NULL; } TeamsInfo *TeamsInfoRec::findTeamInfo(AsciiString name, Int* index /*= NULL*/) { for (int i = 0; i < m_numTeams; i++) { if (m_teams[i].getDict()->getAsciiString(TheKey_teamName) == name) { if (index) *index = i; return &m_teams[i]; } } return NULL; } void TeamsInfoRec::addTeam(const Dict* d) { enum { TEAM_ALLOC_CHUNK = 8 ///< how many teams to alloc at a time }; DEBUG_ASSERTCRASH(m_numTeams < 1024, ("hmm, seems like an awful lot of teams...")); if (m_numTeams >= m_numTeamsAllocated) { // pool[]ify TeamsInfo* nti = NEW TeamsInfo[m_numTeamsAllocated + TEAM_ALLOC_CHUNK]; // throws on failure Int i; for (i = 0; i < m_numTeams; i++) nti[i] = m_teams[i]; for ( ; i < m_numTeamsAllocated + TEAM_ALLOC_CHUNK; i++) nti[i].clear(); delete [] m_teams; m_teams = nti; m_numTeamsAllocated += TEAM_ALLOC_CHUNK; } m_teams[m_numTeams++].init(d); } void TeamsInfoRec::removeTeam(Int i) { if (i < 0 || i >= m_numTeams || m_numTeams <= 1) return; for ( ; i < m_numTeams-1; i++) m_teams[i] = m_teams[i+1]; for ( ; i < m_numTeamsAllocated; i++) m_teams[i].clear(); --m_numTeams; }