| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (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<count; i++)
- {
- if (i >= MAX_PLAYER_COUNT) break;
- Dict d = file.readDict();
- TheSidesList->addSide(&d);
- BuildListInfo* pBuildList;
- Int count = file.readInt();
- for (j=0; j<count; j++)
- {
- pBuildList = newInstance( BuildListInfo );
- pBuildList->setBuildingName(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; i<count; i++)
- {
- Dict d = file.readDict();
- TheSidesList->addTeam(&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; i<count; i++) {
- if (i<TheSidesList->getNumSides()) {
- 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; i<TheSidesList->getNumSides(); 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; i<TheSidesList->getNumTeams(); i++) {
- chunkWriter.writeDict(*TheSidesList->getTeamInfo(i)->getDict());
- }
- // END stuff new to K_SIDES_DATA_VERSION_2
- ScriptList *scripts[MAX_PLAYER_COUNT];
- for (i=0; i<TheSidesList->getNumSides(); 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
- *
- */
- #define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_1 1
- #define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2 2
- static Bool ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
- {
- Int readDicts = 0;
- if (info->version >= K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2) {
- readDicts = file.readInt();
- }
- Int numNames = file.readInt();
- Int i;
- for (i=0; i<numNames; i++) {
- if (i>=MAX_PLAYER_COUNT) break;
- static_readPlayerNames[i] = file.readAsciiString();
- if (readDicts) {
- Dict sideDict = file.readDict();
- }
- }
- 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; i<m_numSkirmishSides; i++) {
- if (m_skirmishSides[i].getDict()->getAsciiString(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; i<count; i++) {
- Int curSide = -1;
- Int j;
- for (j=0; j<m_numSkirmishSides; j++) {
- AsciiString name = getSkirmishSideInfo(j)->getDict()->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; i<MAX_PLAYER_COUNT; i++) {
- static_readPlayerNames[i].clear();
- }
- }
- }
- }
- Bool SidesList::isPlayerDefaultTeam(TeamsInfo *t)
- {
- // if our name is "teamfoo" and there is a player named "foo", we are a player-default team.
- AsciiString tname = t->getDict()->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; i<MAX_RESOURCE_GATHERERS; i++)
- {
- m_resourceGatherers[i] = INVALID_ID;
- }
- }
- /**
- BuildListInfo - Destructor - note - if linked, deletes linked items.
- */
- BuildListInfo::~BuildListInfo(void)
- {
- if (m_nextBuildList) {
- BuildListInfo *cur = m_nextBuildList;
- BuildListInfo *next;
- while (cur) {
- next = cur->getNext();
- 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 < 2048, ("%d teams have been allocated (so far). This seems excessive.", m_numTeams ));
- 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;
- }
|