/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Player.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Player.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#define DEFINE_SCIENCE_AVAILABILITY_NAMES
#include "Common/ActionManager.h"
#include "Common/BuildAssistant.h"
#include "Common/CRCDebug.h"
#include "Common/DisabledTypes.h"
#include "Common/GameState.h"
#include "Common/GlobalData.h"
#include "Common/MessageStream.h"
#include "Common/MiscAudio.h"
#include "Common/PerfTimer.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "Common/PlayerTemplate.h"
#include "Common/ProductionPrerequisite.h"
#include "Common/Radar.h"
#include "Common/ResourceGatheringManager.h"
#include "Common/Team.h"
#include "Common/ThingFactory.h"
#include "Common/ThingTemplate.h"
#include "Common/TunnelTracker.h"
#include "Common/Upgrade.h"
#include "Common/WellKnownKeys.h"
#include "Common/Xfer.h"
#include "Common/BitFlagsIO.h"
#include "Common/SpecialPower.h"
#include "GameClient/ControlBar.h"
#include "GameClient/Drawable.h"
#include "GameClient/Eva.h"
#include "GameClient/GameClient.h"
#include "GameClient/GameText.h"
#include "GameLogic/AI.h"
#include "GameLogic/AIPathfind.h"
#include "GameLogic/AISkirmishPlayer.h"
#include "GameLogic/ExperienceTracker.h"
#include "GameLogic/Object.h"
#include "GameLogic/Scripts.h"
#include "GameLogic/PartitionManager.h"
#include "GameLogic/SidesList.h"
#include "GameLogic/Squad.h"
#include "GameLogic/RankInfo.h"
#include "GameLogic/ScriptEngine.h"
#include "GameLogic/Weapon.h"
#include "GameLogic/Module/AIUpdate.h"
#include "GameLogic/Module/ContainModule.h"
#include "GameLogic/Module/AutoDepositUpdate.h"
#include "GameLogic/Module/StealthUpdate.h"
#include "GameLogic/Module/SpecialPowerModule.h"
#include "GameLogic/Module/SupplyTruckAIUpdate.h"
#include "GameLogic/Module/BattlePlanUpdate.h"
#include "GameLogic/VictoryConditions.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//Grey for neutral.
#define NEUTRAL_PLAYER_COLOR 0xffffffff
// ------------------------------------------------------------------------------------------------
class ClosestKindOfData
{
public:
ClosestKindOfData( void );
//In
KindOfMaskType m_setKindOf;
KindOfMaskType m_clearKindOf;
Object *m_source;
//Out
Object *m_closest;
Real m_closestDistSq;
};
// ------------------------------------------------------------------------------------------------
ClosestKindOfData::ClosestKindOfData( void )
{
m_setKindOf.clear();
m_clearKindOf.clear();
m_source = NULL;
m_closest = NULL;
m_closestDistSq = FLT_MAX;
}
// ------------------------------------------------------------------------------------------------
static void findClosestKindOf( Object *obj, void *userData )
{
ClosestKindOfData *closestData = (ClosestKindOfData *)userData;
if( ! obj->isKindOfMulti( closestData->m_setKindOf, closestData->m_clearKindOf ) )
return; // Do nothing to the magic running total pointer man.
// is this the closest one so far
Real distSq = ThePartitionManager->getDistanceSquared( closestData->m_source, obj, FROM_CENTER_2D );
if( distSq < closestData->m_closestDistSq )
{
closestData->m_closest = obj;
closestData->m_closestDistSq = distSq;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef DEBUG_CRC
#define CRCDUMPBATTLEPLANBONUSES(x,y,z) dumpBattlePlanBonuses(x, #x, y, z, __FILE__, __LINE__, FALSE)
#define DUMPBATTLEPLANBONUSES(x,y,z) dumpBattlePlanBonuses(x, #x, y, z, __FILE__, __LINE__, TRUE)
AsciiString kindofMaskAsAsciiString(KindOfMaskType m)
{
AsciiString s;
const char** kindofNames = KindOfMaskType::getBitNames();
for (Int i=KINDOF_FIRST; igetPlayerIndex():-1, (p)?((Player *)p)->getPlayerDisplayName().str():L"", (o)?o->getID():-1, (o)?o->getTemplate()->getName().str():"",
b->m_armorScalar, AS_INT(b->m_armorScalar),
b->m_bombardment, b->m_holdTheLine, b->m_searchAndDestroy,
b->m_sightRangeScalar, AS_INT(b->m_sightRangeScalar),
kindofMaskAsAsciiString(b->m_validKindOf).str(),
kindofMaskAsAsciiString(b->m_invalidKindOf).str()));
if (!doDebugLog)
return;
DEBUG_LOG(("dumpBattlePlanBonuses() %s:%d %s\n Player %d(%ls) object %d(%s) armor:%g/%8.8X bombardment:%d, holdTheLine:%d, searchAndDestroy:%d sight:%g/%8.8X, valid:%s invalid:%s\n",
fname.str(), line, name.str(),
(p)?p->getPlayerIndex():-1, (p)?((Player *)p)->getPlayerDisplayName().str():L"", (o)?o->getID():-1, (o)?o->getTemplate()->getName().str():"",
b->m_armorScalar, AS_INT(b->m_armorScalar),
b->m_bombardment, b->m_holdTheLine, b->m_searchAndDestroy,
b->m_sightRangeScalar, AS_INT(b->m_sightRangeScalar),
kindofMaskAsAsciiString(b->m_validKindOf).str(),
kindofMaskAsAsciiString(b->m_invalidKindOf).str()));
}
#else
#define DUMPBATTLEPLANBONUSES(x,y,z) {}
#define CRCDUMPBATTLEPLANBONUSES(x,y,z) {}
#endif // DEBUG_CRC
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PlayerRelationMap::PlayerRelationMap( void )
{
} // end PlayerRelationMap
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
PlayerRelationMap::~PlayerRelationMap( void )
{
// make sure the data is cleared
m_map.clear();
} // end ~PlayerRelationmap
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void PlayerRelationMap::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version
*/
// ------------------------------------------------------------------------------------------------
void PlayerRelationMap::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// player relation count
PlayerRelationMapType::iterator playerRelationIt;
UnsignedShort playerRelationCount = m_map.size();
xfer->xferUnsignedShort( &playerRelationCount );
// player relations
Int playerIndex;
Relationship r;
if( xfer->getXferMode() == XFER_SAVE )
{
// go through all player relations
for( playerRelationIt = m_map.begin(); playerRelationIt != m_map.end(); ++playerRelationIt )
{
// write player index
playerIndex = (*playerRelationIt).first;
xfer->xferInt( &playerIndex );
// write relationship
r = (*playerRelationIt).second;
xfer->xferUser( &r, sizeof( Relationship ) );
} // end for, playerRelationIt
} // end if, save
else
{
for( UnsignedShort i = 0; i < playerRelationCount; ++i )
{
// read player index
xfer->xferInt( &playerIndex );
// read relationship
xfer->xferUser( &r, sizeof( Relationship ) );
// assign relationship
m_map[ playerIndex ] = r;
} // end for, i
} // end else, load
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void PlayerRelationMap::loadPostProcess( void )
{
} // end loadPostProcess
//=============================================================================
Player::Player( Int playerIndex )
{
m_isPreorder = FALSE;
m_isPlayerDead = FALSE;
m_playerIndex = playerIndex;
// allocate new relation map pools
m_playerRelations = newInstance(PlayerRelationMap);
m_teamRelations = newInstance(TeamRelationMap);
m_upgradeList = NULL;
m_pBuildList = NULL;
#if !defined(_PLAYTEST)
m_ai = NULL;
#endif
m_resourceGatheringManager = NULL;
m_defaultTeam = NULL;
m_radarCount = 0;
m_disableProofRadarCount = 0;
m_radarDisabled = FALSE;
m_bombardBattlePlans = 0;
m_holdTheLineBattlePlans = 0;
m_searchAndDestroyBattlePlans = 0;
m_upgradesInProgress = 0;
m_upgradesCompleted = 0;
m_tunnelSystem = NULL;
m_playerTemplate = NULL;
m_visionSpiedMask = PLAYERMASK_NONE;
m_battlePlanBonuses = NULL;
m_skillPointsModifier = 1.0f;
//Added By Sadullah
//Initializations inserted
m_canBuildUnits = TRUE;
m_canBuildBase = TRUE;
m_cashBountyPercent = 0.0f;
m_color = 0;
m_currentSelection = NULL;
m_rankLevel = 0;
m_sciencePurchasePoints = 0;
m_side = 0;
m_skillPoints = 0;
Int i;
m_upgradeList = NULL;
for(i = 0; i < NUM_HOTKEY_SQUADS; i++)
{
m_squads[i] = NULL;
}
//
for (i = 0; i < MAX_PLAYER_COUNT; ++i)
{
m_attackedBy[i] = false;
m_visionSpiedBy[i] = 0;
}
m_attackedFrame = 0;
m_unitsShouldHunt = FALSE;
init( NULL );
}
//=============================================================================
void Player::init(const PlayerTemplate* pt)
{
DEBUG_ASSERTCRASH(m_playerTeamPrototypes.size() == 0, ("Player::m_playerTeamPrototypes is not empty at game start!\n"));
m_skillPointsModifier = 1.0f;
m_attackedFrame = 0;
m_isPreorder = FALSE;
m_isPlayerDead = FALSE;
m_radarCount = 0;
m_disableProofRadarCount = 0;
m_radarDisabled = FALSE;
m_bombardBattlePlans = 0;
m_holdTheLineBattlePlans = 0;
m_searchAndDestroyBattlePlans = 0;
if( m_battlePlanBonuses )
{
m_battlePlanBonuses->deleteInstance();
m_battlePlanBonuses = NULL;
}
deleteUpgradeList();
m_energy.init(this);
m_stats.init();
if (m_pBuildList != NULL)
{
m_pBuildList->deleteInstance();
m_pBuildList = NULL;
}
m_defaultTeam = NULL;
#if !defined(_PLAYTEST)
if (m_ai)
{
m_ai->deleteInstance();
}
m_ai = NULL;
#endif
if( m_resourceGatheringManager )
{
m_resourceGatheringManager->deleteInstance();
m_resourceGatheringManager = NULL;
}
for (Int i = 0; i < NUM_HOTKEY_SQUADS; ++i) {
if (m_squads[i] != NULL) {
m_squads[i]->deleteInstance();
m_squads[i] = NULL;
}
m_squads[i] = newInstance(Squad);
}
if (m_currentSelection != NULL) {
m_currentSelection->deleteInstance() ;
m_currentSelection = NULL;
}
m_currentSelection = newInstance(Squad);
if( m_tunnelSystem )
{
m_tunnelSystem->deleteInstance();
m_tunnelSystem = NULL;
}
m_canBuildBase = true;
m_canBuildUnits = true;
m_observer = false;
m_cashBountyPercent = 0.0f;
m_listInScoreScreen = TRUE;
m_unitsShouldHunt = FALSE;
#if defined(_DEBUG) || defined(_INTERNAL)
m_DEMO_ignorePrereqs = FALSE;
m_DEMO_freeBuild = FALSE;
m_DEMO_instantBuild = FALSE;
#endif
if (pt)
{
m_side = pt->getSide();
m_productionCostChanges = pt->getProductionCostChanges();
m_productionTimeChanges = pt->getProductionTimeChanges();
m_productionVeterancyLevels = pt->getProductionVeterancyLevels();
m_color = pt->getPreferredColor()->getAsInt() | 0xff000000;
m_nightColor = m_color;
m_money = *pt->getMoney();
m_money.setPlayerIndex(getPlayerIndex());
m_handicap = *pt->getHandicap();
if( m_money.countMoney() == 0 )
{
m_money.deposit( TheGlobalData->m_defaultStartingCash, FALSE );
}
m_playerDisplayName.clear();
m_playerName.clear();
m_playerNameKey = NAMEKEY_INVALID;
m_playerType = PLAYER_COMPUTER;
m_observer = pt->isObserver();
m_isPlayerDead = m_observer; // observers are dead
}
else
{
// no player template? must be the neutral player!
m_side = "";
m_productionCostChanges.clear();
m_productionTimeChanges.clear();
m_productionVeterancyLevels.clear();
m_color = NEUTRAL_PLAYER_COLOR;
m_nightColor = NEUTRAL_PLAYER_COLOR;
m_money.init();
m_handicap.init();
m_playerDisplayName = UnicodeString::TheEmptyString;
m_playerName = AsciiString::TheEmptyString;
m_playerNameKey = NAMEKEY(AsciiString::TheEmptyString);
m_playerType = PLAYER_COMPUTER;
// neutral is always "allied" with self -- this is the only thing ever allied with neutral!
setPlayerRelationship(this, ALLIES);
}
// reset each player
m_scoreKeeper.reset(m_playerIndex);
m_playerTemplate = pt;
// reset rank info
resetRank();
m_sciencesDisabled.clear();
m_sciencesHidden.clear();
{
SpecialPowerReadyTimerListIterator it = m_specialPowerReadyTimerList.begin();
while(it != m_specialPowerReadyTimerList.end())
{
SpecialPowerReadyTimerType *sprt = &(*it);
it = m_specialPowerReadyTimerList.erase( it );
if(sprt)
sprt->clear();
}
}
KindOfPercentProductionChangeListIt it = m_kindOfPercentProductionChangeList.begin();
while(it != m_kindOfPercentProductionChangeList.end())
{
KindOfPercentProductionChange *tof = *it;
it = m_kindOfPercentProductionChangeList.erase( it );
if(tof)
tof->deleteInstance();
}
}
//=============================================================================
Player::~Player()
{
m_defaultTeam = NULL;
m_playerTemplate = NULL;
for( PlayerTeamList::iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
(*it)->friend_setOwningPlayer(NULL);
}
m_playerTeamPrototypes.clear(); // empty, but don't free the contents
// delete the relation maps (the destructor clears the actual map if any data is present)
m_teamRelations->deleteInstance();
m_playerRelations->deleteInstance();
for (Int i = 0; i < NUM_HOTKEY_SQUADS; ++i) {
if (m_squads[i] != NULL) {
m_squads[i]->deleteInstance();
m_squads[i] = NULL;
}
}
if (m_currentSelection != NULL) {
m_currentSelection->deleteInstance();
m_currentSelection = NULL;
}
if( m_battlePlanBonuses )
{
m_battlePlanBonuses->deleteInstance();
m_battlePlanBonuses = NULL;
}
}
//=============================================================================
//DECLARE_PERF_TIMER(Player_getRelationship)
Relationship Player::getRelationship(const Team *that) const
{
//USE_PERF_TIMER(Player_getRelationship)
if (that)
{
// do we have an override for that particular team? if so, return it.
if (!m_teamRelations->m_map.empty())
{
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 something for that team's player?
if (!m_playerRelations->m_map.empty())
{
const 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;
}
}
}
}
return NEUTRAL;
}
//=============================================================================
void Player::setPlayerRelationship(const Player *that, Relationship r)
{
if (that != NULL)
{
// note that this creates the entry if it doesn't exist.
m_playerRelations->m_map[that->getPlayerIndex()] = r;
}
}
// ------------------------------------------------------------------------
Bool Player::removePlayerRelationship(const Player *that)
{
if (!m_playerRelations->m_map.empty())
{
if (that == NULL)
{
m_playerRelations->m_map.clear();
return true;
}
else
{
PlayerRelationMapType::iterator it = m_playerRelations->m_map.find(that->getPlayerIndex());
if (it != m_playerRelations->m_map.end())
{
m_playerRelations->m_map.erase(it);
return true;
}
}
}
return false;
}
//=============================================================================
void Player::setTeamRelationship(const Team *that, Relationship r)
{
if (that != NULL)
{
// note that this creates the entry if it doesn't exist.
m_teamRelations->m_map[that->getID()] = r;
}
}
// ------------------------------------------------------------------------
Bool Player::removeTeamRelationship(const Team *that)
{
if (!m_teamRelations->m_map.empty())
{
if (that == NULL)
{
m_teamRelations->m_map.clear();
return true;
}
else
{
TeamRelationMapType::iterator it = m_teamRelations->m_map.find(that->getID());
if (it != m_teamRelations->m_map.end())
{
m_teamRelations->m_map.erase(it);
return true;
}
}
}
return false;
}
//=============================================================================
void Player::setBuildList(BuildListInfo *pBuildList)
{
if (m_pBuildList != NULL)
{
m_pBuildList->deleteInstance();
}
m_pBuildList = pBuildList;
}
//=============================================================================
void Player::addToBuildList(Object *obj)
{
BuildListInfo *newInfo = newInstance( BuildListInfo );
newInfo->setObjectID(obj->getID());
newInfo->setTemplateName(obj->getTemplate()->getName());
newInfo->setLocation(*obj->getPosition());
newInfo->setAngle(obj->getOrientation());
newInfo->setNumRebuilds(0); // Can't rebuild.
newInfo->setNextBuildList(m_pBuildList);
m_pBuildList = newInfo;
}
//=============================================================================
void Player::addToPriorityBuildList(AsciiString templateName, Coord3D *pos, Real angle)
{
BuildListInfo *newInfo = newInstance( BuildListInfo );
newInfo->setTemplateName(templateName);
newInfo->setLocation(*pos);
newInfo->setAngle(angle);
newInfo->markPriorityBuild();
newInfo->setNumRebuilds(1); // Build once.
newInfo->setNextBuildList(m_pBuildList);
m_pBuildList = newInfo;
}
//=============================================================================
void Player::update()
{
#if !defined(_PLAYTEST)
if (m_ai)
m_ai->update();
#endif
// Allow the teams this player owns to update themselves.
for (PlayerTeamList::iterator it = m_playerTeamPrototypes.begin(); it != m_playerTeamPrototypes.end(); ++it) {
for (DLINK_ITERATOR iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
Team *team = iter.cur();
if (!team) {
continue;
}
team->updateGenericScripts();
}
}
}
//=============================================================================
void Player::newMap()
{
#if !defined(_PLAYTEST)
if (m_ai)
m_ai->newMap();
#endif
}
//=============================================================================
void Player::setPlayerType(PlayerType t, Bool skirmish)
{
m_playerType = t;
#if !defined(_PLAYTEST)
if (m_ai)
{
m_ai->deleteInstance();
}
m_ai = NULL;
if (t == PLAYER_COMPUTER)
{
if (skirmish || TheAI->getAiData()->m_forceSkirmishAI) {
// create AIPlayer and attach to this Player
m_ai = newInstance(AISkirmishPlayer)( this );
} else {
// create AIPlayer and attach to this Player
m_ai = newInstance(AIPlayer)( this );
}
}
#endif
}
//=============================================================================
// This is called from PlayerList->newGame()
//
void Player::setDefaultTeam(void) {
AsciiString tname;
tname.set("team");
tname.concat(m_playerName);
Team *dt = TheTeamFactory->findTeam(tname);
DEBUG_ASSERTCRASH(dt, ("no team"));
if (dt) {
m_defaultTeam = dt;
dt->setActive();
}
}
//=============================================================================
// This is called from PlayerList->newGame()
//
void Player::initFromDict(const Dict* d)
{
AsciiString tmplname = d->getAsciiString(TheKey_playerFaction);
const PlayerTemplate* pt = ThePlayerTemplateStore->findPlayerTemplate(NAMEKEY(tmplname));
DEBUG_ASSERTCRASH(pt != NULL, ("PlayerTemplate %s not found -- this is an obsolete map (please open and resave in WB)\n",tmplname.str()));
init(pt);
m_playerDisplayName = d->getUnicodeString(TheKey_playerDisplayName);
AsciiString pname = d->getAsciiString(TheKey_playerName);
m_playerName = pname;
m_playerNameKey = NAMEKEY(pname);
Bool exists;
Bool skirmish = false;
Bool forceHuman = false;
if (d->getBool(TheKey_playerIsSkirmish, &exists))
{
// srj sez: check to ensure the map actually has a skirmish player... ordinarily it should, but
// poorly-formed user maps might not, which would be bad, and could crash us later. so if it doesn't
// actually have a skirmish player defined for this side, declare it nonskirmish... the player won't
// really work, but it's better than crashing.
for (Int spIdx = 0; spIdx < TheSidesList->getNumSkirmishSides(); ++spIdx)
{
AsciiString spTemplateName = TheSidesList->getSkirmishSideInfo(spIdx)->getDict()->getAsciiString(TheKey_playerFaction);
const PlayerTemplate* spt = ThePlayerTemplateStore->findPlayerTemplate(NAMEKEY(spTemplateName));
if (spt && spt->getSide() == getSide())
{
skirmish = true;
break;
}
}
DEBUG_ASSERTCRASH(skirmish, ("Could not find skirmish player for side %s... quietly making into nonskirmish.", getSide().str()));
if (!skirmish)
forceHuman = true;
}
if (d->getBool(TheKey_playerIsHuman) || forceHuman)
{
setPlayerType(PLAYER_HUMAN, false);
if (d->getBool(TheKey_playerIsPreorder, &exists))
{
m_isPreorder = TRUE;
}
if (TheSidesList->getNumSkirmishSides()>0) {
// Human player gets scripts from CIVILIAN player.
AsciiString mySide = "Civilian";
Int i;
Bool found = false;
AsciiString qualTemplatePlayerName;
for (i=0; igetNumSkirmishSides(); i++) {
AsciiString templateName = TheSidesList->getSkirmishSideInfo(i)->getDict()->getAsciiString(TheKey_playerFaction);
pt = ThePlayerTemplateStore->findPlayerTemplate(NAMEKEY(templateName));
if (pt && pt->getSide() == mySide) {
qualTemplatePlayerName.format("%s%d", TheSidesList->getSkirmishSideInfo(i)->getDict()->getAsciiString(TheKey_playerName).str(), m_mpStartIndex);
found = true;
break;
}
}
if (found && TheSidesList->getSkirmishSideInfo(i)->getScriptList()) {
AsciiString qualifier;
qualifier.format("%d", m_mpStartIndex);
ScriptList *scripts = TheSidesList->getSkirmishSideInfo(i)->getScriptList()->duplicateAndQualify(
qualifier, qualTemplatePlayerName, pname);
if (TheSidesList->getSideInfo(getPlayerIndex())->getScriptList()) {
TheSidesList->getSideInfo(getPlayerIndex())->getScriptList()->deleteInstance();
}
TheSidesList->getSideInfo(getPlayerIndex())->setScriptList(scripts);
TheSidesList->getSkirmishSideInfo(i)->getScriptList()->deleteInstance();
TheSidesList->getSkirmishSideInfo(i)->setScriptList(NULL);
}
}
skirmish = false;
}
else
{
setPlayerType(PLAYER_COMPUTER, skirmish);
}
m_mpStartIndex = d->getInt(TheKey_multiplayerStartIndex, &exists);
if (skirmish) {
// Copy and qualify scripts, and teams.
AsciiString mySide = getSide();
Int i, skirmishNdx;
Bool found = false;
AsciiString qualTemplatePlayerName;
for (skirmishNdx=0; skirmishNdxgetNumSkirmishSides(); skirmishNdx++) {
AsciiString templateName = TheSidesList->getSkirmishSideInfo(skirmishNdx)->getDict()->getAsciiString(TheKey_playerFaction);
pt = ThePlayerTemplateStore->findPlayerTemplate(NAMEKEY(templateName));
if (pt && pt->getSide() == mySide) {
qualTemplatePlayerName.format("%s%d", TheSidesList->getSkirmishSideInfo(skirmishNdx)->getDict()->getAsciiString(TheKey_playerName).str(), m_mpStartIndex);
found = true;
break;
}
}
Int diffInt = d->getInt(TheKey_skirmishDifficulty, &exists);
GameDifficulty difficulty = TheScriptEngine->getGlobalDifficulty();
if (exists) {
difficulty = (GameDifficulty) diffInt;
}
#if !defined(_PLAYTEST)
if (m_ai) {
m_ai->setAIDifficulty(difficulty);
}
#endif
if (!found) {
DEBUG_CRASH(("Could not find skirmish player for side %s", mySide.str()));
} else {
m_playerName = qualTemplatePlayerName;
AsciiString qualifier;
qualifier.format("%d", m_mpStartIndex);
ScriptList *scripts = TheSidesList->getSkirmishSideInfo(skirmishNdx)->getScriptList()->duplicateAndQualify(
qualifier, qualTemplatePlayerName, pname);
ScriptList* slist = TheSidesList->getSideInfo(getPlayerIndex())->getScriptList();
if (slist)
{
slist->deleteInstance();
}
TheSidesList->getSideInfo(getPlayerIndex())->setScriptList(scripts);
for (i=0; igetNumTeams(); i++) {
if (TheSidesList->getTeamInfo(i)->getDict()->getAsciiString(TheKey_teamOwner) == pname)
{
// Remove any teams in this player.
TheSidesList->removeTeam(i);
i--;
}
}
// Now add teams.
AsciiString originalPlayerName = TheSidesList->getSkirmishSideInfo(skirmishNdx)->getDict()->getAsciiString(TheKey_playerName);
for (i=0; igetNumSkirmishTeams(); i++) {
if (TheSidesList->getSkirmishTeamInfo(i)->getDict()->getAsciiString(TheKey_teamOwner) == originalPlayerName)
{
Dict teamDict(*TheSidesList->getSkirmishTeamInfo(i)->getDict());
AsciiString teamName = teamDict.getAsciiString(TheKey_teamName);
AsciiString newName;
newName.format("%s%d", teamDict.getAsciiString(TheKey_teamName).str(), m_mpStartIndex);
if (TheSidesList->findTeamInfo(newName)) continue;
teamDict.setAsciiString(TheKey_teamOwner, pname);
teamDict.setAsciiString(TheKey_teamName, newName);
// qualify scripts.
NameKeyType nameKeys[] =
{
TheKey_teamOnCreateScript,
TheKey_teamOnIdleScript,
TheKey_teamOnUnitDestroyedScript,
TheKey_teamOnDestroyedScript,
TheKey_teamEnemySightedScript,
TheKey_teamAllClearScript,
TheKey_teamProductionCondition
};
Int j;
const Int numKeys = sizeof(nameKeys) / sizeof(NameKeyType);
AsciiString tmpStr;
for (j = 0; j < numKeys; ++j)
{
tmpStr = teamDict.getAsciiString(nameKeys[j], &exists);
if (exists && !tmpStr.isEmpty())
{
newName.format("%s%d", tmpStr.str(), m_mpStartIndex);
teamDict.setAsciiString(nameKeys[j], newName);
}
}
// Now do the TheKey_teamGenericScriptHookN (where N can be from 0 to 15.)
for (j = 0; j < MAX_GENERIC_SCRIPTS; ++j) {
AsciiString keyName;
keyName.format("%s%d", TheNameKeyGenerator->keyToName(TheKey_teamGenericScriptHook).str(), j);
tmpStr = teamDict.getAsciiString(NAMEKEY(keyName), &exists);
if (exists && !tmpStr.isEmpty())
{
newName.format("%s%d", tmpStr.str(), m_mpStartIndex);
teamDict.setAsciiString(NAMEKEY(keyName), newName);
}
}
// Done. Now add the team.
TheSidesList->addTeam(&teamDict);
}
}
}
}
if( m_resourceGatheringManager )
{
m_resourceGatheringManager->deleteInstance();
m_resourceGatheringManager = NULL;
}
m_resourceGatheringManager = newInstance(ResourceGatheringManager);
if( m_tunnelSystem )
{
m_tunnelSystem->deleteInstance();
m_tunnelSystem = NULL;
}
m_tunnelSystem = newInstance(TunnelTracker);
m_handicap.readFromDict(d);
/// @todo Ack! the todo in PlayerList::reset() mentioning the need for a Player::reset() really needs to get done.
m_playerRelations->m_map.clear(); // For now, it has been decided to just fix this one. Dear god me must reset.
m_teamRelations->m_map.clear(); // For now, it has been decided to just fix this one. Dear god me must reset.
Int i;
for ( i = 0; i < MAX_PLAYER_COUNT; ++i ) // For now, it has been decided to just fix this one. Dear god me must reset.
{
m_attackedBy[i] = false;
m_visionSpiedBy[i] = 0;
}
m_visionSpiedMask = PLAYERMASK_NONE;
Int c = d->getInt(TheKey_playerColor, &exists);
if (exists)
{
m_color = c | 0xff000000;
m_nightColor = m_color;
}
c = d->getInt(TheKey_playerNightColor, &exists);
if (exists)
{
m_nightColor = c | 0xff000000;
}
Int m = d->getInt(TheKey_playerStartMoney, &exists);
if (exists)
m_money.deposit(m);
for ( i = 0; i < NUM_HOTKEY_SQUADS; ++i ) {
if (m_squads[i] != NULL)
{
m_squads[i]->deleteInstance();
m_squads[i] = NULL;
}
m_squads[i] = newInstance( Squad );
}
if (m_currentSelection != NULL) {
m_currentSelection->deleteInstance();
m_currentSelection = NULL;
}
m_currentSelection = newInstance( Squad );
}
//=============================================================================
void Player::becomingTeamMember(Object *obj, Bool yes)
{
if (!obj)
return;
// energy production/consumption hooks, note we ignore things that are UNDER_CONSTRUCTION
if( BitTest( obj->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == FALSE )
{
obj->friend_adjustPowerForPlayer(yes);
} // end if
// when we capture a building, we need to see if there's an AutoDepositUpdate hooked to it,
// if so, award the cash bonus
if(this != ThePlayerList->getNeutralPlayer() && yes)
{
NameKeyType key_AutoDepositUpdate = NAMEKEY("AutoDepositUpdate");
AutoDepositUpdate *adu = (AutoDepositUpdate *)obj->findUpdateModule(key_AutoDepositUpdate);
if (adu != NULL) {
adu->awardInitialCaptureBonus( this );
}
}
if( getNumBattlePlansActive() > 0 && obj->areModulesReady() )
{
if( yes )
{
//We are entering a team with active battle plans so add it's bonuses now
applyBattlePlanBonusesForObject( obj );
}
else
{
//We are leaving a team with active battle plans so remove them now.
removeBattlePlanBonusesForObject( obj );
}
}
if (obj->isKindOf(KINDOF_DOZER)
&& obj->getAIUpdateInterface()
&& obj->getAIUpdateInterface()->isIdle())
{
// Need to remove it from the pick a peasant button
if (yes)
TheInGameUI->addIdleWorker(obj);
else
TheInGameUI->removeIdleWorker(obj, getPlayerIndex());
}
}
//=============================================================================
void Player::becomingLocalPlayer(Bool yes)
{
if (yes)
{
// This changes the color of the little dot on the upper right side of the screen indicating
// which team is under control.
if( TheGameClient )
{
RGBColor rgb;
rgb.setFromInt(m_color);
TheGameClient->setTeamColor(REAL_TO_INT(rgb.red*255), REAL_TO_INT(rgb.green*255), REAL_TO_INT(rgb.blue*255));
}
if( ThePartitionManager )
{
ObjectIterator *iter = ThePartitionManager->iterateAllObjects();
for( Object* object = iter->first(); object; object = iter->next() )
{
// Added support for updating the perceptions of garrisoned buildings containing enemy stealth units.
// When changing teams, it is necessary to update this information.
ContainModuleInterface *contain = object->getContain();
if( contain )
{
contain->recalcApparentControllingPlayer();
TheRadar->removeObject( object );
TheRadar->addObject( object );
}
if( object->isKindOf( KINDOF_DISGUISER ) )
{
//KM -- August 2002:
//Added support for disguised objects, based on relationships, we either show the real color or the disguised color.
Drawable *draw = object->getDrawable();
if( draw )
{
static NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
StealthUpdate *update = (StealthUpdate*)object->findUpdateModule( key_StealthUpdate );
if( update && update->isDisguised() )
{
Player *disguisedPlayer = ThePlayerList->getNthPlayer( update->getDisguisedPlayerIndex() );
if( getRelationship( object->getTeam() ) != ALLIES && isPlayerActive() )
{
//Neutrals and enemies will see this disguised unit as the team it's disguised as.
if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
draw->setIndicatorColor( disguisedPlayer->getPlayerNightColor());
else
draw->setIndicatorColor( disguisedPlayer->getPlayerColor() );
}
else
{
//Otherwise, the color will show up as the team it really belongs to.
if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
draw->setIndicatorColor(object->getNightIndicatorColor());
else
draw->setIndicatorColor( object->getIndicatorColor() );
}
TheRadar->removeObject( object );
TheRadar->addObject( object );
}
}
}
}
iter->deleteInstance();
}
}
else
{
// nothing to do
}
}
//-------------------------------------------------------------------------------------------------
/** Is this player a skirmish ai player? */
//-------------------------------------------------------------------------------------------------
Bool Player::isSkirmishAIPlayer( void )
{
#if !defined(_PLAYTEST)
return m_ai?m_ai->isSkirmishAI():false;
#else
return FALSE;
#endif
}
//----------------------------------------------------------------------------------------------------------
/**
* Find a good spot to fire a superweapon.
*/
void Player::computeSuperweaponTarget(const SpecialPowerTemplate *power, Coord3D *retPos, Int playerNdx, Real weaponRadius)
{
if (m_ai) {
m_ai->computeSuperweaponTarget(power, retPos, playerNdx, weaponRadius);
}
}
//-------------------------------------------------------------------------------------------------
/** Get this player's current enemy. NOTE - Can be NULL. */
//-------------------------------------------------------------------------------------------------
Player *Player::getCurrentEnemy( void )
{
#if !defined(_PLAYTEST)
return m_ai?m_ai->getAiEnemy():NULL;
#else
return NULL;
#endif
}
//-------------------------------------------------------------------------------------------------
/** return the player's command center.
if he has none, return null.
if he has multiple, return one arbitrarily. */
//-------------------------------------------------------------------------------------------------
struct FCCInfo
{
Player* player;
Object* cmdCenter;
};
static void doFindCommandCenter(Object* obj, void* userData)
{
if (!obj)
return;
FCCInfo* info = (FCCInfo*)userData;
if (info->cmdCenter == NULL
&& obj->isKindOf(KINDOF_COMMANDCENTER)
&& obj->getTemplate()->getDefaultOwningSide() == info->player->getSide()
&& !obj->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)
&& !obj->testStatus(OBJECT_STATUS_SOLD))
{
info->cmdCenter = obj;
}
}
Object* Player::findNaturalCommandCenter()
{
FCCInfo info;
info.player = this;
info.cmdCenter = NULL;
iterateObjects(doFindCommandCenter, &info);
return info.cmdCenter;
}
//-------------------------------------------------------------------------------------------------
/** Difficulty level for this player */
//-------------------------------------------------------------------------------------------------
GameDifficulty Player::getPlayerDifficulty(void) const
{
#if !defined(_PLAYTEST)
if (m_ai) {
return m_ai->getAIDifficulty();
}
#endif
return TheScriptEngine->getGlobalDifficulty();
}
//-------------------------------------------------------------------------------------------------
/** Do any bridges need repair, and if so repair them. */
//-------------------------------------------------------------------------------------------------
Bool Player::checkBridges(Object *unit, Waypoint *way)
{
#if !defined(_PLAYTEST)
return m_ai?m_ai->checkBridges(unit, way):false;
#else
return FALSE;
#endif
}
//-------------------------------------------------------------------------------------------------
/** Do any bridges need repair, and if so repair them. */
//-------------------------------------------------------------------------------------------------
Bool Player::getAiBaseCenter(Coord3D *pos)
{
#if !defined(_PLAYTEST)
return m_ai?m_ai->getBaseCenter(pos):false;
#else
return FALSE;
#endif
}
//-------------------------------------------------------------------------------------------------
/** Repair bridge or structure. */
//-------------------------------------------------------------------------------------------------
void Player::repairStructure(ObjectID structureID)
{
#if !defined(_PLAYTEST)
if (m_ai) {
m_ai->repairStructure(structureID);
}
#endif
}
//-------------------------------------------------------------------------------------------------
/** A unit was just created and is ready to control */
//-------------------------------------------------------------------------------------------------
void Player::onUnitCreated( Object *factory, Object *unit )
{
// When a a unit is completed, it becomes "real" as far as scripting is
// concerned. jba.
TheScriptEngine->notifyOfObjectCreationOrDestruction();
// increment our scorekeeper
m_scoreKeeper.addObjectBuilt(unit);
#if !defined(_PLAYTEST)
// ai notification callback
if( m_ai )
m_ai->onUnitProduced( factory, unit );
#endif
} // end onUnitCreated
//-------------------------------------------------------------------------------------------------
/** Is the nearest supply source safe? */
//-------------------------------------------------------------------------------------------------
Bool Player::isSupplySourceSafe( Int minSupplies )
{
// ai query
if( m_ai )
return m_ai->isSupplySourceSafe( minSupplies );
return true;
} // isSupplySourceSafe
//-------------------------------------------------------------------------------------------------
/** Is a supply source attacked? */
//-------------------------------------------------------------------------------------------------
Bool Player::isSupplySourceAttacked( void )
{
// ai query
if( m_ai )
return m_ai->isSupplySourceAttacked( );
return false;
} // isSupplySourceSafe
//-------------------------------------------------------------------------------------------------
/** Set delay between team production */
//-------------------------------------------------------------------------------------------------
void Player::setTeamDelaySeconds(Int delay )
{
// ai action
if( m_ai )
m_ai->setTeamDelaySeconds( delay );
} // guardSupplyCenter
//-------------------------------------------------------------------------------------------------
/** Guard supply center */
//-------------------------------------------------------------------------------------------------
void Player::guardSupplyCenter( Team *team, Int minSupplies )
{
// ai action
if( m_ai )
m_ai->guardSupplyCenter( team, minSupplies );
} // guardSupplyCenter
//-------------------------------------------------------------------------------------------------
/** A team is about to be destroyed */
//-------------------------------------------------------------------------------------------------
void Player::preTeamDestroy( const Team *team )
{
// ai notification callback
if( m_ai )
m_ai->aiPreTeamDestroy( team );
} // preTeamDestroy
//-------------------------------------------------------------------------------------------------
/// a structuer was just created, but is under construction
//-------------------------------------------------------------------------------------------------
void Player::onStructureCreated( Object *builder, Object *structure )
{
} // end onStructureCreated
//-------------------------------------------------------------------------------------------------
/// a structure that was under construction has become completed
//-------------------------------------------------------------------------------------------------
void Player::onStructureConstructionComplete( Object *builder, Object *structure, Bool isRebuild )
{
// When a a structure is completed, it becomes "real" as far as scripting is
// concerned. jba.
TheScriptEngine->notifyOfObjectCreationOrDestruction();
// Update the pathfind footprint. Sometimes when rubble has to be removed
// to build, the initial footprint while building is goofy. jba.
TheAI->pathfinder()->removeObjectFromPathfindMap(structure);
TheAI->pathfinder()->addObjectToPathfindMap(structure);
// increment our scorekeeper
if (isRebuild == FALSE) {
m_scoreKeeper.addObjectBuilt(structure);
m_scoreKeeper.addMoneySpent(structure->getTemplate()->calcCostToBuild(this));
}
structure->friend_adjustPowerForPlayer(TRUE);
#if !defined(_PLAYTEST)
// ai notification callback
if( m_ai )
m_ai->onStructureProduced( builder, structure );
#endif
// the GUI needs to re-evaluate the information being displayed to the user now
if( TheControlBar )
TheControlBar->markUIDirty();
// This object may require us to play some EVA sounds.
Player *localPlayer = ThePlayerList->getLocalPlayer();
if (localPlayer == structure->getControllingPlayer() || localPlayer->getRelationship(structure->getTeam()) != ENEMIES)
return;
// We need to play some EVA sounds.
if (structure->hasSpecialPower(SPECIAL_PARTICLE_UPLINK_CANNON))
TheEva->setShouldPlay(EVA_SuperweaponDetected_ParticleCannon);
if (structure->hasSpecialPower(SPECIAL_NEUTRON_MISSILE))
TheEva->setShouldPlay(EVA_SuperweaponDetected_Nuke);
if (structure->hasSpecialPower(SPECIAL_SCUD_STORM))
TheEva->setShouldPlay(EVA_SuperweaponDetected_ScudStorm);
} // end onStructureConstructionComplete
//=============================================================================
void Player::onStructureUndone(Object *structure)
{
m_scoreKeeper.removeObjectBuilt(structure);
} // end onStructureUndone
//=============================================================================
void Player::addTeamToList(TeamPrototype* team)
{
for( PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it )
{
if (team == *it)
return; // already present
}
m_playerTeamPrototypes.push_back(team);
}
//=============================================================================
void Player::removeTeamFromList(TeamPrototype* team)
{
for (PlayerTeamList::iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
if (team == *it)
{
m_playerTeamPrototypes.erase(it);
return;
}
}
}
//=============================================================================
void Player::healAllObjects()
{
for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
(*it)->healAllObjects();
}
}
//=============================================================================
void Player::iterateObjects( ObjectIterateFunc func, void *userData )
{
for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
(*it)->iterateObjects( func, userData );
}
}
//=============================================================================
void Player::countObjectsByThingTemplate(Int numTmplates, const ThingTemplate* const * things, Bool ignoreDead, Int *counts, Bool ignoreUnderConstruction ) const
{
Int i;
for (i = 0; i < numTmplates; ++i)
counts[i] = 0;
for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end();
++it)
{
(*it)->countObjectsByThingTemplate(numTmplates, things, ignoreDead, counts, ignoreUnderConstruction);
}
}
//=============================================================================
Int Player::countBuildings(void)
{
int retVal = 0;
for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
retVal += (*it)->countBuildings();
}
return retVal;
}
//=============================================================================
Int Player::countObjects(KindOfMaskType setMask, KindOfMaskType clearMask)
{
int retVal = 0;
for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
retVal += (*it)->countObjects(setMask, clearMask);
}
return retVal;
}
//=============================================================================
Object *Player::findClosestByKindOf( Object *queryObject, KindOfMaskType setMask, KindOfMaskType clearMask )
{
if( queryObject == NULL )
return NULL;
ClosestKindOfData data;
data.m_setKindOf = setMask;
data.m_clearKindOf = clearMask;
data.m_source = queryObject;
// Magic presto! data ends up with the answer in it!
iterateObjects( findClosestKindOf, &data );
return data.m_closest;
}
//=============================================================================
Bool Player::hasAnyBuildings(void) const
{
for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
if ((*it)->hasAnyBuildings()) {
return true;
}
}
return false;
}
//=============================================================================
Bool Player::hasAnyBuildings(KindOfMaskType kindOf) const
{
for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
if ((*it)->hasAnyBuildings(kindOf)) {
return true;
}
}
return false;
}
//=============================================================================
Bool Player::hasAnyUnits(void) const
{
for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
if ((*it)->hasAnyUnits()) {
return true;
}
}
return false;
}
//=============================================================================
Bool Player::hasAnyObjects(void) const
{
for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
if ((*it)->hasAnyObjects()) {
return true;
}
}
return false;
}
//=============================================================================
Bool Player::hasAnyBuildFacility(void) const
{
for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
if ((*it)->hasAnyBuildFacility())
return true;
}
return false;
}
//=============================================================================
void Player::updateTeamStates(void)
{
for (PlayerTeamList::const_iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it)
{
(*it)->updateState();
}
}
//=============================================================================
Bool Player::isLocalPlayer() const
{
return this == ThePlayerList->getLocalPlayer();
}
//=============================================================================
void Player::setListInScoreScreen(Bool listInScoreScreen)
{
m_listInScoreScreen = listInScoreScreen;
}
//=============================================================================
Bool Player::getListInScoreScreen()
{
return m_listInScoreScreen;
}
//=============================================================================
UnsignedInt Player::getSupplyBoxValue()
{
/// @todo This would be the hookup for difficulty level modifiers and special economy buildings
return TheGlobalData->m_baseValuePerSupplyBox;
}
//=============================================================================
Real Player::getProductionCostChangePercent( AsciiString buildTemplateName ) const
{
ProductionChangeMap::const_iterator it = m_productionCostChanges.find(NAMEKEY(buildTemplateName));
if (it != m_productionCostChanges.end())
{
return (*it).second;
}
return 0.0f;
}
//=============================================================================
Real Player::getProductionTimeChangePercent( AsciiString buildTemplateName ) const
{
ProductionChangeMap::const_iterator it = m_productionTimeChanges.find(NAMEKEY(buildTemplateName));
if (it != m_productionTimeChanges.end())
{
return (*it).second;
}
return 0.0f;
}
//=============================================================================
VeterancyLevel Player::getProductionVeterancyLevel( AsciiString buildTemplateName ) const
{
NameKeyType templateNameKey = NAMEKEY(buildTemplateName);
ProductionVeterancyMap::const_iterator it = m_productionVeterancyLevels.find(templateNameKey);
if (it != m_productionVeterancyLevels.end())
{
return (*it).second;
}
return LEVEL_FIRST;
}
//=============================================================================
void Player::friend_setSkillset(Int skillSet)
{
if (m_ai) {
m_ai->selectSkillset(skillSet);
}
}
//=============================================================================
void Player::setUnitsShouldHunt(Bool unitsShouldHunt, CommandSourceType source)
{
m_unitsShouldHunt = unitsShouldHunt;
Coord3D pos;
ThePartitionManager->getMostValuableLocation(getPlayerIndex(), ALLOW_ENEMIES, VOT_CashValue, &pos);
for (PlayerTeamList::iterator it = m_playerTeamPrototypes.begin();
it != m_playerTeamPrototypes.end(); ++it) {
for (DLINK_ITERATOR iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
Team *team = iter.cur();
if (!team) {
continue;
}
for (DLINK_ITERATOR