/* ** 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 . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: Team.cpp ///////////////////////////////////////////////////////////////////////////////// // Team interface implementation // Author: Michael S. Booth, March 2001 /////////////////////////////////////////////////////////////////////////////////////////////////// // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/GameState.h" #include "Common/Team.h" #include "Common/ThingFactory.h" #include "Common/PerfTimer.h" #include "Common/Player.h" #include "Common/PlayerList.h" #include "Common/PlayerTemplate.h" #include "Common/ThingTemplate.h" #include "Common/WellKnownKeys.h" #include "Common/Xfer.h" #include "GameClient/Drawable.h" #include "GameLogic/SidesList.h" #include "GameLogic/Object.h" #include "GameLogic/Module/BodyModule.h" #include "GameLogic/Module/ContainModule.h" #include "GameLogic/PolygonTrigger.h" #include "GameLogic/Module/AIUpdate.h" #include "GameLogic/PartitionManager.h" #include "GameLogic/ScriptActions.h" #include "GameLogic/ScriptEngine.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif ///@todo - do delayed script evaluations for team scripts. jba. // GLOBALS //////////////////////////////////////////////////////////////////// TeamFactory *TheTeamFactory = NULL; // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ TeamRelationMap::TeamRelationMap( void ) { } // end TeamRelationMap // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ TeamRelationMap::~TeamRelationMap( void ) { // maek sure the data is clear m_map.clear(); } // end ~TeamRelationMap // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void TeamRelationMap::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info; * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void TeamRelationMap::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // team relation count TeamRelationMapType::iterator teamRelationIt; UnsignedShort teamRelationCount = m_map.size(); xfer->xferUnsignedShort( &teamRelationCount ); // team relations TeamID teamID; Relationship r; if( xfer->getXferMode() == XFER_SAVE ) { // go through all team relations for( teamRelationIt = m_map.begin(); teamRelationIt != m_map.end(); ++teamRelationIt ) { // write team ID teamID = (*teamRelationIt).first; xfer->xferUser( &teamID, sizeof( TeamID ) ); // write relationship r = (*teamRelationIt).second; xfer->xferUser( &r, sizeof( Relationship ) ); } // end for } // end if, save else { for( UnsignedShort i = 0; i < teamRelationCount; ++i ) { // read team ID xfer->xferUser( &teamID, sizeof( TeamID ) ); // read relationship xfer->xferUser( &r, sizeof( Relationship ) ); // assign relationship m_map[teamID] = r; } // end for, i } // end else load } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void TeamRelationMap::loadPostProcess( void ) { } // end loadPostProcess /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // STATIC FUNCTIONS /////////////////////////////////////////////////////////// static Bool locoSetMatches(LocomotorSurfaceTypeMask lstm, UnsignedInt surfaceBitFlags) { surfaceBitFlags = surfaceBitFlags & 0x01 | ((surfaceBitFlags & 0x02) << 2); return (surfaceBitFlags & lstm) != 0; } // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ TeamFactory::TeamFactory() { m_uniqueTeamPrototypeID = TEAM_PROTOTYPE_ID_INVALID; m_uniqueTeamID = TEAM_ID_INVALID; } // ------------------------------------------------------------------------ TeamFactory::~TeamFactory() { clear(); } // ------------------------------------------------------------------------ void TeamFactory::init( void ) { clear(); } // ------------------------------------------------------------------------ void TeamFactory::reset( void ) { m_uniqueTeamPrototypeID = TEAM_PROTOTYPE_ID_INVALID; m_uniqueTeamID = TEAM_ID_INVALID; clear(); } // ------------------------------------------------------------------------ void TeamFactory::update( void ) { } // ------------------------------------------------------------------------ void TeamFactory::clear() { // must remove it from the map before deleting the TeamProto, since // the TeamProto will try to remove itself from the list when it goes away TeamPrototypeMap tmp = m_prototypes; m_prototypes.clear(); for (TeamPrototypeMap::iterator it = tmp.begin(); it != tmp.end(); ++it) { it->second->deleteInstance(); } } // ------------------------------------------------------------------------ void TeamFactory::initFromSides(SidesList *sides) { clear(); // create the teams we need. for(Int i = 0; i < sides->getNumTeams(); i++) { Dict *d = sides->getTeamInfo(i)->getDict(); AsciiString tname = d->getAsciiString(TheKey_teamName); AsciiString oname = d->getAsciiString(TheKey_teamOwner); Bool singleton = d->getBool(TheKey_teamIsSingleton); initTeam(tname, oname, singleton, d); } } // ------------------------------------------------------------------------ void TeamFactory::initTeam(const AsciiString& name, const AsciiString& owner, Bool isSingleton, Dict *d) { DEBUG_ASSERTCRASH(findTeamPrototype(name)==NULL,("team already exists")); Player *pOwner = ThePlayerList->findPlayerWithNameKey(NAMEKEY(owner)); DEBUG_ASSERTCRASH(pOwner, ("no owner found for team %s (%s)\n",name.str(),owner.str())); if (!pOwner) pOwner = ThePlayerList->getNeutralPlayer(); /*TeamPrototype *tp =*/ newInstance(TeamPrototype)(this, name, pOwner, isSingleton, d, ++m_uniqueTeamPrototypeID); if (isSingleton) { // Create the singleton team. createInactiveTeam(name); } } //============================================================================= void TeamFactory::addTeamPrototypeToList(TeamPrototype* team) { NameKeyType nk = NAMEKEY(team->getName()); TeamPrototypeMap::iterator it = m_prototypes.find(nk); if (it != m_prototypes.end()) { DEBUG_ASSERTCRASH((*it).second==team, ("TeamFactory::addTeamPrototypeToList: Team %s already exists... skipping.", team->getName().str())); return; // already present } m_prototypes[nk] = team; } //============================================================================= void TeamFactory::removeTeamPrototypeFromList(TeamPrototype* team) { NameKeyType nk = NAMEKEY(team->getName()); TeamPrototypeMap::iterator it = m_prototypes.find(nk); if (it != m_prototypes.end()) m_prototypes.erase(it); } // ------------------------------------------------------------------------ TeamPrototype *TeamFactory::findTeamPrototype(const AsciiString& name) { NameKeyType nk = NAMEKEY(name); TeamPrototypeMap::iterator it = m_prototypes.find(nk); if (it != m_prototypes.end()) return it->second; return NULL; } // ------------------------------------------------------------------------ TeamPrototype *TeamFactory::findTeamPrototypeByID( TeamPrototypeID id ) { TeamPrototypeMap::iterator it; TeamPrototype *prototype = NULL; for( it = m_prototypes.begin(); it != m_prototypes.end(); ++it ) { prototype = it->second; if( prototype->getID() == id ) return prototype; } // end for // not found return NULL; } // ------------------------------------------------------------------------ Team *TeamFactory::findTeamByID( TeamID teamID ) { // simple case if( teamID == TEAM_ID_INVALID ) return NULL; // search all prototypes for the matching team ID TeamPrototype *tp; TeamPrototypeMap::iterator it; Team *team; for( it = m_prototypes.begin(); it != m_prototypes.end(); ++it ) { tp = (*it).second; team = tp->findTeamByID( teamID ); if( team ) return team; } return NULL; } // ------------------------------------------------------------------------ /** Creates an inactive team, suitable for adding members to as they are built. Call team->setActive() when all members are added. */ Team *TeamFactory::createInactiveTeam(const AsciiString& name) { TeamPrototype *tp = findTeamPrototype(name); if (!tp) throw ERROR_BAD_ARG; Team *t = NULL; if (tp->getIsSingleton()) { t = tp->getFirstItemIn_TeamInstanceList(); if (t) { if (tp->getTemplateInfo()->m_executeActions) { const Script *script = TheScriptEngine->findScriptByName(tp->getTemplateInfo()->m_productionCondition); if (script) { TheScriptEngine->friend_executeAction(script->getAction()); } } return t; } } t = newInstance(Team)(tp, ++m_uniqueTeamID ); if (tp->getTemplateInfo()->m_executeActions) { const Script *script = TheScriptEngine->findScriptByName(tp->getTemplateInfo()->m_productionCondition); if (script) { TheScriptEngine->friend_executeAction(script->getAction()); } } return t; } // ------------------------------------------------------------------------ Team *TeamFactory::createTeam(const AsciiString& name) { Team *t = NULL; t = createInactiveTeam(name); t->setActive(); return t; } // ------------------------------------------------------------------------ Team *TeamFactory::createTeamOnPrototype( TeamPrototype *prototype ) { if( prototype == NULL ) throw ERROR_BAD_ARG; Team *t = NULL; if( prototype->getIsSingleton() ) { t = prototype->getFirstItemIn_TeamInstanceList(); if( t ) return t; } t = newInstance(Team)( prototype, ++m_uniqueTeamID ); t->setActive(); return t; } // ------------------------------------------------------------------------ Team* TeamFactory::findTeam(const AsciiString& name) { TeamPrototype *tp = findTeamPrototype(name); if (tp) { Team *t = tp->getFirstItemIn_TeamInstanceList(); if (t == NULL && !tp->getIsSingleton()) { t = createInactiveTeam(name); } return t; } return NULL; } // ------------------------------------------------------------------------ void TeamFactory::teamAboutToBeDeleted(Team* team) { for (TeamPrototypeMap::iterator it = m_prototypes.begin(); it != m_prototypes.end(); ++it) { it->second->teamAboutToBeDeleted(team); } if (ThePlayerList) ThePlayerList->teamAboutToBeDeleted(team); } // ------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------ void TeamFactory::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------ void TeamFactory::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // unique team ID counter xfer->xferUser( &m_uniqueTeamID, sizeof( TeamID ) ); // how many team prototypes of data do we have to write UnsignedShort prototypeCount = m_prototypes.size(); xfer->xferUnsignedShort( &prototypeCount ); // // prototypes cannot change in number during run time so the count should be the // same as that already loaded into us from a map load // if( prototypeCount != m_prototypes.size() ) { DEBUG_CRASH(( "TeamFactory::xfer - Prototype count mismatch '%d should be '%d'\n", prototypeCount, m_prototypes.size() )); throw SC_INVALID_DATA; } // end if // xfer each of the prototype information TeamPrototypeMap::iterator it; TeamPrototypeID teamPrototypeID; TeamPrototype *teamPrototype; AsciiString prototypeName; if( xfer->getXferMode() == XFER_SAVE ) { // iterate each prototype and xfer if it needs to be in the save file for( it = m_prototypes.begin(); it != m_prototypes.end(); ++it ) { // get prototype teamPrototype = it->second; // xfer prototype id teamPrototypeID = teamPrototype->getID(); xfer->xferUser( &teamPrototypeID, sizeof( TeamPrototypeID ) ); // xfer prototype data xfer->xferSnapshot( teamPrototype ); } //end for, it } // end if, saving else { // read all the team prototype info for( UnsignedShort i = 0; i < prototypeCount; ++i ) { // read prototype ID xfer->xferUser( &teamPrototypeID, sizeof( TeamPrototypeID ) ); // find the prototype teamPrototype = findTeamPrototypeByID( teamPrototypeID ); // sanity if( teamPrototype == NULL ) { DEBUG_CRASH(( "TeamFactory::xfer - Unable to find team prototype by id\n" )); throw SC_INVALID_DATA; } // end if // xfer prototype data xfer->xferSnapshot( teamPrototype ); } // end for, i } // end else, loading /* // SAVE_LOAD_DEBUG if( xfer->getXferMode() == XFER_SAVE ) { FILE *fp = fopen( "TeamCheckSave.txt", "w+t" ); if( fp == NULL ) return; Object *obj; TeamPrototypeMap::iterator prototypeIt; TeamPrototype *prototype; Team *team; for( prototypeIt = m_prototypes.begin(); prototypeIt != m_prototypes.end(); ++prototypeIt ) { prototype = prototypeIt->second; fprintf( fp, "Prototype '%s' for player index '%d'\n", prototype->getName().str(), prototype->getControllingPlayer()->getPlayerIndex() ); for( DLINK_ITERATOR teamIt = prototype->iterate_TeamInstanceList(); !teamIt.done(); teamIt.advance() ) { team = teamIt.cur(); fprintf( fp, " Team Instance '%s', id is '%d'\n", team->getName().str(), team->getID() ); for( DLINK_ITERATOR objIt = team->iterate_TeamMemberList(); !objIt.done(); objIt.advance() ) { obj = objIt.cur(); fprintf( fp, " Member '%s', id '%d'\n", obj->getTemplate()->getName().str(), obj->getID() ); } } // end for } // end for fclose( fp ); } // end if, save */ } // end xfer // ------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------ void TeamFactory::loadPostProcess( void ) { // set the next unique team and prototype ID to just over the highest one in use m_uniqueTeamID = 0; m_uniqueTeamPrototypeID = 0; TeamPrototypeMap::iterator prototypeIt; TeamPrototype *prototype; Team *team; for( prototypeIt = m_prototypes.begin(); prototypeIt != m_prototypes.end(); ++prototypeIt ) { // get prototype prototype = prototypeIt->second; // do protype ID check if( prototype->getID() >= m_uniqueTeamPrototypeID ) m_uniqueTeamPrototypeID = prototype->getID() + 1; // iterate team instances on each prototype and do the team ID check for( DLINK_ITERATOR iter = prototype->iterate_TeamInstanceList(); !iter.done(); iter.advance() ) { team = iter.cur(); if( team->getID() >= m_uniqueTeamID ) m_uniqueTeamID = team->getID() + 1; } // end for } // end for, it /* // SAVE_LOAD_DEBUG FILE *fp = fopen( "TeamCheckLoad.txt", "w+t" ); if( fp == NULL ) return; Object *obj; for( prototypeIt = m_prototypes.begin(); prototypeIt != m_prototypes.end(); ++prototypeIt ) { prototype = prototypeIt->second; fprintf( fp, "Prototype '%s' for player index '%d'\n", prototype->getName().str(), prototype->getControllingPlayer()->getPlayerIndex() ); for( DLINK_ITERATOR teamIt = prototype->iterate_TeamInstanceList(); !teamIt.done(); teamIt.advance() ) { team = teamIt.cur(); fprintf( fp, " Team Instance '%s', id is '%d'\n", team->getName().str(), team->getID() ); team->reverse_TeamMemberList(); for( DLINK_ITERATOR objIt = team->iterate_TeamMemberList(); !objIt.done(); objIt.advance() ) { obj = objIt.cur(); fprintf( fp, " Member '%s', id '%d'\n", obj->getTemplate()->getName().str(), obj->getID() ); } } // end for } // end for fclose( fp ); */ } // end loadPostProcess // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ TeamTemplateInfo::TeamTemplateInfo(Dict *d) : m_numUnitsInfo(0) { Bool exists; Int min, max; AsciiString templateName; min = d->getInt(TheKey_teamUnitMinCount1, &exists); max = d->getInt(TheKey_teamUnitMaxCount1, &exists); templateName = d->getAsciiString(TheKey_teamUnitType1, &exists); if (max>0 && exists) { m_unitsInfo[m_numUnitsInfo].minUnits = min; m_unitsInfo[m_numUnitsInfo].maxUnits = max; m_unitsInfo[m_numUnitsInfo].unitThingName = templateName; m_numUnitsInfo++; } min = d->getInt(TheKey_teamUnitMinCount2, &exists); max = d->getInt(TheKey_teamUnitMaxCount2, &exists); templateName = d->getAsciiString(TheKey_teamUnitType2, &exists); if (max>0 && exists) { m_unitsInfo[m_numUnitsInfo].minUnits = min; m_unitsInfo[m_numUnitsInfo].maxUnits = max; m_unitsInfo[m_numUnitsInfo].unitThingName = templateName; m_numUnitsInfo++; } min = d->getInt(TheKey_teamUnitMinCount3, &exists); max = d->getInt(TheKey_teamUnitMaxCount3, &exists); templateName = d->getAsciiString(TheKey_teamUnitType3, &exists); if (max>0 && exists) { m_unitsInfo[m_numUnitsInfo].minUnits = min; m_unitsInfo[m_numUnitsInfo].maxUnits = max; m_unitsInfo[m_numUnitsInfo].unitThingName = templateName; m_numUnitsInfo++; } min = d->getInt(TheKey_teamUnitMinCount4, &exists); max = d->getInt(TheKey_teamUnitMaxCount4, &exists); templateName = d->getAsciiString(TheKey_teamUnitType4, &exists); if (max>0 && exists) { m_unitsInfo[m_numUnitsInfo].minUnits = min; m_unitsInfo[m_numUnitsInfo].maxUnits = max; m_unitsInfo[m_numUnitsInfo].unitThingName = templateName; m_numUnitsInfo++; } min = d->getInt(TheKey_teamUnitMinCount5, &exists); max = d->getInt(TheKey_teamUnitMaxCount5, &exists); templateName = d->getAsciiString(TheKey_teamUnitType5, &exists); if (max>0 && exists) { m_unitsInfo[m_numUnitsInfo].minUnits = min; m_unitsInfo[m_numUnitsInfo].maxUnits = max; m_unitsInfo[m_numUnitsInfo].unitThingName = templateName; m_numUnitsInfo++; } min = d->getInt(TheKey_teamUnitMinCount6, &exists); max = d->getInt(TheKey_teamUnitMaxCount6, &exists); templateName = d->getAsciiString(TheKey_teamUnitType6, &exists); if (max>0 && exists) { m_unitsInfo[m_numUnitsInfo].minUnits = min; m_unitsInfo[m_numUnitsInfo].maxUnits = max; m_unitsInfo[m_numUnitsInfo].unitThingName = templateName; m_numUnitsInfo++; } min = d->getInt(TheKey_teamUnitMinCount7, &exists); max = d->getInt(TheKey_teamUnitMaxCount7, &exists); templateName = d->getAsciiString(TheKey_teamUnitType7, &exists); if (max>0 && exists) { m_unitsInfo[m_numUnitsInfo].minUnits = min; m_unitsInfo[m_numUnitsInfo].maxUnits = max; m_unitsInfo[m_numUnitsInfo].unitThingName = templateName; m_numUnitsInfo++; } AsciiString waypoint = d->getAsciiString(TheKey_teamHome, &exists); m_homeLocation.x = m_homeLocation.y = 0; m_homeLocation.z = 0; m_hasHomeLocation = false; if (exists) { for (Waypoint *way = TheTerrainLogic->getFirstWaypoint(); way; way = way->getNext()) { if (way->getName() == waypoint) { m_homeLocation = *way->getLocation(); m_hasHomeLocation = true; } } } m_scriptOnCreate = d->getAsciiString(TheKey_teamOnCreateScript, &exists); m_isAIRecruitable = d->getBool(TheKey_teamIsAIRecruitable, &exists); if (!exists) { m_isAIRecruitable = false; } m_isBaseDefense = d->getBool(TheKey_teamIsBaseDefense, &exists); m_isPerimeterDefense = d->getBool(TheKey_teamIsPerimeterDefense, &exists); m_automaticallyReinforce = d->getBool(TheKey_teamAutoReinforce, &exists); Int interact = d->getInt(TheKey_teamAggressiveness, &exists); m_initialTeamAttitude = AI_NORMAL; if (exists) { m_initialTeamAttitude = (AttitudeType) interact; } m_transportsReturn = d->getBool(TheKey_teamTransportsReturn, &exists); m_avoidThreats = d->getBool(TheKey_teamAvoidThreats, &exists); m_attackCommonTarget = d->getBool(TheKey_teamAttackCommonTarget, &exists); m_maxInstances = d->getInt(TheKey_teamMaxInstances, &exists); m_scriptOnIdle = d->getAsciiString(TheKey_teamOnIdleScript, &exists); m_initialIdleFrames = d->getInt(TheKey_teamInitialIdleFrames, &exists); m_scriptOnEnemySighted = d->getAsciiString(TheKey_teamEnemySightedScript, &exists); m_scriptOnAllClear = d->getAsciiString(TheKey_teamAllClearScript, &exists); m_scriptOnDestroyed = d->getAsciiString(TheKey_teamOnDestroyedScript, &exists); m_destroyedThreshold = d->getReal(TheKey_teamDestroyedThreshold, &exists); m_scriptOnUnitDestroyed = d->getAsciiString(TheKey_teamOnUnitDestroyedScript, &exists); m_productionPriority = d->getInt(TheKey_teamProductionPriority, &exists); m_productionPrioritySuccessIncrease = d->getInt(TheKey_teamProductionPrioritySuccessIncrease, &exists); m_productionPriorityFailureDecrease = d->getInt(TheKey_teamProductionPriorityFailureDecrease, &exists); // Production scripts stuff m_productionCondition = d->getAsciiString(TheKey_teamProductionCondition, &exists); m_executeActions = d->getBool(TheKey_teamExecutesActionsOnCreate, &exists); // Which scripts to attempt during run? for (int i = 0; i < MAX_GENERIC_SCRIPTS; ++i) { AsciiString keyName; keyName.format("%s%d", TheNameKeyGenerator->keyToName(TheKey_teamGenericScriptHook).str(), i); m_teamGenericScripts[i] = d->getAsciiString(NAMEKEY(keyName), &exists); if (!exists) { m_teamGenericScripts[i].clear(); } } // reinforcement team info. m_transportUnitType = d->getAsciiString(TheKey_teamTransport, &exists); m_transportsExit = d->getBool(TheKey_teamTransportsExit, &exists); m_teamStartsFull = d->getBool(TheKey_teamStartsFull, &exists); m_startReinforceWaypoint = d->getAsciiString(TheKey_teamReinforcementOrigin, &exists); m_veterancy = (VeterancyLevel)d->getInt(TheKey_teamVeterancy, &exists); } // ------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------ void TeamTemplateInfo::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------ void TeamTemplateInfo::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // xfer the production priority xfer->xferInt( &m_productionPriority ); } // end xfer // ------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------ void TeamTemplateInfo::loadPostProcess( void ) { } // end loadPostProcess // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ TeamPrototype::TeamPrototype( TeamFactory *tf, const AsciiString &name, Player *ownerPlayer, Bool isSingleton, Dict *d, TeamPrototypeID id ) : m_id(id), m_factory(tf), m_name(name), m_owningPlayer(ownerPlayer), m_flags(isSingleton ? TeamPrototype::TEAM_SINGLETON : 0), m_teamTemplate(d), m_productionConditionAlwaysFalse(false), m_productionConditionScript(NULL) { DEBUG_ASSERTCRASH(!(m_owningPlayer == NULL), ("bad args to TeamPrototype ctor")); if (m_factory) m_factory->addTeamPrototypeToList(this); if (m_owningPlayer) m_owningPlayer->addTeamToList(this); m_retrievedGenericScripts = false; for (Int i = 0; i < MAX_GENERIC_SCRIPTS; ++i) { m_genericScriptsToRun[i] = NULL; } } // ------------------------------------------------------------------------ static void deleteTeamCallback(Team* o) { if (o) { TheTeamFactory->teamAboutToBeDeleted(o); o->deleteInstance(); } } TeamPrototype::~TeamPrototype() { removeAll_TeamInstanceList(deleteTeamCallback); if (m_owningPlayer) m_owningPlayer->removeTeamFromList(this); if (m_factory) m_factory->removeTeamPrototypeFromList(this); if (m_productionConditionScript) { m_productionConditionScript->deleteInstance(); } m_productionConditionScript = NULL; for (Int i = 0; i < MAX_GENERIC_SCRIPTS; ++i) { if (m_genericScriptsToRun[i]) { m_genericScriptsToRun[i]->deleteInstance(); m_genericScriptsToRun[i] = NULL; } } } // ------------------------------------------------------------------------ Player *TeamPrototype::getControllingPlayer() const { return m_owningPlayer; } // ------------------------------------------------------------------------ Team *TeamPrototype::findTeamByID( TeamID teamID ) { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { if( iter.cur()->getID() == teamID ) return iter.cur(); } return NULL; } // ------------------------------------------------------------------------ void TeamPrototype::setControllingPlayer(Player *newController) { DEBUG_ASSERTCRASH(newController, ("Attempted to set NULL player as team-owner, illegal.")); if (!newController) { return; } if (m_owningPlayer) m_owningPlayer->removeTeamFromList(this); m_owningPlayer = newController; // impossible to get here with a NULL pointer. m_owningPlayer->addTeamToList(this); } // ------------------------------------------------------------------------ void TeamPrototype::countObjectsByThingTemplate(Int numTmplates, const ThingTemplate* const* things, Bool ignoreDead, Int *counts, Bool ignoreUnderConstruction) const { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { iter.cur()->countObjectsByThingTemplate(numTmplates, things, ignoreDead, counts, ignoreUnderConstruction); } } // ------------------------------------------------------------------------ void TeamPrototype::teamAboutToBeDeleted(Team* team) { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { // iter.cur()->removeOverrideTeamRelationship(team); iter.cur()->removeOverrideTeamRelationship(team ? team->getID() : TEAM_ID_INVALID ); } } // ------------------------------------------------------------------------ Script *TeamPrototype::getGenericScript(Int scriptToRetrieve) { if (!m_retrievedGenericScripts) { m_retrievedGenericScripts = TRUE; // set this to true so we won't do the lookup again. // Go get them from the script engine, and duplicate each one. for (Int i = 0; i < MAX_GENERIC_SCRIPTS; ++i) { const Script *tmpScript = NULL; Script *scriptToSave = NULL; if (!m_teamTemplate.m_teamGenericScripts[i].isEmpty()) { tmpScript = TheScriptEngine->findScriptByName(m_teamTemplate.m_teamGenericScripts[i]); if (tmpScript) { scriptToSave = tmpScript->duplicate(); } else { DEBUG_CRASH(("We attempted to find a generic script, but couldn't. ('%s')", m_teamTemplate.m_teamGenericScripts[i].str())); } } // I now own this one. I'm responsible for cleaning it up on destruction. m_genericScriptsToRun[i] = scriptToSave; } } return m_genericScriptsToRun[scriptToRetrieve]; } // ------------------------------------------------------------------------ // Make a team more likely to be selected by the ai for building due to success. void TeamPrototype::increaseAIPriorityForSuccess(void) const { m_teamTemplate.m_productionPriority += m_teamTemplate.m_productionPrioritySuccessIncrease; } // ------------------------------------------------------------------------ // Make a team more likely to be selected by the ai for building due to success. void TeamPrototype::decreaseAIPriorityForFailure(void) const { m_teamTemplate.m_productionPriority -= m_teamTemplate.m_productionPriorityFailureDecrease; } // ------------------------------------------------------------------------ Int TeamPrototype::countBuildings(void) { int retVal = 0; for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { retVal += iter.cur()->countBuildings(); } return retVal; } // ------------------------------------------------------------------------ Int TeamPrototype::countObjects(KindOfMaskType setMask, KindOfMaskType clearMask) { int retVal = 0; for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { retVal += iter.cur()->countObjects(setMask, clearMask); } return retVal; } // ------------------------------------------------------------------------ void TeamPrototype::healAllObjects(void) { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { iter.cur()->healAllObjects(); } } // ------------------------------------------------------------------------ void TeamPrototype::iterateObjects( ObjectIterateFunc func, void *userData ) { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { iter.cur()->iterateObjects( func, userData ); } } // ------------------------------------------------------------------------ /** * Count the number of teams that have been instanced by this prototype */ Int TeamPrototype::countTeamInstances( void ) { Int count = 0; for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) count++; return count; } // ------------------------------------------------------------------------ Bool TeamPrototype::hasAnyBuildings() const { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { if (iter.cur()->hasAnyBuildings()) return true; } return false; } // ------------------------------------------------------------------------ Bool TeamPrototype::hasAnyBuildings(KindOfMaskType kindOf) const { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { if (iter.cur()->hasAnyBuildings(kindOf)) return true; } return false; } // ------------------------------------------------------------------------ Bool TeamPrototype::hasAnyUnits() const { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { if (iter.cur()->hasAnyUnits()) return true; } return false; } // ------------------------------------------------------------------------ Bool TeamPrototype::hasAnyObjects() const { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { if (iter.cur()->hasAnyObjects()) return true; } return false; } // ------------------------------------------------------------------------ void TeamPrototype::updateState(void) { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { iter.cur()->updateState(); } /* remove empty teams. */ Bool done = false; while (!done) { done = true; for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { if (iter.cur()->getFirstItemIn_TeamMemberList() == NULL) { // Team has no members. if (this->getIsSingleton()) { continue; // Don't delete singleton teams, even if they are empty. } if (iter.cur()->getControllingPlayer() && iter.cur()->getControllingPlayer()->getDefaultTeam() == iter.cur()) { // This is the player's default team, so don't remove it. continue; } // don't delete inactive teams - they are under construction if (iter.cur()->isActive() == false) { continue; } // So remove it TheTeamFactory->teamAboutToBeDeleted(iter.cur()); iter.cur()->deleteInstance(); done = false; break; // Not sure what state the iterator is in after deleting a member of the list. jba } } } } // ------------------------------------------------------------------------ Bool TeamPrototype::hasAnyBuildFacility() const { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { if (iter.cur()->hasAnyBuildFacility()) return true; } return false; } // ------------------------------------------------------------------------ void TeamPrototype::damageTeamMembers(Real amount) { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { iter.cur()->damageTeamMembers(amount); } } // ------------------------------------------------------------------------ void TeamPrototype::moveTeamTo(Coord3D destination) { for (DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance()) { iter.cur()->moveTeamTo(destination); } } // ------------------------------------------------------------------------ Bool TeamPrototype::evaluateProductionCondition(void) { if (m_productionConditionAlwaysFalse) { // Set if we don't have a script. return false; } if (m_productionConditionScript) { // If we are doing peridic evaluation, check the frame. if (TheGameLogic->getFrame()getFrameToEvaluate()) { return false; } Int delaySeconds = m_productionConditionScript->getDelayEvalSeconds(); if (delaySeconds>0) { m_productionConditionScript->setFrameToEvaluate(TheGameLogic->getFrame()+delaySeconds*LOGICFRAMES_PER_SECOND); } return TheScriptEngine->evaluateConditions(m_productionConditionScript, NULL, getControllingPlayer()); } // We don't have a script yet, so check for one. if (m_teamTemplate.m_productionCondition.isEmpty()) { // No script defined. m_productionConditionAlwaysFalse = true; return false; } const Script *pScript = TheScriptEngine->findScriptByName(m_teamTemplate.m_productionCondition); if (pScript) { // Check difficulty. switch (getControllingPlayer()->getPlayerDifficulty() ) { case DIFFICULTY_EASY: if (!pScript->isEasy()) { m_productionConditionAlwaysFalse = true; return false; } break; case DIFFICULTY_NORMAL: if (!pScript->isNormal()) { m_productionConditionAlwaysFalse = true; return false; } break; case DIFFICULTY_HARD: if (!pScript->isHard()) { m_productionConditionAlwaysFalse = true; return false; } break; } // Make a copy of the script locally, just for paranoia's sake. We can't be sure // exactly what order the teams & scripts will get reset, so be safe. m_productionConditionScript = pScript->duplicate(); return TheScriptEngine->evaluateConditions(m_productionConditionScript, NULL, getControllingPlayer()); } // Couldn't find a script. m_productionConditionAlwaysFalse = true; return false; } // ------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------ void TeamPrototype::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------ void TeamPrototype::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 2; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // owning player index Int owningPlayerIndex; if( xfer->getXferMode() == XFER_SAVE ) owningPlayerIndex = m_owningPlayer->getPlayerIndex(); xfer->xferInt( &owningPlayerIndex ); m_owningPlayer = ThePlayerList->getNthPlayer( owningPlayerIndex ); if (version>=2) { xfer->xferAsciiString(&m_attackPriorityName); } // production condition xfer->xferBool( &m_productionConditionAlwaysFalse ); // team template information xfer->xferSnapshot( &m_teamTemplate ); // xfer team instance count UnsignedShort teamInstanceCount = 0; for( DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance() ) teamInstanceCount++; xfer->xferUnsignedShort( &teamInstanceCount ); // xfer team instances Team *teamInstance; TeamID teamID; if( xfer->getXferMode() == XFER_SAVE ) { // xfer each team instance for( DLINK_ITERATOR iter = iterate_TeamInstanceList(); !iter.done(); iter.advance() ) { // get the team teamInstance = iter.cur(); // write team id teamID = teamInstance->getID(); xfer->xferUser( &teamID, sizeof( TeamID ) ); // write team data xfer->xferSnapshot( teamInstance ); } // end for } // end if, save else { // // there is no need to check to make sure the instance list is empty here ... see // the large bock comment below where we find a team given an id // // read each block for( UnsignedShort i = 0; i < teamInstanceCount; ++i ) { // read team id xfer->xferUser( &teamID, sizeof( TeamID ) ); // // find this team, if it's not there create it ... note that there will in fact // be some teams in the instance when were are loading as they are creating during // the map load. But since the team ids are reset on the map load, they are // created with exactly the same team IDs they had before // teamInstance = TheTeamFactory->findTeamByID( teamID ); if( teamInstance == NULL ) { // create team teamInstance = TheTeamFactory->createTeamOnPrototype( this ); // restore original ID we read from the file teamInstance->setID( teamID ); } // end if // xfer team data xfer->xferSnapshot( teamInstance ); } // end for, i } // end else, load } // end xfer // ------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------ void TeamPrototype::loadPostProcess( void ) { } // end loadPostProcess // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ Team::Team(TeamPrototype *proto, TeamID id ) : m_id( id ), m_proto(proto), m_enteredOrExited(false), m_active(false), m_seeEnemy(false), m_prevSeeEnemy(false), m_checkEnemySighted(false), m_isRecruitablitySet(false), m_isRecruitable(false), m_destroyThreshold(0), m_curUnits(0), m_wasIdle(false) { //Added By Sadullah Nader //Initialization(s) inserted m_created = FALSE; // m_commonAttackTarget = INVALID_ID; // allocate new relation map pools m_playerRelations = newInstance(PlayerRelationMap); m_teamRelations = newInstance(TeamRelationMap); if (proto) { proto->prependTo_TeamInstanceList(this); if (!proto->getTemplateInfo()->m_scriptOnAllClear.isEmpty() || !proto->getTemplateInfo()->m_scriptOnEnemySighted.isEmpty()) { m_checkEnemySighted = true; // Only keep track of enemy sighted if there is a script that cares. } AsciiString teamName = proto->getName(); teamName.concat(" - creating team instance."); TheScriptEngine->AppendDebugMessage(teamName, false); } for (Int i = 0; i < MAX_GENERIC_SCRIPTS; ++i) { m_shouldAttemptGenericScript[i] = true; } } // ------------------------------------------------------------------------ Team::~Team() { // DEBUG_ASSERTCRASH(getFirstItemIn_TeamMemberList() == NULL, ("Team still has members in existence")); TheScriptEngine->notifyOfTeamDestruction(this); // Tell the players a team is going away. Int i; for (i=0; igetPlayerCount(); i++) { Player *plyr = ThePlayerList->getNthPlayer(i); if (plyr) { plyr->preTeamDestroy(this); } } Object* tm; while ((tm = getFirstItemIn_TeamMemberList()) != NULL) { tm->setTeam(NULL); } //this test is valid, but will generate a 'false positive' during game teardown //DEBUG_ASSERTCRASH(!(getControllingPlayer() && getControllingPlayer()->getDefaultTeam()==this),("I am still someones default team -- sure you want to delete me?")); DEBUG_ASSERTCRASH(m_proto, ("proto should not be null")); if (m_proto && m_proto->isInList_TeamInstanceList(this)) m_proto->removeFrom_TeamInstanceList(this); // delete the relation maps (the destructor clears the actual map if any data is present) m_teamRelations->deleteInstance(); m_playerRelations->deleteInstance(); // make sure the xfer list is clear m_xferMemberIDList.clear(); } // ------------------------------------------------------------------------ Player *Team::getControllingPlayer() const { return m_proto->getControllingPlayer(); } // ------------------------------------------------------------------------ void Team::setControllingPlayer(Player *newController) { // NULL is not allowed, but is caught by TeamPrototype::setControllingPlayer() m_proto->setControllingPlayer(newController); // This function is used by one script, and it is kind of odd. The actual units // are not getting captured, the team they are on is being reassigned to a new player. // The Team doesn't change, it just starts to return a different answer when you ask for // the controlling player. I don't want to make the major change of onCapture on everyone, // so I will do the minor fix for the specific bug, which is harmless even when misused. // Tell all members to redo their looking status, as their Player has changed, but they don't know. for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { Object *obj = iter.cur(); if (!obj) continue; obj->handlePartitionCellMaintenance(); } } // ------------------------------------------------------------------------ void Team::setAttackPriorityName(AsciiString name) { if (m_proto) m_proto->setAttackPriorityName(name); } // ------------------------------------------------------------------------ void Team::getTeamAsAIGroup(AIGroup *pAIGroup) { if (!pAIGroup) { return; } // Should this clear out the pAIGroup that it receives? I don't think so, but that // would go here if so. jkmcd for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { if (iter.cur()) { pAIGroup->add(iter.cur()); } } } // ------------------------------------------------------------------------ Int Team::getTargetableCount() const { Int retVal = 0; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { Object *obj = iter.cur(); if (!obj) { continue; } if (obj->isEffectivelyDead() || (obj->getAIUpdateInterface() == NULL && !obj->isKindOf(KINDOF_STRUCTURE))) { continue; } ++retVal; } return retVal; } // ------------------------------------------------------------------------ Relationship Team::getRelationship(const Team *that) const { // do we have an override for that particular team? if so, return it. if (!m_teamRelations->m_map.empty() && that != NULL) { TeamRelationMapType::const_iterator it = m_teamRelations->m_map.find(that->getID()); if (it != m_teamRelations->m_map.end()) { return (*it).second; } } // hummm... well, do we have an override for that team's player? if (!m_playerRelations->m_map.empty() && that != NULL) { Player* thatPlayer = that->getControllingPlayer(); if (thatPlayer != NULL) { PlayerRelationMapType::const_iterator it = m_playerRelations->m_map.find(thatPlayer->getPlayerIndex()); if (it != m_playerRelations->m_map.end()) { return (*it).second; } } } // nope -- go with our Player's view on the matter. return getControllingPlayer()->getRelationship(that); } // ------------------------------------------------------------------------ void Team::setTeamTargetObject(const Object *target) { if (target==NULL) { m_commonAttackTarget = INVALID_ID; return; } // Only ai players do common attack. if (getControllingPlayer()->getPlayerType() == PLAYER_COMPUTER) { if (getControllingPlayer()->getPlayerDifficulty() == DIFFICULTY_EASY) { return; // we don't do this for easy. jba. } m_commonAttackTarget = target->getID(); } } // ------------------------------------------------------------------------ Object *Team::getTeamTargetObject(void) { if (m_commonAttackTarget == INVALID_ID) { return NULL; } Object *target = TheGameLogic->findObjectByID(m_commonAttackTarget); if (target) { //If the enemy unit is stealthed and not detected, then we can't attack it! if( target->testStatus( OBJECT_STATUS_STEALTHED ) && !target->testStatus( OBJECT_STATUS_DETECTED ) && !target->testStatus( OBJECT_STATUS_DISGUISED ) ) { target = NULL; } } if (target && target->isEffectivelyDead()) { target = NULL; } if (target && target->getContainedBy()) { target = NULL; // target entered a building or vehicle, so stop targeting. } if (target && target->isKindOf(KINDOF_AIRCRAFT)) { // It is just generally bad to have an aircraft as the team target. // Let team members acquire aircraft individually. jba. [8/27/2003] target = NULL; } if (target == NULL) { m_commonAttackTarget = INVALID_ID; } return target; } // ------------------------------------------------------------------------ void Team::setOverrideTeamRelationship( TeamID teamID, Relationship r) { if (teamID != TEAM_ID_INVALID ) { // note that this creates the entry if it doesn't exist. m_teamRelations->m_map[teamID] = r; } } // ------------------------------------------------------------------------ Bool Team::removeOverrideTeamRelationship( TeamID teamID ) { if (!m_teamRelations->m_map.empty()) { if (teamID == TEAM_ID_INVALID) { m_teamRelations->m_map.clear(); return true; } else { TeamRelationMapType::iterator it = m_teamRelations->m_map.find(teamID); if (it != m_teamRelations->m_map.end()) { m_teamRelations->m_map.erase(it); return true; } } } return false; } // ------------------------------------------------------------------------ void Team::setOverridePlayerRelationship( Int playerIndex, Relationship r ) { if (playerIndex != PLAYER_INDEX_INVALID) { // note that this creates the entry if it doesn't exist. m_playerRelations->m_map[playerIndex] = r; } } // ------------------------------------------------------------------------ Bool Team::removeOverridePlayerRelationship( Int playerIndex ) { if (!m_playerRelations->m_map.empty()) { if (playerIndex == PLAYER_INDEX_INVALID) { m_playerRelations->m_map.clear(); return true; } else { PlayerRelationMapType::iterator it = m_playerRelations->m_map.find(playerIndex); if (it != m_playerRelations->m_map.end()) { m_playerRelations->m_map.erase(it); return true; } } } return false; } // ------------------------------------------------------------------------ void Team::countObjectsByThingTemplate(Int numTmplates, const ThingTemplate* const* things, Bool ignoreDead, Int *counts, Bool ignoreUnderConstruction) const { for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { const ThingTemplate *objtmpl = iter.cur()->getTemplate(); for (Int i = 0; i < numTmplates; ++i) { //Kris: Compare if( !objtmpl->isEquivalentTo( things[i] ) ) { continue; } if (ignoreDead && iter.cur()->isEffectivelyDead()) continue; if( ignoreUnderConstruction && iter.cur()->getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) ) continue; counts[i] += 1; break; // from 'next i', NOT 'next object' } } } // ------------------------------------------------------------------------ Int Team::countBuildings(void) { int retVal = 0; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { const ThingTemplate* objtmpl = iter.cur()->getTemplate(); if (!objtmpl) { continue; } if (objtmpl->isKindOf(KINDOF_STRUCTURE)) { ++retVal; } } return retVal; } // ------------------------------------------------------------------------ Int Team::countObjects(KindOfMaskType setMask, KindOfMaskType clearMask) { int retVal = 0; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { const ThingTemplate* objtmpl = iter.cur()->getTemplate(); if (!objtmpl) { continue; } if (objtmpl->isKindOfMulti(setMask, clearMask)) { ++retVal; } } return retVal; } // ------------------------------------------------------------------------ void Team::healAllObjects(void) { for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { iter.cur()->healCompletely(); } } // ------------------------------------------------------------------------ void Team::iterateObjects( ObjectIterateFunc func, void *userData ) { for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { func( iter.cur(), userData ); } } // ------------------------------------------------------------------------ Bool Team::hasAnyBuildings() const { for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { if (iter.cur()->isEffectivelyDead()) continue; if (iter.cur()->isDestroyed()) continue; if (iter.cur()->isKindOf(KINDOF_STRUCTURE)) return true; } return false; } // ------------------------------------------------------------------------ Bool Team::hasAnyBuildings(KindOfMaskType kindOf) const { for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { if (iter.cur()->isEffectivelyDead()) continue; if (iter.cur()->isDestroyed()) continue; kindOf.set(KINDOF_STRUCTURE); if (iter.cur()->isKindOfMulti(kindOf, KINDOFMASK_NONE)) return true; } return false; } // ------------------------------------------------------------------------ Bool Team::hasAnyUnits() const { for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { if (iter.cur()->isEffectivelyDead()) continue; if (iter.cur()->isDestroyed()) continue; // If it's a structure, it's not a unit. if (iter.cur()->isKindOf(KINDOF_STRUCTURE)) continue; // If it's a projectile, it's not a unit. if (iter.cur()->isKindOf(KINDOF_PROJECTILE)) continue; // If it's a mine, it's not a unit. if (iter.cur()->isKindOf(KINDOF_MINE)) continue; return true; } return false; } // ------------------------------------------------------------------------ Bool Team::isIdle() const { Bool idle = true; // assume idle. for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { AIUpdateInterface *ai = iter.cur()->getAIUpdateInterface(); if (!ai) continue; // Non-ai things are idle. if (iter.cur()->isEffectivelyDead()) continue; if (!ai->isIdle()) { idle = false; break; } } return idle; } // ------------------------------------------------------------------------ Bool Team::hasAnyObjects() const { for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { if (iter.cur()->isEffectivelyDead()) continue; if (iter.cur()->isDestroyed()) continue; if (iter.cur()->isKindOf(KINDOF_PROJECTILE)) { continue; // shell & missiles don't count. jba. } if (iter.cur()->isKindOf(KINDOF_INERT)) { // inert stuff doesn't count. This is for radiation fields, which are living // so they can be attacked by ambulances. continue; } if (iter.cur()->isKindOf(KINDOF_MINE)) { // Mines don't count. continue; } return true; } return false; } // ------------------------------------------------------------------------ /** Clears m_enteredExited, checks & clears m_created. */ void Team::updateState(void) { m_enteredOrExited = false; if (!m_active) { return; } const TeamTemplateInfo *pInfo = m_proto->getTemplateInfo(); if (m_created) { m_created = false; // Run the on create script, if any. if (!pInfo->m_scriptOnCreate.isEmpty()) { TheScriptEngine->runScript(pInfo->m_scriptOnCreate, this); } // Set up info for the onDestroyed script, if needed. if (!pInfo->m_scriptOnDestroyed.isEmpty() ) { for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { m_curUnits++; } m_destroyThreshold = m_curUnits - (m_curUnits * pInfo->m_destroyedThreshold); if (m_destroyThreshold>m_curUnits-1) m_destroyThreshold = m_curUnits-1; if (m_destroyThreshold<0) m_destroyThreshold = 0; } } // Do enemy sighted/on clear checks. if (m_checkEnemySighted) { m_prevSeeEnemy = m_seeEnemy; m_seeEnemy = false; Bool anyAliveInTeam = false; // If we're all dead, don't do all clear. // only consider enemies. for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { if (iter.cur()->isEffectivelyDead()) continue; PartitionFilterRelationship filterTeam(iter.cur(), PartitionFilterRelationship::ALLOW_ENEMIES); // and only stuff that is not dead PartitionFilterAlive filterAlive; PartitionFilterSameMapStatus filterMapStatus(iter.cur()); PartitionFilter *filters[] = { &filterTeam, &filterAlive, &filterMapStatus, NULL }; Real visionRange = iter.cur()->getVisionRange(); anyAliveInTeam = true; Object *pObj = ThePartitionManager->getClosestObject( iter.cur(), visionRange, FROM_CENTER_2D, filters ); if (pObj) { m_seeEnemy = true; break; } } if (anyAliveInTeam) { if (m_prevSeeEnemy != m_seeEnemy) { if (m_seeEnemy) { // fire onEnemySighted TheScriptEngine->runScript(pInfo->m_scriptOnEnemySighted, this); } else { // fire on all clear. TheScriptEngine->runScript(pInfo->m_scriptOnAllClear, this); } } } } // Do onDestroyed checks. if (!pInfo->m_scriptOnDestroyed.isEmpty()) { Int prevUnits = m_curUnits; m_curUnits = 0; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { if (iter.cur()->isEffectivelyDead()) continue; m_curUnits++; } if (m_curUnits != prevUnits && m_curUnits <= m_destroyThreshold) { TheScriptEngine->runScript(pInfo->m_scriptOnDestroyed, this); m_destroyThreshold = -1; // don't trigger again. } } // Do onIdle checks. if (!pInfo->m_scriptOnIdle.isEmpty()) { Bool isIdle = true; Bool anyAliveInTeam = false; // If we're all dead, don't do all clear. for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { if (iter.cur()->isEffectivelyDead()) { continue; } AIUpdateInterface *ai = iter.cur()->getAIUpdateInterface(); if (!ai) continue; anyAliveInTeam = true; if (!ai->isIdle()) { isIdle = false; } } if (anyAliveInTeam && isIdle && m_wasIdle) { TheScriptEngine->runScript(pInfo->m_scriptOnIdle, this); } m_wasIdle = isIdle; } } // ------------------------------------------------------------------------ void Team::notifyTeamOfObjectDeath( void ) { const TeamTemplateInfo *pInfo = m_proto->getTemplateInfo(); if (!pInfo) { return; } if (pInfo->m_scriptOnUnitDestroyed.isEmpty()) { return; } TheScriptEngine->runScript(pInfo->m_scriptOnUnitDestroyed, this); } // ------------------------------------------------------------------------ Bool Team::didAllEnter(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const { // If any units entered or exited, they set this flag. if (!m_enteredOrExited) return false; Bool anyConsidered = false; Bool entered = false; Bool outside = false; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { AIUpdateInterface *ai = iter.cur()->getAIUpdateInterface(); if (ai) { const LocomotorSet& locoSet = ai->getLocomotorSet(); { if (!locoSetMatches(locoSet.getValidSurfaces(), whichToConsider)) { continue; } } } else { // things without ai should consider themselves ground units if (!locoSetMatches(LOCOMOTORSURFACE_GROUND, whichToConsider)) { continue; } } if (iter.cur()->isEffectivelyDead()) continue; if (iter.cur()->isKindOf(KINDOF_INERT)) continue; if (iter.cur()->didEnter(pTrigger)) { entered = true; } else { if (!iter.cur()->isInside(pTrigger)) { outside = true; } } // We need this in order to prevent this from returning a false positive anyConsidered = true; } return entered && !outside; } // ------------------------------------------------------------------------ Bool Team::didPartialEnter(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const { // If any units entered or exited, they set this flag. if (!m_enteredOrExited) return false; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { AIUpdateInterface *ai = iter.cur()->getAIUpdateInterface(); if (ai) { const LocomotorSet& locoSet = ai->getLocomotorSet(); { if (!locoSetMatches(locoSet.getValidSurfaces(), whichToConsider)) { continue; } } } else { // things without ai should consider themselves ground units if (!locoSetMatches(LOCOMOTORSURFACE_GROUND, whichToConsider)) { continue; } } if (iter.cur()->isEffectivelyDead()) continue; if (iter.cur()->isKindOf(KINDOF_INERT)) continue; if (iter.cur()->didEnter(pTrigger)) { return true; } } return false; } // ------------------------------------------------------------------------ Bool Team::didPartialExit(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const { // If any units entered or exited, they set this flag. if (!m_enteredOrExited) return false; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { AIUpdateInterface *ai = iter.cur()->getAIUpdateInterface(); if (ai) { const LocomotorSet& locoSet = ai->getLocomotorSet(); { if (!locoSetMatches(locoSet.getValidSurfaces(), whichToConsider)) { continue; } } } else { // things without ai should consider themselves ground units if (!locoSetMatches(LOCOMOTORSURFACE_GROUND, whichToConsider)) { continue; } } if (iter.cur()->isEffectivelyDead()) continue; if (iter.cur()->isKindOf(KINDOF_INERT)) continue; if (iter.cur()->didExit(pTrigger)) { return true; } } return false; } // ------------------------------------------------------------------------ Bool Team::didAllExit(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const { // If any units entered or exited, they set this flag. if (!m_enteredOrExited) return false; Bool anyConsidered = false; Bool exited = false; Bool inside = false; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { AIUpdateInterface *ai = iter.cur()->getAIUpdateInterface(); if (ai) { const LocomotorSet& locoSet = ai->getLocomotorSet(); { if (!locoSetMatches(locoSet.getValidSurfaces(), whichToConsider)) { continue; } } } else { // things without ai should consider themselves ground units if (!locoSetMatches(LOCOMOTORSURFACE_GROUND, whichToConsider)) { continue; } } if (iter.cur()->isEffectivelyDead()) continue; if (iter.cur()->isKindOf(KINDOF_INERT)) continue; if (iter.cur()->didExit(pTrigger)) { exited = true; } else { if (iter.cur()->isInside(pTrigger)) { inside = true; } } // We need this in order to prevent this from returning a false positive anyConsidered = true; } return anyConsidered && exited && !inside; } // ------------------------------------------------------------------------ Bool Team::allInside(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const { // empty teams are not inside. if (!hasAnyObjects()) { return false; } Bool anyConsidered = false; Bool anyOutside = false; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { AIUpdateInterface *ai = iter.cur()->getAIUpdateInterface(); if (ai) { const LocomotorSet& locoSet = ai->getLocomotorSet(); { if (!locoSetMatches(locoSet.getValidSurfaces(), whichToConsider)) { continue; } } } else { // things without ai should consider themselves ground units if (!locoSetMatches(LOCOMOTORSURFACE_GROUND, whichToConsider)) { continue; } } if (iter.cur()->isEffectivelyDead()) { continue; } if (iter.cur()->isKindOf(KINDOF_INERT)) continue; if (!iter.cur()->isInside(pTrigger)) { anyOutside = true; } // We need this in order to prevent this from returning a false positive anyConsidered = true; } return anyConsidered && !anyOutside; } // ------------------------------------------------------------------------ Bool Team::noneInside(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const { Bool anyConsidered = false; Bool anyInside = false; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { AIUpdateInterface *ai = iter.cur()->getAIUpdateInterface(); if (ai) { const LocomotorSet& locoSet = ai->getLocomotorSet(); { if (!locoSetMatches(locoSet.getValidSurfaces(), whichToConsider)) { continue; } } } else { // things without ai should consider themselves ground units if (!locoSetMatches(LOCOMOTORSURFACE_GROUND, whichToConsider)) { continue; } } // don't consider dead things. if (iter.cur()->isEffectivelyDead()) { continue; } if (iter.cur()->isKindOf(KINDOF_INERT)) continue; if (iter.cur()->isInside(pTrigger)) { anyInside = true; } // We need this in order to prevent this from returning a false positive anyConsidered = true; } return anyConsidered && !anyInside; } // ------------------------------------------------------------------------ Bool Team::someInsideSomeOutside(PolygonTrigger *pTrigger, UnsignedInt whichToConsider) const { Bool anyConsidered = false; Bool anyInside = false; Bool anyOutside = false; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { AIUpdateInterface *ai = iter.cur()->getAIUpdateInterface(); if (ai) { const LocomotorSet& locoSet = ai->getLocomotorSet(); { if (!locoSetMatches(locoSet.getValidSurfaces(), whichToConsider)) { continue; } } } else { // things without ai should consider themselves ground units if (!locoSetMatches(LOCOMOTORSURFACE_GROUND, whichToConsider)) { continue; } } // don't consider dead things. if (iter.cur()->isEffectivelyDead()) { continue; } if (iter.cur()->isKindOf(KINDOF_INERT)) continue; if (iter.cur()->isInside(pTrigger)) { anyInside = true; } else { anyOutside = true; } // In this particular case, this is unnecessary. However, unless it is a performance hit, please // leave it. anyConsidered = true; } return anyConsidered && anyInside && anyOutside; } const Coord3D* Team::getEstimateTeamPosition(void) const { // this doesn't actually calculate the team position, but rather estimates it by // returning the position of the first member of the team DLINK_ITERATOR iter = iterate_TeamMemberList(); Object *obj = iter.cur(); if (!obj) return NULL; const Coord3D *pos = iter.cur()->getPosition(); if (!pos) return NULL; return pos; } // ------------------------------------------------------------------------ void Team::deleteTeam(Bool ignoreDead) { // First off, if this Team is the Player's default team, we need to Evacuate everyone or else // Garrisoned buildings will fall victim to this deletion as well, since they were added to the // Default when captured. Design intends with this script to kill the people out from inside. // If the thing is a transport, everything will still work, as the issue at hand is the container's // wanting to change sides when emptied. The bug is that the people in the Garrisoned building // are deleted, and that changes the Team of the building, and then the DLink walks down the new team // and deletes the wrong stuff. Like every tree and civialian building on the map. // Of course, to prevent the exact same DLINK jumping bug, I must first record what guys I am going to // Evacuate, or else after the first occupied building is emptied, he will move his Next into the // same damn wrong team. if( this == getControllingPlayer()->getDefaultTeam() ) { std::list guysToMakeEvacuate; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { Object *obj = iter.cur(); if (!obj) continue; if( obj->getContain() && (obj->getContain()->getContainCount() > 0) ) { // Write them all down, so the DLINK track jumping doesn't screw me up here as well. guysToMakeEvacuate.push_back( obj ); } } for( std::list::iterator it = guysToMakeEvacuate.begin(); it != guysToMakeEvacuate.end(); /*nothing*/ ) { Object *obj = *it; it++; if( obj->getContain() ) { obj->getContain()->removeAllContained(); } } } // this doesn't actually delete the team, it deletes the members of the team. // the team itself will be deleted in updateState. for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { Object *obj = iter.cur(); if (!obj) { continue; } // The reason the comment says the team will be deleted is because it will see that it is empty. // So yes, the Team will survive for a while, but it was going to anyway. This is a script flag, so if // they use it on a team with things that can't die, then yeah, the Team will last forever. But then it is // user error. if( ignoreDead && obj->isEffectivelyDead() ) continue; TheGameLogic->destroyObject(obj); } } // ------------------------------------------------------------------------ /* Transfer our units to new team. */ void Team::transferUnitsTo(Team *newTeam) { if (this == newTeam) return; if (newTeam == NULL) return; Object *obj; while ((obj = getFirstItemIn_TeamMemberList()) != 0) { obj->setTeam(newTeam); } } // ------------------------------------------------------------------------ static Bool isInBuildVariations(const ThingTemplate* ttWithVariations, const ThingTemplate* b) { const std::vector& bv = ttWithVariations->getBuildVariations(); if (bv.empty()) return false; for (std::vector::const_iterator it = bv.begin(); it != bv.end(); ++it) { if (b->getName() == *it) return true; } return false; } // ------------------------------------------------------------------------ /* Try to recruit a unit from other teams of this player. */ Object *Team::tryToRecruit(const ThingTemplate *tTemplate, const Coord3D *teamHome, Real maxDist) { Player *myPlayer = getControllingPlayer(); Object *obj=NULL; Real distSqr = maxDist*maxDist; Object *recruit = NULL; for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() ) { if (!obj->getTemplate()->isEquivalentTo(tTemplate)) { // it might be ok, if tTemplate is really just a "build-variations" template... if (!isInBuildVariations(tTemplate, obj->getTemplate())) continue; } if (obj->getControllingPlayer() != myPlayer) continue; Team *team = obj->getTeam(); Bool isDefaultTeam = false; if (team == myPlayer->getDefaultTeam()) { isDefaultTeam = true; } if (!team->isActive()) { continue; // Team is building, so don't steal it's members yet. } if (team->getPrototype()->getTemplateInfo()->m_productionPriority>=getPrototype()->getTemplateInfo()->m_productionPriority) { continue; } Bool teamIsRecruitable = isDefaultTeam; // Default team always recruitable. if (team->getPrototype()->getTemplateInfo()->m_isAIRecruitable) { teamIsRecruitable = true; } // Check & see if individual team has been marked for recruitability. if (team->m_isRecruitablitySet) { teamIsRecruitable = team->m_isRecruitable; } if (!teamIsRecruitable) { continue; } if (obj->getAIUpdateInterface() && !obj->getAIUpdateInterface()->isRecruitable()) { continue; // can't recruit this unit. } if( obj->isDisabledByType( DISABLED_HELD ) ) { continue; // Don't recruit held units. } Real dx, dy; dx = teamHome->x - obj->getPosition()->x; dy = teamHome->y - obj->getPosition()->y; if (isDefaultTeam && recruit == NULL) { recruit = obj; distSqr = dx*dx+dy*dy; } if (dx*dx+dy*dy > distSqr) { continue; } distSqr = dx*dx+dy*dy; recruit = obj; } if (recruit!=NULL) { return recruit; } return NULL; } // ------------------------------------------------------------------------ void Team::evacuateTeam(void) { std::list objectsToProcess; for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { Object *obj = iter.cur(); if (!obj || obj->isDestroyed() || obj->isEffectivelyDead()) { continue; } ContainModuleInterface *cmi = obj->getContain(); UnsignedInt numContained = 0; if (cmi != NULL) { numContained = cmi->getContainCount(); } if (numContained > 0) { objectsToProcess.push_back(obj); } } // evacuate any containers std::list::iterator objIt; for (objIt = objectsToProcess.begin(); objIt != objectsToProcess.end(); ++objIt) { Object *obj = *objIt; ContainModuleInterface *cmi = obj->getContain(); if (cmi) { cmi->removeAllContained(); } } objectsToProcess.clear(); } // ------------------------------------------------------------------------ void Team::killTeam(void) { std::list objectsToProcess; evacuateTeam(); // beacons are effectively dead, so we need to destroy via a non-kill() method const ThingTemplate *beaconTemplate = TheThingFactory->findTemplate( getControllingPlayer()->getPlayerTemplate()->getBeaconTemplate() ); // now find objects to kill for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { Object *obj = iter.cur(); if (!obj || obj->isDestroyed() || (obj->isEffectivelyDead() && !obj->getTemplate()->isEquivalentTo(beaconTemplate))) { continue; } Team *objTeam = obj->getTeam(); // the object's team could change after having all the passengers get out. if (objTeam == this) { objectsToProcess.push_back(obj); } } // and finally, kill things std::list::iterator objIt; for (objIt = objectsToProcess.begin(); objIt != objectsToProcess.end(); ++objIt) { Object *obj = *objIt; if (obj->isKindOf(KINDOF_TECH_BUILDING)) obj->setTeam(ThePlayerList->getNeutralPlayer()->getDefaultTeam()); else obj->kill(); } objectsToProcess.clear(); } // ------------------------------------------------------------------------ Bool Team::damageTeamMembers(Real amount) { for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { if (iter.cur()->isEffectivelyDead()) continue; if (iter.cur()->isDestroyed()) continue; // do max amount of damage to object if (amount < 0.0) { iter.cur()->kill(); } else { DamageInfo damageInfo; damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE; damageInfo.in.m_deathType = DEATH_NORMAL; damageInfo.in.m_sourceID = INVALID_ID; damageInfo.in.m_amount = amount; iter.cur()->attemptDamage( &damageInfo ); } } return false; } // ------------------------------------------------------------------------ /// @todo This should give a "team move" command, not individual move orders (MSB) void Team::moveTeamTo(Coord3D destination) { for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { if (iter.cur()->isEffectivelyDead()) continue; if (iter.cur()->isDestroyed()) continue; } } // ------------------------------------------------------------------------ Bool Team::hasAnyBuildFacility() const { for (DLINK_ITERATOR iter = iterate_TeamMemberList(); !iter.done(); iter.advance()) { const ThingTemplate *objtmpl = iter.cur()->getTemplate(); if (objtmpl->isBuildFacility()) return true; } return false; } // ------------------------------------------------------------------------ //DECLARE_PERF_TIMER(updateGenericScripts) void Team::updateGenericScripts(void) { //USE_PERF_TIMER(updateGenericScripts) for (Int i = 0; i < MAX_GENERIC_SCRIPTS; ++i) { if (m_shouldAttemptGenericScript[i]) { // Does the condition succeed? If so, run it. If it is a run once script, also mark that we // shouldn't run it again. Script *script = m_proto->getGenericScript(i); if (script) { if (TheScriptEngine->evaluateConditions(script, this)) { // It was successful. if (script->isOneShot()) { m_shouldAttemptGenericScript[i] = false; } TheScriptEngine->friend_executeAction(script->getAction(), this); AsciiString msg = "Generic script '"; msg.concat(script->getName()); msg.concat("' run on team "); msg.concat(getName()); TheScriptEngine->AppendDebugMessage(msg, false); } } else { m_shouldAttemptGenericScript[i] = false; } } } } // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void Team::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer Method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void Team::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // xfer id, this is a sanity check as team id m_id should always be valid at this point TeamID teamID = m_id; xfer->xferUser( &teamID, sizeof( TeamID ) ); if( teamID != m_id ) { DEBUG_CRASH(( "Team::xfer - TeamID mismatch. Xfered '%d' but should be '%d'\n", teamID, m_id )); throw SC_INVALID_DATA; } // end if // member list count and data ObjectID memberID; UnsignedShort memberCount = 0; for( DLINK_ITERATOR< Object > objIt = iterate_TeamMemberList(); objIt.done() == FALSE; objIt.advance() ) memberCount++; xfer->xferUnsignedShort( &memberCount ); if( xfer->getXferMode() == XFER_SAVE ) { Object *obj; // save all member info for( DLINK_ITERATOR< Object > objIt = iterate_TeamMemberList(); objIt.done() == FALSE; objIt.advance() ) { // get object obj = objIt.cur(); // save id memberID = obj->getID(); xfer->xferObjectID( &memberID ); } // end for } // end if, save else { // load all members for( UnsignedShort i = 0; i < memberCount; ++i ) { // read ID xfer->xferObjectID( &memberID ); // put on pending list for later processing m_xferMemberIDList.push_back( memberID ); } // end for, i } // end else, load // state xfer->xferAsciiString( &m_state ); // entered or exited xfer->xferBool( &m_enteredOrExited ); // active status xfer->xferBool( &m_active ); // created flag xfer->xferBool( &m_created ); // check enemy sighted xfer->xferBool( &m_checkEnemySighted ); // see enemy xfer->xferBool( &m_seeEnemy ); // previous see enemy xfer->xferBool( &m_prevSeeEnemy ); // was idle xfer->xferBool( &m_wasIdle ); // destroy threshold xfer->xferInt( &m_destroyThreshold ); // current units xfer->xferInt( &m_curUnits ); // waypoint UnsignedInt currentWaypointID = m_currentWaypoint ? m_currentWaypoint->getID() : 0; xfer->xferUnsignedInt( ¤tWaypointID ); if( xfer->getXferMode() == XFER_LOAD ) m_currentWaypoint = TheTerrainLogic->getWaypointByID( currentWaypointID ); UnsignedShort shouldAttemptGenericScriptCount = MAX_GENERIC_SCRIPTS; xfer->xferUnsignedShort( &shouldAttemptGenericScriptCount ); if ( shouldAttemptGenericScriptCount != MAX_GENERIC_SCRIPTS ) { DEBUG_CRASH(("Team::xfer - The number of allowable Generic scripts has changed, and this chunk needs to be versioned.")); throw SC_INVALID_DATA; } for (Int i = 0; i < shouldAttemptGenericScriptCount; ++i) xfer->xferBool(&m_shouldAttemptGenericScript[i]); // recruitability set xfer->xferBool( &m_isRecruitablitySet ); // is recruitable xfer->xferBool( &m_isRecruitable ); // Common attack target. xfer->xferObjectID( &m_commonAttackTarget ); // team relations xfer->xferSnapshot( m_teamRelations ); // player relations xfer->xferSnapshot( m_playerRelations ); } // ene xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void Team::loadPostProcess( void ) { // // now that all objects have actually been loaded, populate the member list with // real object pointers // Object *obj; std::list< ObjectID >::const_iterator it; for( it = m_xferMemberIDList.begin(); it != m_xferMemberIDList.end(); ++it ) { // find object obj = TheGameLogic->findObjectByID( *it ); if( obj == NULL ) { DEBUG_CRASH(( "Team::loadPostProcess - Unable to post process object to member list, object ID = '%d'\n", *it )); throw SC_INVALID_DATA; } // end if // // we are now disabling this code since the objects set their team during their // own xfer function which will actually put it on the team ... however, we will sanity // check everything here to make sure that all the objects that should be on the team, // are in fact on the team // if( isInList_TeamMemberList( obj ) == FALSE ) { DEBUG_CRASH(( "Team::loadPostProcess - Object '%s'(%d) should be in team list but is not\n", obj->getTemplate()->getName().str(), obj->getID() )); throw SC_INVALID_DATA; } // end if } // end for // since we prepended the object member pointers, reverse that list so it's just like before // reverse_TeamMemberList(); // we're done with the xfer list now m_xferMemberIDList.clear(); } // end loadPostProcess // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // ------------------------------------------------------------------------