/* ** 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: 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/Module/ProductionUpdate.h" #include "GameLogic/VictoryConditions.h" #include "GameNetwork/GameInfo.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; m_ai = NULL; 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_tunnelSystem = NULL; m_playerTemplate = NULL; 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_baseSide = 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_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 (m_ai) { m_ai->deleteInstance(); } m_ai = NULL; 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; #endif #if defined(_DEBUG) || defined(_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE) m_DEMO_instantBuild = FALSE; #endif if (pt) { m_side = pt->getSide(); m_baseSide = pt->getBaseSide(); 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 ) { if ( TheGameInfo ) { m_money = TheGameInfo->getStartingCash(); } else { m_money = TheGlobalData->m_defaultStartingCash; } } 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_baseSide = ""; 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(); } getAcademyStats()->init( this ); //Always off at the beginning of a game! Only GameLogic::update has //the power to turn it on. Don't want to cause desyncs! m_logicalRetaliationModeEnabled = FALSE; } //============================================================================= 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 (m_ai) m_ai->update(); // 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(); } } if( m_energy.getPowerSabotagedTillFrame() != 0 && TheGameLogic->getFrame() > m_energy.getPowerSabotagedTillFrame() ) { m_energy.setPowerSabotagedTillFrame( 0 ); //Tells us we're no longer sabotaged for above check. onPowerBrownOutChange( !m_energy.hasSufficientPower() ); } //Update the academy stats (this only checks applicable things that require a polling method) getAcademyStats()->update(); //Kris: August 25, 2003 (DAY OF CODE LOCK -- NO NEW FEATURES, REALLY!) if( ThePlayerList->getLocalPlayer() == this ) { UnsignedInt now = TheGameLogic->getFrame(); //Only check and post the message once every second so we don't spam the message stream to account for lag. if( now % LOGICFRAMES_PER_SECOND == 0 ) { if( TheGlobalData->m_clientRetaliationModeEnabled != isLogicalRetaliationModeEnabled() ) { //Post a logical message that will switch the retaliation mode on or off. GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_ENABLE_RETALIATION_MODE ); if( msg ) { msg->appendIntegerArgument( getPlayerIndex() ); msg->appendBooleanArgument( TheGlobalData->m_clientRetaliationModeEnabled ); } } } } } //============================================================================= void Player::newMap() { if (m_ai) m_ai->newMap(); } //============================================================================= void Player::setPlayerType(PlayerType t, Bool skirmish) { m_playerType = t; 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 ); } } } //============================================================================= // 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 (m_ai) { m_ai->setAIDifficulty(difficulty); } 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; } 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( !obj->getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) ) { 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 ) { StealthUpdate *update = object->getStealth(); 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(); } if( TheControlBar ) TheControlBar->markUIDirty(); } else { // nothing to do } } //------------------------------------------------------------------------------------------------- /** Is this player a skirmish ai player? */ //------------------------------------------------------------------------------------------------- Bool Player::isSkirmishAIPlayer( void ) { return m_ai ? m_ai->isSkirmishAI() : false; } //---------------------------------------------------------------------------------------------------------- /** * Find a good spot to fire a superweapon. */ Bool Player::computeSuperweaponTarget(const SpecialPowerTemplate *power, Coord3D *retPos, Int playerNdx, Real weaponRadius) { if (m_ai) { return m_ai->computeSuperweaponTarget(power, retPos, playerNdx, weaponRadius); } return FALSE; } //------------------------------------------------------------------------------------------------- /** Get this player's current enemy. NOTE - Can be NULL. */ //------------------------------------------------------------------------------------------------- Player *Player::getCurrentEnemy( void ) { return m_ai?m_ai->getAiEnemy():NULL; } //------------------------------------------------------------------------------------------------- // PlayerObjectFindInfo is used to find a player's object. For example, we iterate through // to find a player's command center, or a specific building capable of firing the specified // special power. // // if he has none, return null. // if he has multiple, return one arbitrarily. //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- // Iterator data struct //------------------------------------------------------------------------------------------------- struct PlayerObjectFindInfo { Player* player; Object* obj; SpecialPowerType spType; const ThingTemplate *thing; UnsignedInt lowestReadyFrame; UnsignedInt highestPercentage; UnsignedInt numReady; }; //------------------------------------------------------------------------------------------------- // Iterator function // Find the first available command center that is naturally ours (not captured from enemy). //------------------------------------------------------------------------------------------------- static void doFindCommandCenter(Object* obj, void* userData) { if (!obj) return; PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; if (info->obj == NULL && obj->isKindOf(KINDOF_COMMANDCENTER) && obj->getTemplate()->getDefaultOwningSide() == info->player->getSide() && !obj->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) && !obj->testStatus(OBJECT_STATUS_SOLD)) { info->obj = obj; } } //------------------------------------------------------------------------------------------------- // Iterator function // Find first object capable of firing specified special power right now. //------------------------------------------------------------------------------------------------- static void doFindSpecialPowerSourceObject( Object *obj, void *userData ) { PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; if( info->lowestReadyFrame == 0 ) { //We already found the best case scenario, so no need to iterate any more. return; } if( !obj->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) && !obj->testStatus( OBJECT_STATUS_SOLD ) && !obj->isEffectivelyDead() ) { if( info->spType == SPECIAL_INVALID && obj->hasAnySpecialPower() ) { //We just care about find an object that has *any* shortcut capable special power. //Iterate through the special power modules and look for one. SpecialPowerModuleInterface *spmInterface = obj->findAnyShortcutSpecialPowerModuleInterface(); if( spmInterface && !spmInterface->isScriptOnly() ) { info->obj = obj; info->lowestReadyFrame = 0; return; } } else if( obj->hasSpecialPower( info->spType ) ) { SpecialPowerModuleInterface *spmInterface = obj->findSpecialPowerModuleInterface( info->spType ); if( spmInterface && !spmInterface->isScriptOnly() ) { UnsignedInt readyFrame = spmInterface->getReadyFrame(); #if defined(_DEBUG) || defined(_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE) // Everything is ready if timers are debug off'd if( ! TheGlobalData->m_specialPowerUsesDelay ) readyFrame = 0; #endif // A disabled guy should only be considered as a last resort. We need it to be counted // so that a disabled button can appear on the shortcut. if( obj->isDisabled() ) readyFrame = UINT_MAX - 10; if( readyFrame < TheGameLogic->getFrame()) { //This special power is ready now and matches, so simply return the //first one. info->obj = obj; info->lowestReadyFrame = 0; return; } else if( readyFrame < info->lowestReadyFrame ) { //This special power isn't ready, but it is going to be ready sooner than any others //we checked (or it's the first one we checked). info->obj = obj; info->lowestReadyFrame = readyFrame; return; } } } } } //------------------------------------------------------------------------------------------------- // Iterator function // Count number of specified special powers that are ready to fire now. //------------------------------------------------------------------------------------------------- static void doCountSpecialPowersReady( Object *obj, void *userData ) { PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; if( !obj->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) && !obj->testStatus( OBJECT_STATUS_SOLD ) && !obj->isEffectivelyDead() ) { if( obj->hasSpecialPower( info->spType ) ) { SpecialPowerModuleInterface *spmInterface = obj->findSpecialPowerModuleInterface( info->spType ); if( spmInterface && !spmInterface->isScriptOnly() ) { if( spmInterface->getSpecialPowerTemplate()->isSharedNSync() && info->numReady == 1 ) { //Shared powers don't stack after the first one is counted. return; } UnsignedInt readyFrame = spmInterface->getReadyFrame(); #if defined(_DEBUG) || defined(_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE) // Everything is ready if timers are debug off'd if( ! TheGlobalData->m_specialPowerUsesDelay ) readyFrame = 0; #endif // A disabled guy should only be considered as a last resort. We do not want him counted here // so that Disabled guys do not go in to the number on the shortcut button. if( obj->isDisabled() ) readyFrame = UINT_MAX - 10; if( readyFrame < TheGameLogic->getFrame()) { //This special power is ready now and matches, so simply return the //first one. info->numReady++; } } } } } //------------------------------------------------------------------------------------------------- static void doFindMostReadyWeaponForThing( Object *obj, void *userData ) { PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; if( info->highestPercentage >= 100 ) { //We already found the best case scenario, so no need to iterate any more. return; } if( info->thing->isEquivalentTo( obj->getTemplate() ) ) { if( !obj->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) && !obj->testStatus( OBJECT_STATUS_SOLD ) && !obj->isEffectivelyDead() ) { if( obj->hasAnyWeapon() ) { UnsignedInt percentage = obj->getMostPercentReadyToFireAnyWeapon(); if( percentage > info->highestPercentage ) { //This weapon is more ready than any others we've checked. info->obj = obj; info->highestPercentage = percentage; return; } } } } } //------------------------------------------------------------------------------------------------- static void doFindMostReadySpecialPowerForThing( Object *obj, void *userData ) { PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; if( info->highestPercentage >= 100 ) { //We already found the best case scenario, so no need to iterate any more. return; } if( info->thing->isEquivalentTo( obj->getTemplate() ) ) { if( !obj->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) && !obj->testStatus( OBJECT_STATUS_SOLD ) && !obj->isEffectivelyDead() ) { // search the modules for the one with the matching template for( BehaviorModule** m = obj->getBehaviorModules(); *m; ++m ) { SpecialPowerModuleInterface* sp = (*m)->getSpecialPower(); if (!sp) continue; UnsignedInt percentage = sp->getPercentReady(); if( percentage > info->highestPercentage ) { //This weapon is more ready than any others we've checked. info->obj = obj; info->highestPercentage = percentage; } } } } } //------------------------------------------------------------------------------------------------- static void doFindExistingObjectWithThingTemplate( Object *obj, void *userData ) { PlayerObjectFindInfo* info = (PlayerObjectFindInfo*)userData; if( info->obj ) { //We already found a matching obj, so return return; } if( info->thing->isEquivalentTo( obj->getTemplate() ) ) { if( !obj->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) && !obj->testStatus( OBJECT_STATUS_SOLD ) && !obj->isEffectivelyDead() ) { //We found one. info->obj = obj; } } } //------------------------------------------------------------------------------------------------- Object* Player::findNaturalCommandCenter() { PlayerObjectFindInfo info; info.player = this; info.obj = NULL; iterateObjects(doFindCommandCenter, &info); return info.obj; } //------------------------------------------------------------------------------------------------- Object* Player::findMostReadyShortcutSpecialPowerOfType( SpecialPowerType spType ) { PlayerObjectFindInfo info; info.player = this; info.obj = NULL; info.spType = spType; info.lowestReadyFrame = 0xffffffff; iterateObjects( doFindSpecialPowerSourceObject, &info ); return info.obj; } //------------------------------------------------------------------------------------------------- Object* Player::findMostReadyShortcutWeaponForThing( const ThingTemplate *thing, UnsignedInt &mostReadyPercentage ) { PlayerObjectFindInfo info; info.player = this; info.obj = NULL; info.thing = thing; info.highestPercentage = 0; iterateObjects( doFindMostReadyWeaponForThing, &info ); mostReadyPercentage = info.highestPercentage; return info.obj; } //------------------------------------------------------------------------------------------------- Object* Player::findMostReadyShortcutSpecialPowerForThing( const ThingTemplate *thing, UnsignedInt &mostReadyPercentage ) { PlayerObjectFindInfo info; info.player = this; info.obj = NULL; info.thing = thing; info.highestPercentage = 0; iterateObjects( doFindMostReadySpecialPowerForThing, &info ); mostReadyPercentage = info.highestPercentage; return info.obj; } //------------------------------------------------------------------------------------------------- Object* Player::findAnyExistingObjectWithThingTemplate( const ThingTemplate *thing ) { PlayerObjectFindInfo info; info.player = this; info.obj = NULL; info.thing = thing; iterateObjects( doFindExistingObjectWithThingTemplate, &info ); return info.obj; } //------------------------------------------------------------------------------------------------- // Finds a short-cut firing special power of any type arbitrarily. //------------------------------------------------------------------------------------------------- Bool Player::hasAnyShortcutSpecialPower() { PlayerObjectFindInfo info; info.player = this; info.obj = NULL; info.spType = SPECIAL_INVALID; //Invalid dictates that we don't care about the type. info.lowestReadyFrame = 0xffffffff; iterateObjects( doFindSpecialPowerSourceObject, &info ); return info.obj; } //------------------------------------------------------------------------------------------------- Int Player::countReadyShortcutSpecialPowersOfType( SpecialPowerType spType ) { PlayerObjectFindInfo info; info.spType = spType; info.numReady = 0; iterateObjects( doCountSpecialPowersReady, &info ); return info.numReady; } //------------------------------------------------------------------------------------------------- /** Difficulty level for this player */ //------------------------------------------------------------------------------------------------- GameDifficulty Player::getPlayerDifficulty(void) const { if (m_ai) { return m_ai->getAIDifficulty(); } return TheScriptEngine->getGlobalDifficulty(); } //------------------------------------------------------------------------------------------------- /** Do any bridges need repair, and if so repair them. */ //------------------------------------------------------------------------------------------------- Bool Player::checkBridges(Object *unit, Waypoint *way) { return m_ai?m_ai->checkBridges(unit, way):false; } //------------------------------------------------------------------------------------------------- /** Do any bridges need repair, and if so repair them. */ //------------------------------------------------------------------------------------------------- Bool Player::getAiBaseCenter(Coord3D *pos) { return m_ai?m_ai->getBaseCenter(pos):false; } //------------------------------------------------------------------------------------------------- /** Repair bridge or structure. */ //------------------------------------------------------------------------------------------------- void Player::repairStructure(ObjectID structureID) { if (m_ai) { m_ai->repairStructure(structureID); } } //------------------------------------------------------------------------------------------------- /** 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); // ai notification callback if( m_ai ) m_ai->onUnitProduced( factory, unit ); } // 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); // ai notification callback if( m_ai ) m_ai->onStructureProduced( builder, structure ); // 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( structure->hasSpecialPower( SPECIAL_PARTICLE_UPLINK_CANNON ) || structure->hasSpecialPower( SUPW_SPECIAL_PARTICLE_UPLINK_CANNON ) || structure->hasSpecialPower( LAZR_SPECIAL_PARTICLE_UPLINK_CANNON ) ) { if ( localPlayer == structure->getControllingPlayer() ) { TheEva->setShouldPlay(EVA_SuperweaponDetected_Own_ParticleCannon); } else if ( localPlayer->getRelationship(structure->getTeam()) != ENEMIES ) { // Note: treating NEUTRAL as ally. Is this correct? TheEva->setShouldPlay(EVA_SuperweaponDetected_Ally_ParticleCannon); } else { TheEva->setShouldPlay(EVA_SuperweaponDetected_Enemy_ParticleCannon); } } if( structure->hasSpecialPower( SPECIAL_NEUTRON_MISSILE ) || structure->hasSpecialPower( NUKE_SPECIAL_NEUTRON_MISSILE ) || structure->hasSpecialPower( SUPW_SPECIAL_NEUTRON_MISSILE ) ) { if ( localPlayer == structure->getControllingPlayer() ) { TheEva->setShouldPlay(EVA_SuperweaponDetected_Own_Nuke); } else if ( localPlayer->getRelationship(structure->getTeam()) != ENEMIES ) { // Note: treating NEUTRAL as ally. Is this correct? TheEva->setShouldPlay(EVA_SuperweaponDetected_Ally_Nuke); } else { TheEva->setShouldPlay(EVA_SuperweaponDetected_Enemy_Nuke); } } if (structure->hasSpecialPower(SPECIAL_SCUD_STORM)) { if ( localPlayer == structure->getControllingPlayer() ) { TheEva->setShouldPlay(EVA_SuperweaponDetected_Own_ScudStorm); } else if ( localPlayer->getRelationship(structure->getTeam()) != ENEMIES ) { // Note: treating NEUTRAL as ally. Is this correct? TheEva->setShouldPlay(EVA_SuperweaponDetected_Ally_ScudStorm); } else { TheEva->setShouldPlay(EVA_SuperweaponDetected_Enemy_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 ) const { 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 iterObj = team->iterate_TeamMemberList(); !iterObj.done(); iterObj.advance()) { Object *obj = iterObj.cur(); if (!obj) { continue; } KindOfMaskType disqualifyingKindofs; disqualifyingKindofs.set(KINDOF_DOZER); disqualifyingKindofs.set(KINDOF_HARVESTER); disqualifyingKindofs.set(KINDOF_IGNORES_SELECT_ALL); if (obj->isAnyKindOf( disqualifyingKindofs )) { continue; // Harvesters, dozers etc. } obj->leaveGroup(); AIUpdateInterface *ai = obj->getAIUpdateInterface(); if (ai) { if (unitsShouldHunt) { ai->aiHunt(source); } else { ai->aiIdle(source); } } } } } } //============================================================================= void Player::killPlayer(void) { 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->evacuateTeam(); // force containers on team to dump contents } } m_isPlayerDead = TRUE; // this is so OCLs don't ever again spawn useful units for us. for (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->killTeam(); } } if (TheGameLogic->isInSinglePlayerGame()) { if (getPlayerType()==PLAYER_COMPUTER) { // This is an AI player in a solo mission - leave him alive so he can be used later. jba. m_isPlayerDead = FALSE; // this is so we can later spawn useful units for us. return; } } if (isLocalPlayer() && !TheGameLogic->isInShellGame()) { becomingLocalPlayer(TRUE); // recalc disguises, etc if (TheControlBar ) { if (isPlayerActive()) { TheControlBar->setControlBarSchemeByPlayer(this); } else { TheControlBar->setControlBarSchemeByPlayerTemplate(ThePlayerTemplateStore->findPlayerTemplate(NAMEKEY("FactionObserver"))); } } } m_money.withdraw(m_money.countMoney()); // force $$$ to 0 on death } //============================================================================= void Player::setObjectsEnabled(AsciiString templateTypeToAffect, Bool enable) { 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 iterObj = team->iterate_TeamMemberList(); !iterObj.done(); iterObj.advance()) { Object *obj = iterObj.cur(); if (!obj) { continue; } if (obj->getTemplate()->getName().compare(templateTypeToAffect) == 0) { obj->setScriptStatus( OBJECT_STATUS_SCRIPT_DISABLED, !enable ); } } } } } //============================================================================= void Player::transferAssetsFromThat(Player *that) { Team *defaultTeam = getDefaultTeam(); if (!defaultTeam) { return; } std::list objsToTransfer; // let's not transfer beacons const ThingTemplate *beaconTemplate = TheThingFactory->findTemplate( that->getPlayerTemplate()->getBeaconTemplate() ); // transfer all his units. for (PlayerTeamList::iterator it = that->m_playerTeamPrototypes.begin(); it != that->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 iterObj = team->iterate_TeamMemberList(); !iterObj.done(); iterObj.advance()) { Object *obj = iterObj.cur(); if (!obj || obj->getTemplate()->isEquivalentTo(beaconTemplate)) // don't transfer NULL objs or beacons { continue; } objsToTransfer.push_back(obj); } } } for (std::list::iterator itObjs = objsToTransfer.begin(); itObjs != objsToTransfer.end(); ++itObjs) { (*itObjs)->setTeam(defaultTeam); } // transfer all his money UnsignedInt allMoney = that->getMoney()->countMoney(); that->getMoney()->withdraw(allMoney); getMoney()->deposit(allMoney); } //============================================================================= void Player::garrisonAllUnits(CommandSourceType source) { PartitionFilterAcceptByKindOf f1(MAKE_KINDOF_MASK(KINDOF_STRUCTURE), KINDOFMASK_NONE); PartitionFilter *filters[] = { &f1, NULL }; Coord3D pos = {50.0, 50.0, 50.0}; /// @todo srj -- we should really use iterateAllObjects() here instead, but I have no time to // test such a change... make someday ObjectIterator *iterBuilding = ThePartitionManager->iterateObjectsInRange(&pos, 1e9f, FROM_CENTER_3D, filters, ITER_SORTED_NEAR_TO_FAR); MemoryPoolObjectHolder hold(iterBuilding); 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 iterObj = team->iterate_TeamMemberList(); !iterObj.done(); iterObj.advance()) { Object *obj = iterObj.cur(); if (!obj) { continue; } AIUpdateInterface *ai = obj->getAIUpdateInterface(); if (!ai) { continue; } for (Object *theBuilding = iterBuilding->first(); theBuilding; theBuilding = iterBuilding->next()) { ContainModuleInterface *contain = theBuilding->getContain(); if (contain) { PlayerMaskType player = contain->getPlayerWhoEntered(); if (!((player == 0) || (player == obj->getControllingPlayer()->getPlayerMask()))) { continue; } } if( !TheActionManager->canEnterObject( obj, theBuilding, source, CHECK_CAPACITY ) ) { continue; } ai->aiEnter(theBuilding, source); } } } } } //============================================================================= void Player::ungarrisonAllUnits(CommandSourceType source) { 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 iterObj = team->iterate_TeamMemberList(); !iterObj.done(); iterObj.advance()) { Object *obj = iterObj.cur(); if (!obj) { continue; } AIUpdateInterface *ai = obj->getAIUpdateInterface(); if (!ai) { continue; } // Tell everything that has stuff in it to kick them all out. You can't tell the people to get // out of what they are in, because they may not know, or they may be two transports deep, and their // transport may not know where they are. // And check for building so you don't unload transports in the ungarrison command if( obj->isKindOf( KINDOF_STRUCTURE ) ) ai->aiEvacuate( FALSE, source); } } } } //============================================================================= void Player::setUnitsShouldIdleOrResume(Bool idle) { 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 iterObj = team->iterate_TeamMemberList(); !iterObj.done(); iterObj.advance()) { Object *obj = iterObj.cur(); if (!obj) continue; if (obj->isKindOf(KINDOF_STRUCTURE)) continue; AIUpdateInterface *ai = obj->getAIUpdateInterface(); if (!ai) continue; if (idle) { // force it to move to its position to make it stop. ai->aiMoveToPosition(obj->getPosition(), CMD_FROM_SCRIPT); } else { // Here is the special bit for this exit style, force wanting on SupplyTruck types if (ai->isIdle()) { SupplyTruckAIInterface* supplyTruckAI = ai->getSupplyTruckAIInterface(); if( supplyTruckAI ) supplyTruckAI->setForceWantingState(true); } } } } } } //------------------------------------------------------------------------------- void sellBuildings( Object *obj, void *userData ) { if( obj->isFactionStructure() || obj->isKindOf( KINDOF_COMMANDCENTER ) || obj->isKindOf( KINDOF_FS_POWER ) ) { TheBuildAssistant->sellObject( obj ); } } //============================================================================= void Player::sellEverythingUnderTheSun() { iterateObjects( sellBuildings, NULL ); } //============================================================================= Bool Player::allowedToBuild(const ThingTemplate *tmplate) const { if (!m_canBuildBase && tmplate->isKindOf(KINDOF_STRUCTURE)) { return false; } if (!m_canBuildUnits && !tmplate->isKindOf(KINDOF_STRUCTURE)) { return false; } return true; } //============================================================================= void Player::buildSpecificTeam( TeamPrototype *teamProto) { if (m_ai) { // Do a priority build. m_ai->buildSpecificAITeam(teamProto, true); } } //============================================================================= void Player::buildBaseDefense(Bool flank) { if (m_ai) { // Do a priority build. m_ai->buildAIBaseDefense(flank); } } //============================================================================= void Player::buildBaseDefenseStructure(const AsciiString &thingName, Bool flank) { if (m_ai) { // Do a priority build. m_ai->buildAIBaseDefenseStructure(thingName, flank); } } //============================================================================= void Player::buildSpecificBuilding(const AsciiString &thingName) { if (m_ai) { // Do a priority build. m_ai->buildSpecificAIBuilding(thingName); } } //============================================================================= void Player::buildBySupplies(Int minimumCash, const AsciiString &thingName) { if (m_ai) { m_ai->buildBySupplies(minimumCash, thingName); } } //============================================================================= void Player::buildSpecificBuildingNearestTeam( const AsciiString &thingName, const Team *team ) { if( m_ai ) { m_ai->buildSpecificBuildingNearestTeam( thingName, team ); } } //============================================================================= void Player::buildUpgrade( const AsciiString &upgrade) { if (m_ai) { m_ai->buildUpgrade(upgrade); } } //============================================================================= void Player::recruitSpecificTeam( TeamPrototype *teamProto, Real recruitRadius) { if (m_ai) { // Do a priority build. m_ai->recruitSpecificAITeam(teamProto, recruitRadius); } } //============================================================================= // Calculates the closest construction zone location based on a template. Gets plassed to aiPlayer //============================================================================= Bool Player::calcClosestConstructionZoneLocation( const ThingTemplate *constructTemplate, Coord3D *location ) { if( m_ai ) { return m_ai->calcClosestConstructionZoneLocation( constructTemplate, location ); } return FALSE; } //============================================================================= void Player::doBountyForKill(const Object* killer, const Object* victim) { if (!killer || !victim) return; // srj sez: per dustin, no experience (et al) for killing things under construction. if (victim->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) return; Int costToBuild = victim->getTemplate()->calcCostToBuild(victim->getControllingPlayer()); Int bounty = REAL_TO_INT_CEIL(costToBuild * m_cashBountyPercent); if( bounty ) { getMoney()->deposit( bounty ); m_scoreKeeper.addMoneyEarned( bounty ); //Display cash income floating over the recipient. UnicodeString moneyString; moneyString.format( TheGameText->fetch( "GUI:AddCash" ), bounty ); Coord3D pos; pos.zero(); pos.add( killer->getPosition() ); pos.z += 10.0f; //add a little z to make it show up above the unit. TheInGameUI->addFloatingText( moneyString, &pos, GameMakeColor( 255, 255, 0, 255 ) ); } } //============================================================================= Bool Player::hasPrereqsForScience(ScienceType t) const { return TheScienceStore->playerHasPrereqsForScience(this, t); } //============================================================================= /// returns TRUE if the player gained/lost levels as a result. Bool Player::addSkillPoints(Int delta) { delta = REAL_TO_INT_CEIL(m_skillPointsModifier * INT_TO_REAL(delta)); if( delta == 0 ) return false; Int levelCap = min( TheGameLogic->getRankLevelLimit(), TheRankInfoStore->getRankLevelCount() ); Int pointCap = TheRankInfoStore->getRankInfo(levelCap)->m_skillPointsNeeded; // Cap at lowest point of cap level, not highest. Bool levelGained = FALSE; m_skillPoints = min( pointCap, (m_skillPoints + delta) ); while( m_skillPoints >= m_levelUp ) { // LevelUp gets increased as a side effect of setRankLevel, and this won't infinitely loop, // because when there are no more levels to be gained, m_levelUp is set to INT_MAX. setRankLevel( m_rankLevel + 1 ); levelGained = TRUE; } return levelGained; } //============================================================================= /// returns TRUE if the player gained/lost levels as a result. Bool Player::addSkillPointsForKill(const Object* killer, const Object* victim) { if (!killer || !victim) return false; // srj sez: per dustin, no experience (et al) for killing things under construction. if (victim->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) return false; Int victimLevel = victim->getVeterancyLevel(); Int skillValue = victim->getTemplate()->getSkillPointValue(victimLevel); return addSkillPoints(skillValue); } //============================================================================= void Player::resetSciences() { m_sciences.clear(); if (getPlayerTemplate()) m_sciences = getPlayerTemplate()->getIntrinsicSciences(); for (Int i = 1; i <= m_rankLevel; ++i) { const RankInfo* rank = TheRankInfoStore->getRankInfo(i); if (rank) { for (ScienceVec::const_iterator it = rank->m_sciencesGranted.begin(); it != rank->m_sciencesGranted.end(); ++it) { addScience(*it); } } } for (ScienceVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it) TheScriptEngine->notifyOfAcquiredScience(getPlayerIndex(), *it); } //============================================================================= /// returns TRUE if sciences were gained/lost. Bool Player::addScience(ScienceType science) { if (hasScience(science)) return false; //DEBUG_LOG(("Adding Science %s\n",TheScienceStore->getInternalNameForScience(science).str())); m_sciences.push_back(science); // 'wake up' any special powers controlled by, well, stuff 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 iterObj = team->iterate_TeamMemberList(); !iterObj.done(); iterObj.advance()) { Object *obj = iterObj.cur(); if (!obj) continue; for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m) { SpecialPowerModuleInterface* sp = (*m)->getSpecialPower(); if (!sp) continue; if (sp->getRequiredScience() == science) { // Turn on the power, and set it to be instantly ready, because that is cool. sp->onSpecialPowerCreation(); sp->setReadyFrame( TheGameLogic->getFrame() ); } } } } TheControlBar->markUIDirty();// Refresh the UI to show new cameos, etc } // notify the script engine TheScriptEngine->notifyOfAcquiredScience(getPlayerIndex(), science); return true; } //============================================================================= void Player::addSciencePurchasePoints(Int delta) { //DEBUG_LOG(("Adding SciencePurchasePoints %d -> %d\n",m_sciencePurchasePoints,m_sciencePurchasePoints+delta)); Int oldSPP = m_sciencePurchasePoints; m_sciencePurchasePoints += delta; if (m_sciencePurchasePoints < 0) m_sciencePurchasePoints = 0; if (oldSPP != m_sciencePurchasePoints && TheControlBar != NULL) TheControlBar->onPlayerSciencePurchasePointsChanged(this); } //============================================================================= Bool Player::attemptToPurchaseScience(ScienceType science) { if (!isCapableOfPurchasingScience(science)) { DEBUG_CRASH(("isCapableOfPurchasingScience: need other prereqs/points to purchase, request is ignored!\n")); return false; } Int cost = TheScienceStore->getSciencePurchaseCost(science); addSciencePurchasePoints(-cost); addScience(science); getAcademyStats()->recordGeneralsPointsSpent( cost ); if( ThePlayerList->getLocalPlayer() == this ) { TheControlBar->markUIDirty(); } return true; } //============================================================================= Bool Player::grantScience(ScienceType science) { if (!TheScienceStore->isScienceGrantable(science)) { DEBUG_CRASH(("Cannot grant science %s, since it is marked as nonGrantable.\n",TheScienceStore->getInternalNameForScience(science).str())); return false; // it's not grantable, so tough, can't have it, even via this method. } return addScience(science); } //============================================================================= Bool Player::isCapableOfPurchasingScience(ScienceType science) const { if (science == SCIENCE_INVALID) { return false; } if (hasScience(science)) { return false; } if( isScienceDisabled( science ) || isScienceHidden( science ) ) { return false; } if (!hasPrereqsForScience(science)) { return false; } Int cost = TheScienceStore->getSciencePurchaseCost(science); // purchase cost of zero means "not purchasable!" if (cost == 0 || cost > getSciencePurchasePoints()) { return false; } return true; } //============================================================================= void Player::resetRank() { m_rankLevel = 1; m_skillPoints = 0; const RankInfo* nextRank = TheRankInfoStore->getRankInfo(m_rankLevel+1); m_levelUp = nextRank ? nextRank->m_skillPointsNeeded : INT_MAX; m_levelDown = 0; m_sciences.clear(); m_sciencePurchasePoints = getPlayerTemplate() ? getPlayerTemplate()->getIntrinsicSciencePurchasePoints() : 0; const RankInfo* curRank = TheRankInfoStore->getRankInfo(m_rankLevel); m_sciencePurchasePoints += curRank ? curRank->m_sciencePurchasePointsGranted : 0; m_generalName = TheGameText? TheGameText->fetch("SCIENCE:GeneralName"):UnicodeString::TheEmptyString; resetSciences(); } //============================================================================= /// returns TRUE if rank level really changed. Bool Player::setRankLevel(Int newLevel) { if (newLevel < 1) newLevel = 1; else if (newLevel > TheRankInfoStore->getRankLevelCount()) newLevel = TheRankInfoStore->getRankLevelCount(); if (newLevel > TheGameLogic->getRankLevelLimit()) newLevel = TheGameLogic->getRankLevelLimit(); if (newLevel == m_rankLevel) return false; //DEBUG_LOG(("Set Rank Level %d -> %d\n",m_rankLevel,newLevel)); Int oldSPP = m_sciencePurchasePoints; if (newLevel < m_rankLevel) { // when downgrading in rank, you lose everything. oh well... resetRank(); } for (Int i = m_rankLevel + 1; i <= newLevel; ++i) { const RankInfo* rank = TheRankInfoStore->getRankInfo(i); DEBUG_ASSERTCRASH(rank, ("rank should never be null here")); if (rank) { //addSciencePurchasePoints(rank->m_sciencePurchasePointsGranted); // do this directly, so we can defer the UI notification till the end m_sciencePurchasePoints += rank->m_sciencePurchasePointsGranted; if (m_sciencePurchasePoints < 0) m_sciencePurchasePoints = 0; if (m_skillPoints < rank->m_skillPointsNeeded) m_skillPoints = rank->m_skillPointsNeeded; for (ScienceVec::const_iterator it = rank->m_sciencesGranted.begin(); it != rank->m_sciencesGranted.end(); ++it) { addScience(*it); } m_levelDown = rank->m_skillPointsNeeded; } } const RankInfo* nextRank = TheRankInfoStore->getRankInfo(newLevel + 1); m_levelUp = nextRank ? nextRank->m_skillPointsNeeded : INT_MAX; m_rankLevel = newLevel; DEBUG_ASSERTCRASH(m_skillPoints >= m_levelDown && m_skillPoints < m_levelUp, ("hmm, wrong")); //DEBUG_LOG(("Rank %d, Skill %d, down %d, up %d\n",m_rankLevel,m_skillPoints, m_levelDown, m_levelUp)); if (TheControlBar != NULL) { if( m_levelUp ) { //Play an EVA sound if (isLocalPlayer()) TheEva->setShouldPlay( EVA_GeneralLevelUp ); } // notify of rank-change first TheControlBar->onPlayerRankChanged(this); // then of SPP change, if any if (oldSPP != m_sciencePurchasePoints) TheControlBar->onPlayerSciencePurchasePointsChanged(this); } return true; } //============================================================================= Bool Player::hasScience(ScienceType t) const { return std::find(m_sciences.begin(), m_sciences.end(), t) != m_sciences.end(); } //============================================================================= Bool Player::isScienceDisabled( ScienceType t ) const { return std::find( m_sciencesDisabled.begin(), m_sciencesDisabled.end(), t ) != m_sciencesDisabled.end(); } //============================================================================= Bool Player::isScienceHidden( ScienceType t ) const { return std::find( m_sciencesHidden.begin(), m_sciencesHidden.end(), t ) != m_sciencesHidden.end(); } //============================================================================= void Player::setScienceAvailability( ScienceType science, ScienceAvailabilityType type ) { ScienceType sType; ScienceVec::iterator it; Bool found = false; //First remove it from disabled sciences if it's there. for( it = m_sciencesDisabled.begin(); it != m_sciencesDisabled.end(); ++it ) { sType = *it; if( sType == science ) { m_sciencesDisabled.erase( it ); found = true; break; } } if( !found ) { //If still not found, remove it from hidden sciences if it's there. for( it = m_sciencesHidden.begin(); it != m_sciencesHidden.end(); ++it ) { sType = *it; if( sType == science ) { m_sciencesHidden.erase( it ); found = true; break; } } } //At this point, this science shouldn't be disabled nor hidden! if( type == SCIENCE_DISABLED ) { //Add science to disabled vec. m_sciencesDisabled.push_back( science ); } else if( type == SCIENCE_HIDDEN ) { //Add science to hidden vec. m_sciencesHidden.push_back( science ); } else if( type == SCIENCE_AVAILABLE ) { //Do nothing, because being available means that the science isn't in one of the hidden or disabled vecs. } } //============================================================================= ScienceAvailabilityType Player::getScienceAvailabilityTypeFromString( const AsciiString& name ) { for( Int i = 0; i < SCIENCE_AVAILABILITY_COUNT; i++ ) { if( !name.compareNoCase( ScienceAvailabilityNames[ i ] ) ) { return (ScienceAvailabilityType)i; } } return SCIENCE_AVAILABILITY_INVALID; } namespace { // ------------------------------------------------------------------------------------------------ // For countExisting struct TypeCountData { UnsignedInt count; const ThingTemplate *type; NameKeyType linkKey; Bool checkProductionInterface; }; } // ------------------------------------------------------------------------------------------------ /** Count all the units of a given type that exist or are in any production queues for a player */ // ------------------------------------------------------------------------------------------------ static void countExisting( Object *obj, void *userData ) { // Don't care about dead objects if ( obj->isEffectivelyDead() ) return; TypeCountData *typeCountData = (TypeCountData *)userData; // Compare templates if ( typeCountData->type->isEquivalentTo( obj->getTemplate() ) || ( typeCountData->linkKey != NAMEKEY_INVALID && obj->getTemplate() != NULL && typeCountData->linkKey == obj->getTemplate()->getMaxSimultaneousLinkKey() ) ) { typeCountData->count++; } // Also consider objects that have a production update interface if ( typeCountData->checkProductionInterface ) { ProductionUpdateInterface *pui = ProductionUpdate::getProductionUpdateInterfaceFromObject( obj ); if( pui ) { // add the count of this type that are in the queue typeCountData->count += pui->countUnitTypeInQueue( typeCountData->type ); } // end if } } // end countInProduction //============================================================================= // Make sure that building another of this unit/structure/object won't exceed MaxSimultaneousOfType() Bool Player::canBuildMoreOfType( const ThingTemplate *whatToBuild ) const { // make sure we're not maxed out for this type of unit. UnsignedInt maxSimultaneousOfType = whatToBuild->getMaxSimultaneousOfType(); if (maxSimultaneousOfType != 0) { TypeCountData typeCountData; typeCountData.count = 0; typeCountData.type = whatToBuild; typeCountData.linkKey = whatToBuild->getMaxSimultaneousLinkKey(); // Assumption: Things with a KINDOF_STRUCTURE flag can never be built from // a factory (ProductionUpdateInterface), because the building can't move // out of the factory. When we do our Starcraft port and have flying Terran // buildings, we'll have to change this ;-) // Remember: To ASSUME makes an ASS out of U and ME. typeCountData.checkProductionInterface = !whatToBuild->isKindOf( KINDOF_STRUCTURE ); iterateObjects( countExisting, &typeCountData ); if( typeCountData.count >= maxSimultaneousOfType ) return false; } return true; } //============================================================================= Bool Player::canBuild(const ThingTemplate *tmplate) const { if (!tmplate) return false; if (!allowedToBuild(tmplate)) return false; if (tmplate->getBuildable() == BSTATUS_NO) return false; if (tmplate->getBuildable() == BSTATUS_IGNORE_PREREQUISITES) return true; if (tmplate->getBuildable() == BSTATUS_ONLY_BY_AI && getPlayerType() != PLAYER_COMPUTER) return false; // else BSTATUS tmplate->getBuildable() == BSTATUS_YES { // we must satisfy all of the prereqs Bool prereqsOK = true; for (Int i = 0; i < tmplate->getPrereqCount(); i++) { const ProductionPrerequisite *pre = tmplate->getNthPrereq(i); if (pre->isSatisfied(this) == false ) prereqsOK = false; } #if defined(_DEBUG) || defined(_INTERNAL) if (ignoresPrereqs()) prereqsOK = true; #endif if (!prereqsOK) return false; } if ( !canBuildMoreOfType( tmplate ) ) return false; return true; } //================================================================================================= Bool Player::canAffordBuild( const ThingTemplate *whatToBuild ) const { // make sure we have enough money to build this const Money *money = getMoney(); if( whatToBuild->calcCostToBuild( this ) <= money->countMoney() ) { return true; } return false; } //================================================================================================= void Player::deleteUpgradeList( void ) { Upgrade *next; // delete all of the upgrades we have while( m_upgradeList ) { next = m_upgradeList->friend_getNext(); m_upgradeList->deleteInstance(); m_upgradeList = next; } // end while // This doesn't call removeUpgrade, so clear these ourselves. m_upgradesInProgress.clear(); m_upgradesCompleted.clear(); } // end deleteUpgradeList //================================================================================================= /** Find an upgrade in our list of upgrades with matching name key */ //================================================================================================= Upgrade *Player::findUpgrade( const UpgradeTemplate *upgradeTemplate ) { Upgrade *upgrade; for( upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() ) if( upgrade->getTemplate() == upgradeTemplate ) return upgrade; return NULL; } // end findUpgrade //================================================================================================= /** Does the player have this completed upgrade */ //================================================================================================= Bool Player::hasUpgradeComplete( const UpgradeTemplate *upgradeTemplate ) { UpgradeMaskType testMask = upgradeTemplate->getUpgradeMask(); return hasUpgradeComplete( testMask ); } //================================================================================================= /** Does the player have this completed upgrade. This form is exposed so Objects can do quick lookups. */ //================================================================================================= Bool Player::hasUpgradeComplete( UpgradeMaskType testMask ) { return m_upgradesCompleted.testForAll( testMask ); } //================================================================================================= /** Does the player have this upgrade In Production*/ //================================================================================================= Bool Player::hasUpgradeInProduction( const UpgradeTemplate *upgradeTemplate ) { UpgradeMaskType testMask = upgradeTemplate->getUpgradeMask(); return m_upgradesInProgress.testForAll( testMask ); } //================================================================================================= /** Give the player an upgrade or change status on an existing upgrade entry */ //================================================================================================= Upgrade *Player::addUpgrade( const UpgradeTemplate *upgradeTemplate, UpgradeStatusType status ) { Upgrade *u = findUpgrade( upgradeTemplate ); // if no upgrade instance found, make a new one if( u == NULL ) { // make new one u = newInstance(Upgrade)( upgradeTemplate ); // tie to list u->friend_setPrev( NULL ); u->friend_setNext( m_upgradeList ); if( m_upgradeList ) m_upgradeList->friend_setPrev( u ); m_upgradeList = u; } // end if // set the new status for the upgrade u->setStatus( status ); // Update our Bitmasks UpgradeMaskType newMask = upgradeTemplate->getUpgradeMask(); if( status == UPGRADE_STATUS_IN_PRODUCTION ) { m_upgradesInProgress.set( newMask ); } else if( status == UPGRADE_STATUS_COMPLETE ) { m_upgradesInProgress.clear( newMask ); m_upgradesCompleted.set( newMask ); onUpgradeCompleted( upgradeTemplate ); } if( ThePlayerList->getLocalPlayer() == this ) { TheControlBar->markUIDirty(); } return u; } // end addUpgrade //================================================================================================= /** An upgrade just finished, do things like tell all objects to recheck UpgradeModules */ void Player::onUpgradeCompleted( const UpgradeTemplate *upgradeTemplate ) { 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 == NULL ) { continue; } for (DLINK_ITERATOR iterObj = team->iterate_TeamMemberList(); !iterObj.done(); iterObj.advance()) { Object *obj = iterObj.cur(); if( obj == NULL ) { continue; } // Dear copy-paste monkeys, the meat of this iterate-all-player-objects loop goes twixt the MEAT comments obj->updateUpgradeModules(); // end MEAT } } } } //================================================================================================= /** Remove upgrade from a player */ //================================================================================================= void Player::removeUpgrade( const UpgradeTemplate *upgradeTemplate ) { Upgrade *upgrade = findUpgrade( upgradeTemplate ); if( upgrade ) { if( upgrade->friend_getNext() ) upgrade->friend_getNext()->friend_setPrev( upgrade->friend_getPrev() ); if( upgrade->friend_getPrev() ) upgrade->friend_getPrev()->friend_setNext( upgrade->friend_getNext() ); else m_upgradeList = upgrade->friend_getNext(); // Clear this upgrade's bits from our mind UpgradeMaskType oldMask = upgradeTemplate->getUpgradeMask(); m_upgradesInProgress.clear( oldMask ); m_upgradesCompleted.clear( oldMask ); if( upgrade->getStatus() == UPGRADE_STATUS_COMPLETE ) onUpgradeRemoved(); if( ThePlayerList->getLocalPlayer() == this ) { TheControlBar->markUIDirty(); } } // end if } // end removeUpgrade //------------------------------------------------------------------------------------------------- Bool Player::okToPlayRadarEdgeSound( void ) { return ( ! TheVictoryConditions->hasSinglePlayerBeenDefeated( this ) && ! m_isPlayerDead && ! TheInGameUI->isClientQuiet() && TheGameLogic->isInGameLogicUpdate() && TheGameLogic->getFrame() > 0 ); } //------------------------------------------------------------------------------------------------- /** The parameter object has just aquired a radar */ //------------------------------------------------------------------------------------------------- void Player::addRadar( Bool disableProof ) { Bool hadRadar = hasRadar(); // increment count ++m_radarCount; if( disableProof ) ++m_disableProofRadarCount;// Disable proof is also in the normal refcount if( !hadRadar && hasRadar() && okToPlayRadarEdgeSound() ) { // This player just got radar, so play the "You have Radar!" sound AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_radarOnlineSound; soundToPlay.setPlayerIndex(getPlayerIndex()); TheAudio->addAudioEvent(&soundToPlay); } } // end addRadar //------------------------------------------------------------------------------------------------- /** The parameter object has is taking its radar away from the player */ //------------------------------------------------------------------------------------------------- void Player::removeRadar( Bool disableProof ) { Bool hadRadar = hasRadar(); // decrement count DEBUG_ASSERTCRASH( m_radarCount > 0, ("removeRadar: An Object is taking its radar away, but the player radar count says they don't have radar!\n") ); --m_radarCount; if( disableProof ) --m_disableProofRadarCount;// Disable proof is also in the normal refcount if( hadRadar && !hasRadar() && okToPlayRadarEdgeSound() ) { // This player just lost radar, so play the "You lost Radar!" sound AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_radarOfflineSound; soundToPlay.setPlayerIndex(getPlayerIndex()); TheAudio->addAudioEvent(&soundToPlay); } } // end removeRadar //------------------------------------------------------------------------------------------------- void Player::disableRadar() { Bool hadRadar = hasRadar(); m_radarDisabled = TRUE; if( hadRadar && !hasRadar() && okToPlayRadarEdgeSound() ) { // This player just lost radar, so play the "You lost Radar!" sound AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_radarOfflineSound; soundToPlay.setPlayerIndex(getPlayerIndex()); TheAudio->addAudioEvent(&soundToPlay); } } //------------------------------------------------------------------------------------------------- void Player::enableRadar() { Bool hadRadar = hasRadar(); m_radarDisabled = FALSE; if( !hadRadar && hasRadar() && okToPlayRadarEdgeSound() ) { // This player just got radar, so play the "You have Radar!" sound AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_radarOnlineSound; soundToPlay.setPlayerIndex(getPlayerIndex()); TheAudio->addAudioEvent(&soundToPlay); } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- Bool Player::hasRadar() const { if( m_radarDisabled && (m_disableProofRadarCount == 0) ) return FALSE;// Nope, no matter how many you have, if I say no, you don't have it // Otherwise, check if I actually do have it. return m_radarCount > 0; } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------ static void doPowerDisable( Object *obj, void *userData ) { Bool disabling = *((Bool*)userData); if( obj && obj->isKindOf(KINDOF_POWERED) ) { if( disabling ) obj->setDisabled( DISABLED_UNDERPOWERED ); //set disabled has a pauseAllSpecialPowers that prevents double pausing else obj->clearDisabled( DISABLED_UNDERPOWERED ); } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void Player::onPowerBrownOutChange( Bool brownOut ) { // Everything that changes due to Player's power supply goes in here. if( brownOut ) disableRadar(); else enableRadar(); //This doesn't give radar necessarily, it just removes the restriction iterateObjects( doPowerDisable, &brownOut );// This function is so cool. } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- // SEVERAL OBJECTS (LIKE MULTIPLE COMMAND CENTERS) MAY HAVE MATCHING SETS OF SPECIAL POWERS // THIS KEEPS THEM IN SYNC SO IT LOOKS LIKE EACH SPECIAL POWER IS SHARED BETWEEN ALL void Player::addNewSharedSpecialPowerTimer( const SpecialPowerTemplate *temp, UnsignedInt frame ) { SpecialPowerReadyTimerType newTimer; newTimer.m_templateID = temp->getID(); newTimer.m_readyFrame = frame; m_specialPowerReadyTimerList.push_back( newTimer ); } void Player::resetOrStartSpecialPowerReadyFrame( const SpecialPowerTemplate *temp ) { UnsignedInt lookupID = temp->getID(); UnsignedInt now = TheGameLogic->getFrame(); SpecialPowerReadyTimerType *timer; SpecialPowerReadyTimerListIterator it; for( it = m_specialPowerReadyTimerList.begin(); it != m_specialPowerReadyTimerList.end(); ++it ) { timer = &(*it); if ( timer->m_templateID == lookupID ) { timer->m_readyFrame = now + temp->getReloadTime(); return; } } addNewSharedSpecialPowerTimer( temp, now ); } void Player::expressSpecialPowerReadyFrame( const SpecialPowerTemplate *temp, UnsignedInt frame ) { SpecialPowerReadyTimerType *timer; SpecialPowerReadyTimerListIterator it; for( it = m_specialPowerReadyTimerList.begin(); it != m_specialPowerReadyTimerList.end(); ++it ) { timer = &(*it); if ( timer->m_templateID == temp->getID() ) { timer->m_readyFrame = frame; return; } } addNewSharedSpecialPowerTimer( temp, frame ); } //------------------------------------------------------------------------------------------------- UnsignedInt Player::getOrStartSpecialPowerReadyFrame( const SpecialPowerTemplate *temp) { UnsignedInt lookupID = temp->getID(); UnsignedInt now = TheGameLogic->getFrame(); SpecialPowerReadyTimerType *timer; SpecialPowerReadyTimerListIterator it; UnsignedInt count = 0; UnsignedInt timerID = 0xfacade; for( it = m_specialPowerReadyTimerList.begin(); it != m_specialPowerReadyTimerList.end(); ++it ) { timer = &(*it); ++count; timerID = timer->m_templateID; if ( timerID == lookupID ) { return timer->m_readyFrame; } } addNewSharedSpecialPowerTimer( temp, now ); return now; } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void Player::friend_applyDifficultyBonusesForObject(Object* obj, Bool apply) const { if (TheGameLogic->isInSinglePlayerGame()) { Real healthFactor = TheGlobalData->m_soloPlayerHealthBonusForDifficulty[getPlayerType()][getPlayerDifficulty()]; if (healthFactor != 1.0f) { BodyModuleInterface* body = obj->getBodyModule(); if (apply) body->setMaxHealth(body->getMaxHealth() * healthFactor, PRESERVE_RATIO); else body->setMaxHealth(body->getMaxHealth() / healthFactor, PRESERVE_RATIO); } static const WeaponBonusConditionType wbonus[PLAYERTYPE_COUNT][DIFFICULTY_COUNT] = { { WEAPONBONUSCONDITION_SOLO_HUMAN_EASY, WEAPONBONUSCONDITION_SOLO_HUMAN_NORMAL, WEAPONBONUSCONDITION_SOLO_HUMAN_HARD }, { WEAPONBONUSCONDITION_SOLO_AI_EASY, WEAPONBONUSCONDITION_SOLO_AI_NORMAL, WEAPONBONUSCONDITION_SOLO_AI_HARD } }; if (apply) obj->setWeaponBonusCondition(wbonus[getPlayerType()][getPlayerDifficulty()]); else obj->clearWeaponBonusCondition(wbonus[getPlayerType()][getPlayerDifficulty()]); } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- Bool Player::doesObjectQualifyForBattlePlan( Object *obj ) const { if( m_battlePlanBonuses && obj ) { if( obj->isAnyKindOf( m_battlePlanBonuses->m_validKindOf ) ) { if( !obj->isAnyKindOf( m_battlePlanBonuses->m_invalidKindOf ) ) { return true; } } } return false; } //------------------------------------------------------------------------------------------------- // note, bonus is an in-out parm. void Player::changeBattlePlan( BattlePlanStatus plan, Int delta, BattlePlanBonuses *bonus ) { DUMPBATTLEPLANBONUSES(bonus, this, NULL); Bool addBonus = false; Bool removeBonus = false; switch( plan ) { case PLANSTATUS_BOMBARDMENT: { m_bombardBattlePlans += delta; if( m_bombardBattlePlans == 1 && delta == 1 ) { addBonus = true; } else if( m_bombardBattlePlans == 0 && delta == -1 ) { removeBonus = true; } break; } case PLANSTATUS_HOLDTHELINE: { m_holdTheLineBattlePlans += delta; if( m_holdTheLineBattlePlans == 1 && delta == 1 ) { addBonus = true; } else if( m_holdTheLineBattlePlans == 0 && delta == -1 ) { removeBonus = true; } break; } case PLANSTATUS_SEARCHANDDESTROY: { m_searchAndDestroyBattlePlans += delta; if( m_searchAndDestroyBattlePlans == 1 && delta == 1 ) { addBonus = true; } else if( m_searchAndDestroyBattlePlans == 0 && delta == -1 ) { removeBonus = true; } break; } } if( addBonus ) { applyBattlePlanBonusesForPlayerObjects( bonus ); } else if( removeBonus ) { //First, inverse the bonuses bonus->m_armorScalar = 1.0f / __max( bonus->m_armorScalar, 0.01f ); bonus->m_sightRangeScalar = 1.0f / __max( bonus->m_sightRangeScalar, 0.01f ); if( bonus->m_bombardment > 0 ) { bonus->m_bombardment = -1; } if( bonus->m_holdTheLine > 0 ) { bonus->m_holdTheLine = -1; } if( bonus->m_searchAndDestroy > 0 ) { bonus->m_searchAndDestroy = -1; } applyBattlePlanBonusesForPlayerObjects( bonus ); } } //------------------------------------------------------------------------------------------------- Int Player::getBattlePlansActiveSpecific( BattlePlanStatus plan ) const { switch( plan ) { case PLANSTATUS_BOMBARDMENT: { return m_bombardBattlePlans; } case PLANSTATUS_HOLDTHELINE: { return m_holdTheLineBattlePlans; } case PLANSTATUS_SEARCHANDDESTROY: { return m_searchAndDestroyBattlePlans; } } return 0; } //------------------------------------------------------------------------------------------------ static void localApplyBattlePlanBonusesToObject( Object *obj, void *userData ) { const BattlePlanBonuses* bonus = (const BattlePlanBonuses*)userData; Object *objectToValidate = obj; Object *objectToModify = obj; DEBUG_LOG(("localApplyBattlePlanBonusesToObject() - looking at object %d (%s)\n", (objectToValidate)?objectToValidate->getID():INVALID_ID, (objectToValidate)?objectToValidate->getTemplate()->getName().str():"")); //First check if the obj is a projectile -- if so split the //object so that the producer is validated, not the projectile. Bool isProjectile = obj->isKindOf( KINDOF_PROJECTILE ); if( isProjectile ) { objectToValidate = TheGameLogic->findObjectByID( obj->getProducerID() ); DEBUG_LOG(("Object is a projectile - looking at object %d (%s) instead\n", (objectToValidate)?objectToValidate->getID():INVALID_ID, (objectToValidate)?objectToValidate->getTemplate()->getName().str():"")); } if( objectToValidate && objectToValidate->isAnyKindOf( bonus->m_validKindOf ) ) { DEBUG_LOG(("Is valid kindof\n")); if( !objectToValidate->isAnyKindOf( bonus->m_invalidKindOf ) ) { DEBUG_LOG(("Is not invalid kindof\n")); //Quite the trek eh? Now we can apply the bonuses! if( !isProjectile ) { DEBUG_LOG(("Is not projectile. Armor scalar is %g\n", bonus->m_armorScalar)); //Really important to not apply certain bonuses like health augmentation to projectiles! if( bonus->m_armorScalar != 1.0f ) { BodyModuleInterface *body = objectToModify->getBodyModule(); body->applyDamageScalar( bonus->m_armorScalar ); CRCDEBUG_LOG(("Applying armor scalar of %g (%8.8X) to object %d (%ls) owned by player %d\n", bonus->m_armorScalar, AS_INT(bonus->m_armorScalar), objectToModify->getID(), objectToModify->getTemplate()->getDisplayName().str(), objectToModify->getControllingPlayer()->getPlayerIndex())); DEBUG_LOG(("After apply, armor scalar is %g\n", body->getDamageScalar())); } if( bonus->m_sightRangeScalar != 1.0f ) { objectToModify->setVisionRange( obj->getVisionRange() * bonus->m_sightRangeScalar ); objectToModify->setShroudClearingRange( obj->getShroudClearingRange() * bonus->m_sightRangeScalar ); } } if( bonus->m_bombardment > 0 ) { objectToModify->setWeaponBonusCondition( WEAPONBONUSCONDITION_BATTLEPLAN_BOMBARDMENT ); } else { objectToModify->clearWeaponBonusCondition( WEAPONBONUSCONDITION_BATTLEPLAN_BOMBARDMENT ); } if( bonus->m_holdTheLine > 0 ) { objectToModify->setWeaponBonusCondition( WEAPONBONUSCONDITION_BATTLEPLAN_HOLDTHELINE ); } else { objectToModify->clearWeaponBonusCondition( WEAPONBONUSCONDITION_BATTLEPLAN_HOLDTHELINE ); } if( bonus->m_searchAndDestroy > 0 ) { objectToModify->setWeaponBonusCondition( WEAPONBONUSCONDITION_BATTLEPLAN_SEARCHANDDESTROY ); } else { objectToModify->clearWeaponBonusCondition( WEAPONBONUSCONDITION_BATTLEPLAN_SEARCHANDDESTROY ); } } } } //------------------------------------------------------------------------------------------------- //New object or converted object gaining our current battle plan bonuses. //------------------------------------------------------------------------------------------------- void Player::applyBattlePlanBonusesForObject( Object *obj ) const { localApplyBattlePlanBonusesToObject( obj, m_battlePlanBonuses ); } //------------------------------------------------------------------------------------------------- //Object has just left our team, so remove it's bonuses! //------------------------------------------------------------------------------------------------- void Player::removeBattlePlanBonusesForObject( Object *obj ) const { //Copy bonuses, and invert them. BattlePlanBonuses* bonus = newInstance(BattlePlanBonuses); *bonus = *m_battlePlanBonuses; bonus->m_armorScalar = 1.0f / __max( bonus->m_armorScalar, 0.01f ); bonus->m_sightRangeScalar = 1.0f / __max( bonus->m_sightRangeScalar, 0.01f ); bonus->m_bombardment = -ALL_PLANS; //Safe to remove as it clears the weapon bonus flag bonus->m_searchAndDestroy = -ALL_PLANS; //Safe to remove as it clears the weapon bonus flag bonus->m_holdTheLine = -ALL_PLANS; //Safe to remove as it clears the weapon bonus flag DUMPBATTLEPLANBONUSES(bonus, this, obj); localApplyBattlePlanBonusesToObject( obj, bonus ); bonus->deleteInstance(); } //------------------------------------------------------------------------------------------------- //Battle plan bonuses changing, so apply to all of our objects! //------------------------------------------------------------------------------------------------- void Player::applyBattlePlanBonusesForPlayerObjects( const BattlePlanBonuses *bonus ) { DUMPBATTLEPLANBONUSES(bonus, this, NULL); //Only allocate the battle plan bonuses if we actually use it! if( !m_battlePlanBonuses ) { DEBUG_LOG(("Allocating new m_battlePlanBonuses\n")); m_battlePlanBonuses = newInstance( BattlePlanBonuses ); *m_battlePlanBonuses = *bonus; } else { DEBUG_LOG(("Adding bonus into existing m_battlePlanBonuses\n")); DUMPBATTLEPLANBONUSES(m_battlePlanBonuses, this, NULL); //Just apply the differences by multiplying the scalars together (kindofs won't change) //These bonuses are used for new objects that are created or objects that are transferred //to our team. m_battlePlanBonuses->m_armorScalar *= bonus->m_armorScalar; m_battlePlanBonuses->m_sightRangeScalar *= bonus->m_sightRangeScalar; m_battlePlanBonuses->m_bombardment += bonus->m_bombardment; m_battlePlanBonuses->m_bombardment = MAX( 0, m_battlePlanBonuses->m_bombardment ); m_battlePlanBonuses->m_holdTheLine += bonus->m_holdTheLine; m_battlePlanBonuses->m_holdTheLine = MAX( 0, m_battlePlanBonuses->m_holdTheLine ); m_battlePlanBonuses->m_searchAndDestroy += bonus->m_searchAndDestroy; m_battlePlanBonuses->m_searchAndDestroy = MAX( 0, m_battlePlanBonuses->m_searchAndDestroy ); } DUMPBATTLEPLANBONUSES(m_battlePlanBonuses, this, NULL); iterateObjects( localApplyBattlePlanBonusesToObject, (void*)bonus ); } //------------------------------------------------------------------------------------------------- /** Create a hotkey team based on this GameMessage */ //------------------------------------------------------------------------------------------------- void Player::processCreateTeamGameMessage(Int hotkeyNum, GameMessage *msg) { // GameMessage arguments are the object ID's of the objects that are to be in this team. if ((hotkeyNum < 0) || (hotkeyNum >= NUM_HOTKEY_SQUADS)) { DEBUG_CRASH(("processCreateTeamGameMessage got an invalid hotkey number")); return; } m_squads[hotkeyNum]->clearSquad(); UnsignedByte numArgs = msg->getArgumentCount(); for (UnsignedByte i = 0; i < numArgs; ++i) { ObjectID objID = msg->getArgument(i)->objectID; Object *obj = TheGameLogic->findObjectByID(objID); if (obj != NULL) { // first, remove it from any other hotkey squads it is in. removeObjectFromHotkeySquad(obj); m_squads[hotkeyNum]->addObject(obj); } } } //------------------------------------------------------------------------------------------------- /** Select a hotkey team based on this GameMessage */ //------------------------------------------------------------------------------------------------- void Player::processSelectTeamGameMessage(Int hotkeyNum, GameMessage *msg) { if ((hotkeyNum < 0) || (hotkeyNum >= NUM_HOTKEY_SQUADS)) { DEBUG_CRASH(("processSelectTeamGameMessage got an invalid hotkey number")); return; } if (m_squads[hotkeyNum] == NULL) { return; } m_currentSelection->clearSquad(); VecObjectPtr objectList = m_squads[hotkeyNum]->getLiveObjects(); Int numObjs = objectList.size(); for (Int i = 0; i < numObjs; ++i) { m_currentSelection->addObject(objectList[i]); } if( numObjs > 0 ) { getAcademyStats()->recordControlGroupsUsed(); } } //------------------------------------------------------------------------------------------------- /** Select a hotkey team based on this GameMessage */ //------------------------------------------------------------------------------------------------- void Player::processAddTeamGameMessage(Int hotkeyNum, GameMessage *msg) { if ((hotkeyNum < 0) || (hotkeyNum >= NUM_HOTKEY_SQUADS)) { DEBUG_CRASH(("processAddTeamGameMessage got an invalid hotkey number")); return; } if (m_squads[hotkeyNum] == NULL) { return; } if (m_currentSelection == NULL) { m_currentSelection = newInstance( Squad ); } VecObjectPtr objectList = m_squads[hotkeyNum]->getLiveObjects(); Int numObjs = objectList.size(); for (Int i = 0; i < numObjs; ++i) { m_currentSelection->addObject(objectList[i]); } } //------------------------------------------------------------------------------------------------- /** Select a hotkey team based on this GameMessage */ //------------------------------------------------------------------------------------------------- void Player::getCurrentSelectionAsAIGroup(AIGroup *group) { if (m_currentSelection != NULL) { m_currentSelection->aiGroupFromSquad(group); } } //------------------------------------------------------------------------------------------------- /** Select a hotkey team based on this GameMessage */ //------------------------------------------------------------------------------------------------- void Player::setCurrentlySelectedAIGroup(AIGroup *group) { if (m_currentSelection == NULL) { m_currentSelection = newInstance( Squad ); } m_currentSelection->clearSquad(); if (group != NULL) { m_currentSelection->squadFromAIGroup(group, true); } } //------------------------------------------------------------------------------------------------- /** Select a hotkey team based on this GameMessage */ //------------------------------------------------------------------------------------------------- Squad *Player::getHotkeySquad(Int squadNumber) { if ((squadNumber >= 0) && (squadNumber < NUM_HOTKEY_SQUADS)) { return m_squads[squadNumber]; } return NULL; } //------------------------------------------------------------------------------------------------- /** return the hotkey squad that a unit is in, or NO_HOTKEY_SQUAD if it isn't in one */ //------------------------------------------------------------------------------------------------- Int Player::getSquadNumberForObject(const Object *objToFind) const { for (Int i = 0; i < NUM_HOTKEY_SQUADS; ++i) { if (m_squads[i]->isOnSquad(objToFind)) { return i; } } return NO_HOTKEY_SQUAD; } //------------------------------------------------------------------------------------------------- /** Remove an object from any hotkey squads its on. (Should never be more than one, but do them */ /** all for good measure. */ //------------------------------------------------------------------------------------------------- void Player::removeObjectFromHotkeySquad(Object *objToRemove) { for (Int i = 0; i < NUM_HOTKEY_SQUADS; ++i) { if (!m_squads[i]) { continue; } m_squads[i]->removeObject(objToRemove); } } //------------------------------------------------------------------------------------------------- /** Select a hotkey team based on this GameMessage */ //------------------------------------------------------------------------------------------------- void Player::addAIGroupToCurrentSelection(AIGroup *group) { if (group == NULL) { return; } if (m_currentSelection == NULL) { m_currentSelection = newInstance( Squad ); } VecObjectID objectIDVec = group->getAllIDs(); Int numObjs = objectIDVec.size(); for (Int i = 0; i < numObjs; ++i) { m_currentSelection->addObjectID(objectIDVec[i]); } } //------------------------------------------------------------------------------------------------- /** addTypeOfProductionCostChange adds a production change to the typeof list */ //------------------------------------------------------------------------------------------------- void Player::addKindOfProductionCostChange( KindOfMaskType kindOf, Real percent ) { KindOfPercentProductionChangeListIt it = m_kindOfPercentProductionChangeList.begin(); while(it != m_kindOfPercentProductionChangeList.end()) { KindOfPercentProductionChange *tof = *it; if( tof->m_percent == percent && tof->m_kindOf == kindOf) { tof->m_ref++; return; } ++it; } KindOfPercentProductionChange *newTof = newInstance( KindOfPercentProductionChange ); newTof->m_kindOf = kindOf; newTof->m_percent = percent; newTof->m_ref = 1; m_kindOfPercentProductionChangeList.push_back(newTof); } //------------------------------------------------------------------------------------------------- /** addTypeOfProductionCostChange adds a production change to the typeof list */ //------------------------------------------------------------------------------------------------- void Player::removeKindOfProductionCostChange( KindOfMaskType kindOf, Real percent ) { KindOfPercentProductionChangeListIt it = m_kindOfPercentProductionChangeList.begin(); while(it != m_kindOfPercentProductionChangeList.end()) { KindOfPercentProductionChange* tof = *it; if( tof->m_percent == percent && tof->m_kindOf == kindOf) { tof->m_ref--; if(tof->m_ref == 0) { m_kindOfPercentProductionChangeList.erase( it ); if(tof) tof->deleteInstance(); } return; } ++it; } DEBUG_ASSERTCRASH(FALSE, ("removeKindOfProductionCostChange was called with kindOf=%d and percent=%f. We could not find the entry in the list with these variables. CLH.",kindOf, percent)); } //------------------------------------------------------------------------------------------------- /** getProductionCostChangeBasedOnKindOf gets the cost percentage change based off of Kindof Mask */ //------------------------------------------------------------------------------------------------- Real Player::getProductionCostChangeBasedOnKindOf( KindOfMaskType kindOf ) const { Real start = 1.0f; KindOfPercentProductionChangeListIt it = m_kindOfPercentProductionChangeList.begin(); while(it != m_kindOfPercentProductionChangeList.end()) { KindOfPercentProductionChange *tof = *it; if(TEST_KINDOFMASK_MULTI(kindOf, tof->m_kindOf, KINDOFMASK_NONE)) { start *= (1 + tof->m_percent); } ++it; } return (start); } //------------------------------------------------------------------------------------------------- /** setAttackedBy */ //------------------------------------------------------------------------------------------------- void Player::setAttackedBy( Int playerNdx ) { DEBUG_ASSERTCRASH(playerNdx >= 0, ("Player::setAttackedBy Player index is %d", playerNdx)); m_attackedBy[playerNdx] = true; m_attackedFrame = TheGameLogic->getFrame(); } //------------------------------------------------------------------------------------------------- /** getAttackedBy */ //------------------------------------------------------------------------------------------------- Bool Player::getAttackedBy( Int playerNdx ) const { return m_attackedBy[playerNdx]; } // ------------------------------------------------------------------------------------------------ // Little wrapper function so I can use it in iterateObjects, which is cool. struct VisionSpiedStruct { Bool setting; KindOfMaskType whichUnits; PlayerIndex byWhom; }; static void iterator_setUnitsVisionSpied( Object *obj, void * voidData) { VisionSpiedStruct *data = (VisionSpiedStruct *)voidData; // I feel I have to disapprove of the naming of this gathering of cell functions. It is called by death, // alliance change, containment, spy change, and dynamic view range as well as partition cell change. if( obj && obj->isAnyKindOf(data->whichUnits) ) obj->setVisionSpied(data->setting, data->byWhom); } // ------------------------------------------------------------------------------------------------ void Player::setUnitsVisionSpied( Bool setting, KindOfMaskType whichUnits, PlayerIndex byWhom ) { VisionSpiedStruct data; data.setting = setting; data.whichUnits = whichUnits; data.byWhom = byWhom; // Being spied is now a property of the unit, not us, since we can spy only a portion of the enemy. iterateObjects( iterator_setUnitsVisionSpied, &data ); } // ------------------------------------------------------------------------------------------------ Bool Player::isPlayerObserver(void) const { return m_observer; } // ------------------------------------------------------------------------------------------------ Bool Player::isPlayerDead(void) const { return m_isPlayerDead; } // ------------------------------------------------------------------------------------------------ Bool Player::isPlayerActive(void) const { return !m_observer && !m_isPlayerDead; } // ------------------------------------------------------------------------------------------------ Bool Player::isPlayableSide( void ) const { return m_playerTemplate ? m_playerTemplate->isPlayableSide() : FALSE; } // end isPlayableSide // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void Player::crc( Xfer *xfer ) { // Player battle plan bonuses Bool battlePlanBonus = m_battlePlanBonuses != NULL; xfer->xferBool( &battlePlanBonus ); CRCDEBUG_LOG(("Player %d[%ls] %s battle plans\n", m_playerIndex, m_playerDisplayName.str(), (battlePlanBonus)?"has":"doesn't have")); if( m_battlePlanBonuses ) { CRCDUMPBATTLEPLANBONUSES(m_battlePlanBonuses, this, NULL); xfer->xferReal( &m_battlePlanBonuses->m_armorScalar ); xfer->xferReal( &m_battlePlanBonuses->m_sightRangeScalar ); xfer->xferInt( &m_battlePlanBonuses->m_bombardment ); xfer->xferInt( &m_battlePlanBonuses->m_holdTheLine ); xfer->xferInt( &m_battlePlanBonuses->m_searchAndDestroy ); m_battlePlanBonuses->m_validKindOf.xfer(xfer); m_battlePlanBonuses->m_invalidKindOf.xfer(xfer); } xfer->xferInt( &m_skillPoints ); xfer->xferInt( &m_sciencePurchasePoints ); } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer Method * Version Info: * 1: Initial version * 2: Player can now have a modifier on his skill points (multiplicative) * 3: Player can be excluded from the score screen via script. * 4: Player stores a list of specialpowerreadyframe timers, used by specialpowermodules abroad * 5: ??? (Profit) * 6: Store m_unitsShouldHunt, set to true after the script "Tell player to hunt" is called. * 7: added Preorder flag * 8: Save m_disabledSciences & m_hiddenSciences. jba. */ // ------------------------------------------------------------------------------------------------ void Player::xfer( Xfer *xfer ) { // version const XferVersion currentVersion = 8; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // money xfer->xferSnapshot( &m_money ); // upgrade list count Upgrade *upgrade; UnsignedShort upgradeCount = 0; for( upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() ) upgradeCount++; xfer->xferUnsignedShort( &upgradeCount ); if (version >= 7) { // preorder info xfer->xferBool( & m_isPreorder ); } if (version >= 8) { xfer->xferScienceVec(&m_sciencesDisabled); xfer->xferScienceVec(&m_sciencesHidden); } // xfer upgrade instances AsciiString upgradeName; if( xfer->getXferMode() == XFER_SAVE ) { for( upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() ) { // write upgrade name upgradeName = upgrade->getTemplate()->getUpgradeName(); xfer->xferAsciiString( &upgradeName ); // xfer upgrade data xfer->xferSnapshot( upgrade ); } // end for, upgrade } // end if, save else { const UpgradeTemplate *upgradeTemplate; for( UnsignedShort i = 0; i < upgradeCount; ++i ) { // read name xfer->xferAsciiString( &upgradeName ); // find template for this upgrade upgradeTemplate = TheUpgradeCenter->findUpgrade( upgradeName ); // sanity if( upgradeTemplate == NULL ) { DEBUG_CRASH(( "Player::xfer - Unable to find upgrade '%s'\n", upgradeName.str() )); throw SC_INVALID_DATA; } // end if // add upgrade to player, the status is invalid, but that's OK cause we're about to xfer it upgrade = addUpgrade( upgradeTemplate, UPGRADE_STATUS_INVALID ); // xfer upgrade data xfer->xferSnapshot( upgrade ); } // end for, i } // end else, load // radar info xfer->xferInt( &m_radarCount ); xfer->xferBool( & m_isPlayerDead ); xfer->xferInt( &m_disableProofRadarCount ); xfer->xferBool( & m_radarDisabled ); // upgrades in progress xfer->xferUpgradeMask( &m_upgradesInProgress ); // upgrades complete xfer->xferUpgradeMask( &m_upgradesCompleted ); // energy info xfer->xferSnapshot( &m_energy ); // // team prototypes ... this is only the fact that team prototypes are on this player // it is not the team prototype data itself // UnsignedShort prototypeCount = m_playerTeamPrototypes.size(); xfer->xferUnsignedShort( &prototypeCount ); TeamPrototypeID prototypeID; TeamPrototype *prototype; if( xfer->getXferMode() == XFER_SAVE ) { PlayerTeamList::iterator it; for( it = m_playerTeamPrototypes.begin(); it != m_playerTeamPrototypes.end(); ++it ) { prototype = *it; prototypeID = prototype->getID(); xfer->xferUser( &prototypeID, sizeof( TeamPrototypeID ) ); } // end for } // end if, save else { // empty the list right now m_playerTeamPrototypes.clear(); // read all the data for( UnsignedShort i = 0; i < prototypeCount; ++i ) { // read id xfer->xferUser( &prototypeID, sizeof( TeamPrototypeID ) ); // find prototype prototype = TheTeamFactory->findTeamPrototypeByID( prototypeID ); // sanity if( prototype == NULL ) { DEBUG_CRASH(( "Player::xfer - Unable to find team prototype by id\n" )); throw SC_INVALID_DATA; } // end if // put in list m_playerTeamPrototypes.push_back( prototype ); } // end for, i } // end else, load // build list info UnsignedShort buildListInfoCount = 0; BuildListInfo *buildListInfo; for( buildListInfo = m_pBuildList; buildListInfo; buildListInfo = buildListInfo->getNext() ) buildListInfoCount++; xfer->xferUnsignedShort( &buildListInfoCount ); if( xfer->getXferMode() == XFER_SAVE ) { // xfer each build list info for( buildListInfo = m_pBuildList; buildListInfo; buildListInfo = buildListInfo->getNext() ) xfer->xferSnapshot( buildListInfo ); } // end if, save else { // // destroy any build list that we got from loading the bare bones map, note that deleting // the head of these structures automatically deletes any links attached // if( m_pBuildList) m_pBuildList->deleteInstance(); m_pBuildList = NULL; // read each build list info for( UnsignedShort i = 0; i < buildListInfoCount; ++i ) { // allocate new build list buildListInfo = newInstance( BuildListInfo ); buildListInfo->setNextBuildList( NULL ); // attach to the *end* of the list in the player if( m_pBuildList == NULL ) m_pBuildList = buildListInfo; else { BuildListInfo *last = m_pBuildList; while( last->getNext() != NULL ) last = last->getNext(); last->setNextBuildList( buildListInfo ); } // end else // xfer data xfer->xferSnapshot( buildListInfo ); } // end for,i } // end else, load // ai player data Bool aiPlayerPresent = m_ai ? TRUE : FALSE; xfer->xferBool( &aiPlayerPresent ); if( (aiPlayerPresent == TRUE && m_ai == NULL) || (aiPlayerPresent == FALSE && m_ai != NULL) ) { DEBUG_CRASH(( "Player::xfer - m_ai present/missing mismatch\n" )); throw SC_INVALID_DATA;; } // end if if( m_ai ) xfer->xferSnapshot( m_ai ); // resource gathering manager Bool resourceGatheringManagerPresent = m_resourceGatheringManager ? TRUE : FALSE; xfer->xferBool( &resourceGatheringManagerPresent ); if( (resourceGatheringManagerPresent == TRUE && m_resourceGatheringManager == NULL) || (resourceGatheringManagerPresent == FALSE && m_resourceGatheringManager != NULL ) ) { DEBUG_CRASH(( "Player::xfer - m_resourceGatheringManager present/missing mismatch\n" )); throw SC_INVALID_DATA; } // end if if( m_resourceGatheringManager ) xfer->xferSnapshot( m_resourceGatheringManager ); // tunnel tracking system Bool tunnelTrackerPresent = m_tunnelSystem ? TRUE : FALSE; xfer->xferBool( &tunnelTrackerPresent ); if( (tunnelTrackerPresent == TRUE && m_tunnelSystem == NULL) || (tunnelTrackerPresent == FALSE && m_tunnelSystem != NULL) ) { DEBUG_CRASH(( "Player::xfer - m_tunnelSystem present/missing mismatch\n" )); throw SC_INVALID_DATA; } // end if if( m_tunnelSystem ) xfer->xferSnapshot( m_tunnelSystem ); // default team TeamID teamID = m_defaultTeam ? m_defaultTeam->getID() : TEAM_ID_INVALID; xfer->xferUser( &teamID, sizeof( TeamID ) ); if( xfer->getXferMode() == XFER_LOAD ) m_defaultTeam = TheTeamFactory->findTeamByID( teamID ); // sciences if (version >= 5) { // m_sciences will contain some intrinsic sciences and stuff, which we don't want. // so nuke 'em for load. if( xfer->getXferMode() == XFER_LOAD ) m_sciences.clear(); xfer->xferScienceVec(&m_sciences); } else { /* This code is WRONG WRONG WRONG and must not be used or mimicked; it is present for backwards "compatibility" only. (srj) */ UnsignedShort scienceCount = m_sciences.size(); xfer->xferUnsignedShort( &scienceCount ); ScienceType science; if( xfer->getXferMode() == XFER_SAVE ) { ScienceVec::const_iterator it; for( it = m_sciences.begin(); it != m_sciences.end(); ++it ) { science = *it; xfer->xferUser( &science, sizeof( ScienceType ) ); } } else { for( UnsignedShort i = 0; i < scienceCount; ++i ) { xfer->xferUser( &science, sizeof( ScienceType ) ); m_sciences.push_back( science ); } } /* This code is WRONG WRONG WRONG and must not be used or mimicked; it is present for backwards "compatibility" only. (srj) */ } // rank level xfer->xferInt( &m_rankLevel ); // skill points xfer->xferInt( &m_skillPoints ); // science purchase points xfer->xferInt( &m_sciencePurchasePoints ); // level up xfer->xferInt( &m_levelUp ); // level down xfer->xferInt( &m_levelDown ); // general name xfer->xferUnicodeString( &m_generalName ); // player relations xfer->xferSnapshot( m_playerRelations ); // team relations xfer->xferSnapshot( m_teamRelations ); // can build units xfer->xferBool( &m_canBuildUnits ); // can build base xfer->xferBool( &m_canBuildBase ); // observer xfer->xferBool( &m_observer ); if (version >= 2) { // current skill point modifier value xfer->xferReal( &m_skillPointsModifier); } else { m_skillPointsModifier = 1.0f; } if (version >= 3) { xfer->xferBool( &m_listInScoreScreen ); } else { m_listInScoreScreen = TRUE; } // attacked by xfer->xferUser( m_attackedBy, sizeof( Bool ) * MAX_PLAYER_COUNT ); // cash bounty percent xfer->xferReal( &m_cashBountyPercent ); // score keeper xfer->xferSnapshot( &m_scoreKeeper ); // size of and data for kindof percent production change list UnsignedShort percentProductionChangeCount = m_kindOfPercentProductionChangeList.size(); xfer->xferUnsignedShort( &percentProductionChangeCount ); KindOfPercentProductionChange *entry; if( xfer->getXferMode() == XFER_SAVE ) { KindOfPercentProductionChangeListIt it; // save each item for( it = m_kindOfPercentProductionChangeList.begin(); it != m_kindOfPercentProductionChangeList.end(); ++it ) { // get entry data entry = *it; // kind of mask type entry->m_kindOf.xfer(xfer); // percent xfer->xferReal( &entry->m_percent ); // ref xfer->xferUnsignedInt( &entry->m_ref ); } // end for } // end if, save else { // sanity, list must be empty right now if( m_kindOfPercentProductionChangeList.size() != 0 ) { DEBUG_CRASH(( "Player::xfer - m_kindOfPercentProductionChangeList should be empty but is not\n" )); throw SC_INVALID_DATA; } // end if // read each entry for( UnsignedInt i = 0; i < percentProductionChangeCount; ++i ) { // allocate new entry entry = newInstance( KindOfPercentProductionChange ); // read data entry->m_kindOf.xfer(xfer); xfer->xferReal( &entry->m_percent ); xfer->xferUnsignedInt( &entry->m_ref ); // put at end of list m_kindOfPercentProductionChangeList.push_back( entry ); } // end for i } // end else, load /////////////////////////////////////////////////////////////////////////// if ( version < 4 ) { m_specialPowerReadyTimerList.clear(); } else { UnsignedShort timerListSize = m_specialPowerReadyTimerList.size(); xfer->xferUnsignedShort( &timerListSize );// HANDY LITTLE SHORT TO SIZE MY LIST if( xfer->getXferMode() == XFER_SAVE ) { SpecialPowerReadyTimerType *timer; SpecialPowerReadyTimerListIterator it; for( it = m_specialPowerReadyTimerList.begin(); it != m_specialPowerReadyTimerList.end(); ++it ) { timer = &(*it); xfer->xferUnsignedInt( &timer->m_templateID ); xfer->xferUnsignedInt( &timer->m_readyFrame ); } } else { if( m_specialPowerReadyTimerList.size() != 0 ) // sanity, list must be empty right now { DEBUG_CRASH(( "Player::xfer - m_specialPowerReadyTimerList should be empty but is not\n" )); throw SC_INVALID_DATA; } // end if // read each entry for( UnsignedInt i = 0; i < timerListSize; ++i ) { SpecialPowerReadyTimerType timer; // read data xfer->xferUnsignedInt( &timer.m_templateID ); xfer->xferUnsignedInt( &timer.m_readyFrame ); // put at end of list m_specialPowerReadyTimerList.push_back( timer ); } // end for i } } /////////////////////////////////////////////////////////////////////////// // squads UnsignedShort squadCount = NUM_HOTKEY_SQUADS; xfer->xferUnsignedShort( &squadCount ); if( squadCount != NUM_HOTKEY_SQUADS ) { DEBUG_CRASH(( "Player::xfer - size of m_squadCount array has changed\n" )); throw SC_INVALID_DATA; } // end if for( UnsignedShort i = 0; i < squadCount; ++i ) { if( m_squads[ i ] == NULL ) { DEBUG_CRASH(( "Player::xfer - NULL squad at index '%d'\n", i )); throw SC_INVALID_DATA; } // end if xfer->xferSnapshot( m_squads[ i ] ); } // end for, i // current squad selection Bool currentSelectionPresent = m_currentSelection ? TRUE : FALSE; xfer->xferBool( ¤tSelectionPresent ); if( currentSelectionPresent ) { // allocate squad if needed if( m_currentSelection == NULL && xfer->getXferMode() == XFER_LOAD ) m_currentSelection = newInstance( Squad ); // xfer xfer->xferSnapshot( m_currentSelection ); } // end if // Player battle plan bonuses Bool battlePlanBonus = m_battlePlanBonuses != NULL; xfer->xferBool( &battlePlanBonus ); //If we're loading, it just replaces the bool if( xfer->getXferMode() == XFER_LOAD ) { if (m_battlePlanBonuses) { m_battlePlanBonuses->deleteInstance(); m_battlePlanBonuses = NULL; } if ( battlePlanBonus ) { m_battlePlanBonuses = newInstance( BattlePlanBonuses ); } } if( m_battlePlanBonuses ) { xfer->xferReal( &m_battlePlanBonuses->m_armorScalar ); xfer->xferReal( &m_battlePlanBonuses->m_sightRangeScalar ); xfer->xferInt( &m_battlePlanBonuses->m_bombardment ); xfer->xferInt( &m_battlePlanBonuses->m_holdTheLine ); xfer->xferInt( &m_battlePlanBonuses->m_searchAndDestroy ); m_battlePlanBonuses->m_validKindOf.xfer(xfer); m_battlePlanBonuses->m_invalidKindOf.xfer(xfer); } xfer->xferInt( &m_bombardBattlePlans ); xfer->xferInt( &m_holdTheLineBattlePlans ); xfer->xferInt( &m_searchAndDestroyBattlePlans ); if (version >= 6) { xfer->xferBool(&m_unitsShouldHunt); } else m_unitsShouldHunt = FALSE; } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void Player::loadPostProcess( void ) { } // end loadPostProcess