/*
** 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
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------