Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.

This commit is contained in:
LFeenanEA 2025-02-27 17:34:39 +00:00
parent 2e338c00cb
commit 3d0ee53a05
No known key found for this signature in database
GPG key ID: C6EBE8C2EA08F7E0
6072 changed files with 2283311 additions and 0 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,304 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Energy.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Energy.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Energy.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-----------------------------------------------------------------------------
Energy::Energy()
{
m_energyProduction = 0;
m_energyConsumption = 0;
m_owner = NULL;
m_powerSabotagedTillFrame = 0;
}
//-----------------------------------------------------------------------------
Int Energy::getProduction() const
{
if( TheGameLogic->getFrame() < m_powerSabotagedTillFrame )
{
//Power sabotaged, therefore no power.
return 0;
}
return m_energyProduction;
}
//-----------------------------------------------------------------------------
Real Energy::getEnergySupplyRatio() const
{
DEBUG_ASSERTCRASH(m_energyProduction >= 0 && m_energyConsumption >= 0, ("neg Energy numbers\n"));
if( TheGameLogic->getFrame() < m_powerSabotagedTillFrame )
{
//Power sabotaged, therefore no power, no ratio.
return 0.0f;
}
if (m_energyConsumption == 0)
return (Real)m_energyProduction;
return (Real)m_energyProduction / (Real)m_energyConsumption;
}
//-------------------------------------------------------------------------------------------------
Bool Energy::hasSufficientPower(void) const
{
if( TheGameLogic->getFrame() < m_powerSabotagedTillFrame )
{
//Power sabotaged, therefore no power.
return FALSE;
}
return m_energyProduction >= m_energyConsumption;
}
//-------------------------------------------------------------------------------------------------
void Energy::adjustPower(Int powerDelta, Bool adding)
{
if (powerDelta == 0) {
return;
}
if (powerDelta > 0) {
if (adding) {
addProduction(powerDelta);
} else {
addProduction(-powerDelta);
}
} else {
// Seems a little odd, however, consumption is reversed. Negative power is positive consumption.
if (adding) {
addConsumption(-powerDelta);
} else {
addConsumption(powerDelta);
}
}
}
//-------------------------------------------------------------------------------------------------
/** new 'obj' will now add/subtract from this energy construct */
//-------------------------------------------------------------------------------------------------
void Energy::objectEnteringInfluence( Object *obj )
{
// sanity
if( obj == NULL )
return;
// get the amount of energy this object produces or consumes
Int energy = obj->getTemplate()->getEnergyProduction();
// adjust energy
if( energy < 0 )
addConsumption( -energy );
else if( energy > 0 )
addProduction( energy );
// sanity
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
m_energyProduction, m_energyConsumption) );
} // end objectEnteringInfluence
//-------------------------------------------------------------------------------------------------
/** 'obj' will now no longer add/subtrack from this energy construct */
//-------------------------------------------------------------------------------------------------
void Energy::objectLeavingInfluence( Object *obj )
{
// sanity
if( obj == NULL )
return;
// get the amount of energy this object produces or consumes
Int energy = obj->getTemplate()->getEnergyProduction();
// adjust energy
if( energy < 0 )
addConsumption( energy );
else if( energy > 0 )
addProduction( -energy );
// sanity
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
m_energyProduction, m_energyConsumption) );
}
//-------------------------------------------------------------------------------------------------
/** Adds an energy bonus to the player's pool of energy when the "Control Rods" upgrade
is made to the American Cold Fusion Plant */
//-------------------------------------------------------------------------------------------------
void Energy::addPowerBonus( Object *obj )
{
// sanity
if( obj == NULL )
return;
addProduction(obj->getTemplate()->getEnergyBonus());
// sanity
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
m_energyProduction, m_energyConsumption) );
}
// ------------------------------------------------------------------------------------------------
/** Removed an energy bonus */
// ------------------------------------------------------------------------------------------------
void Energy::removePowerBonus( Object *obj )
{
// sanity
if( obj == NULL )
return;
addProduction( -obj->getTemplate()->getEnergyBonus() );
// sanity
DEBUG_ASSERTCRASH( m_energyProduction >= 0 && m_energyConsumption >= 0,
("Energy - Negative Energy numbers, Produce=%d Consume=%d\n",
m_energyProduction, m_energyConsumption) );
} // end removePowerBonus
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// Private functions
// ------------------------------------------------------------------------------------------------
void Energy::addProduction(Int amt)
{
m_energyProduction += amt;
if( m_owner == NULL )
return;
// A repeated Brownout signal does nothing bad, and we need to handle more than just edge cases.
// Like low power, now even more low power, refresh disable.
m_owner->onPowerBrownOutChange( !hasSufficientPower() );
}
// ------------------------------------------------------------------------------------------------
void Energy::addConsumption(Int amt)
{
m_energyConsumption += amt;
if( m_owner == NULL )
return;
m_owner->onPowerBrownOutChange( !hasSufficientPower() );
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void Energy::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void Energy::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 3;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// It is actually incorrect to save these, as they are reconstructed when the buildings are loaded
// I need to version though so old games will load wrong rather than crashing
// production
if( version < 2 )
xfer->xferInt( &m_energyProduction );
// consumption
if( version < 2 )
xfer->xferInt( &m_energyConsumption );
// owning player index
Int owningPlayerIndex;
if( xfer->getXferMode() == XFER_SAVE )
owningPlayerIndex = m_owner->getPlayerIndex();
xfer->xferInt( &owningPlayerIndex );
m_owner = ThePlayerList->getNthPlayer( owningPlayerIndex );
//Sabotage
if( version >= 3 )
{
xfer->xferUnsignedInt( &m_powerSabotagedTillFrame );
}
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void Energy::loadPostProcess( void )
{
} // end loadPostProcess

View file

@ -0,0 +1,126 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Handicap.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Handicap.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Handicap.h"
#include "Common/Player.h"
#include "Common/Dict.h"
#include "Common/ThingTemplate.h"
//-----------------------------------------------------------------------------
Handicap::Handicap()
{
init();
}
//-----------------------------------------------------------------------------
void Handicap::init()
{
for (Int i = 0; i < HANDICAP_TYPE_COUNT; ++i)
for (Int j = 0; j < THING_TYPE_COUNT; ++j)
m_handicaps[i][j] = 1.0f;
}
//-----------------------------------------------------------------------------
void Handicap::readFromDict(const Dict* d)
{
// this isn't very efficient, but is only called at load times,
// so it probably doesn't really matter.
const char* htNames[HANDICAP_TYPE_COUNT] =
{
"BUILDCOST",
"BUILDTIME",
// "FIREPOWER",
// "ARMOR",
// "GROUNDSPEED",
// "AIRSPEED",
// "INCOME"
};
const char* ttNames[THING_TYPE_COUNT] =
{
"GENERIC",
"BUILDINGS",
};
// no, you should NOT call init() here.
//init();
AsciiString c;
for (Int i = 0; i < HANDICAP_TYPE_COUNT; ++i)
{
for (Int j = 0; j < THING_TYPE_COUNT; ++j)
{
c.clear();
c.set("HANDICAP_");
c.concat(htNames[i]);
c.concat("_");
c.concat(ttNames[j]);
NameKeyType k = TheNameKeyGenerator->nameToKey(c);
Bool exists;
Real r = d->getReal(k, &exists);
if (exists)
m_handicaps[i][j] = r;
}
}
}
//-----------------------------------------------------------------------------
/*static*/ Handicap::ThingType Handicap::getBestThingType(const ThingTemplate *tmpl)
{
/// if this ends up being too slow, cache the information in the object
if (tmpl->isKindOf(KINDOF_STRUCTURE))
return BUILDINGS;
return GENERIC;
}
//-----------------------------------------------------------------------------
Real Handicap::getHandicap(HandicapType ht, const ThingTemplate *tmpl) const
{
ThingType tt = getBestThingType(tmpl);
return m_handicaps[ht][tt];
}

View file

@ -0,0 +1,113 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: MissionStats.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: MissionStats.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/MissionStats.h"
#include "Common/Player.h"
#include "Common/Xfer.h"
//-----------------------------------------------------------------------------
MissionStats::MissionStats()
{
init();
}
//-----------------------------------------------------------------------------
void MissionStats::init()
{
Int i;
for (i = 0; i < MAX_PLAYER_COUNT; ++i)
{
m_unitsKilled[i] = 0;
m_buildingsKilled[i] = 0;
}
m_unitsLost = 0;
m_buildingsLost = 0;
//m_whoLastHurtMe = PLAYER_NONE;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void MissionStats::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info;
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void MissionStats::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// units killed
xfer->xferUser( m_unitsKilled, sizeof( Int ) * MAX_PLAYER_COUNT );
// units lost
xfer->xferInt( &m_unitsLost );
// buidings killed
xfer->xferUser( m_buildingsKilled, sizeof( Int ) * MAX_PLAYER_COUNT );
// buildings lost
xfer->xferInt( &m_buildingsLost );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void MissionStats::loadPostProcess( void )
{
} // end loadPostProcess

View file

@ -0,0 +1,145 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Money.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Money.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Money.h"
#include "Common/GameAudio.h"
#include "Common/MiscAudio.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "Common/Xfer.h"
// ------------------------------------------------------------------------------------------------
UnsignedInt Money::withdraw(UnsignedInt amountToWithdraw, Bool playSound)
{
if (amountToWithdraw > m_money)
amountToWithdraw = m_money;
if (amountToWithdraw == 0)
return amountToWithdraw;
//@todo: Do we do this frequently enough that it is a performance hit?
AudioEventRTS event = TheAudio->getMiscAudio()->m_moneyWithdrawSound;
event.setPlayerIndex(m_playerIndex);
// Play a sound
if (playSound)
TheAudio->addAudioEvent(&event);
m_money -= amountToWithdraw;
return amountToWithdraw;
}
// ------------------------------------------------------------------------------------------------
void Money::deposit(UnsignedInt amountToDeposit, Bool playSound)
{
if (amountToDeposit == 0)
return;
//@todo: Do we do this frequently enough that it is a performance hit?
AudioEventRTS event = TheAudio->getMiscAudio()->m_moneyDepositSound;
event.setPlayerIndex(m_playerIndex);
// Play a sound
if (playSound)
TheAudio->addAudioEvent(&event);
m_money += amountToDeposit;
if( amountToDeposit > 0 )
{
Player *player = ThePlayerList->getNthPlayer( m_playerIndex );
if( player )
{
player->getAcademyStats()->recordIncome();
}
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void Money::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void Money::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// money value
xfer->xferUnsignedInt( &m_money );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void Money::loadPostProcess( void )
{
} // end loadPostProcess
// ------------------------------------------------------------------------------------------------
/** Parse a money amount for the ini file. E.g. DefaultStartingMoney = 10000 */
// ------------------------------------------------------------------------------------------------
void Money::parseMoneyAmount( INI *ini, void *instance, void *store, const void* userData )
{
// Someday, maybe, have mulitple fields like Gold:10000 Wood:1000 Tiberian:10
Money * money = (Money *)store;
INI::parseUnsignedInt( ini, instance, &money->m_money, userData );
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,493 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: PlayerList.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: PlayerList.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Errors.h"
#include "Common/DataChunk.h"
#include "Common/GameState.h"
#include "Common/GlobalData.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "Common/PlayerTemplate.h"
#include "Common/Team.h"
#include "Common/WellKnownKeys.h"
#include "Common/Xfer.h"
#ifdef _DEBUG
#include "GameLogic/Object.h"
#endif
#include "GameLogic/SidesList.h"
#include "GameNetwork/NetworkDefs.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-----------------------------------------------------------------------------
/*extern*/ PlayerList *ThePlayerList = NULL;
//-----------------------------------------------------------------------------
PlayerList::PlayerList() :
m_local(NULL),
m_playerCount(0)
{
// we only allocate a few of these, so don't bother pooling 'em
for (Int i = 0; i < MAX_PLAYER_COUNT; i++)
m_players[ i ] = NEW Player( i );
init();
}
//-----------------------------------------------------------------------------
PlayerList::~PlayerList()
{
try {
// the world is happier if we reinit things before destroying them,
// to avoid debug warnings
init();
} catch (...) {
// nothing
}
for( Int i = 0; i < MAX_PLAYER_COUNT; ++i )
delete m_players[ i ];
}
//-----------------------------------------------------------------------------
Player *PlayerList::getNthPlayer(Int i)
{
if( i < 0 || i >= MAX_PLAYER_COUNT )
{
// DEBUG_CRASH( ("Illegal player index\n") );
return NULL;
}
return m_players[i];
}
//-----------------------------------------------------------------------------
Player *PlayerList::findPlayerWithNameKey(NameKeyType key)
{
for (Int i = 0; i < m_playerCount; i++)
{
if (m_players[i]->getPlayerNameKey() == key)
{
return m_players[i];
}
}
return NULL;
}
//-----------------------------------------------------------------------------
void PlayerList::reset()
{
TheTeamFactory->clear(); // cleans up energy, among other things
init();
}
//-----------------------------------------------------------------------------
void PlayerList::newGame()
{
Int i;
DEBUG_ASSERTCRASH(this != NULL, ("null this"));
TheTeamFactory->clear(); // cleans up energy, among other things
// first, re-init ourselves.
init();
// ok, now create the rest of players we need.
Bool setLocal = false;
for( i = 0; i < TheSidesList->getNumSides(); i++)
{
Dict *d = TheSidesList->getSideInfo(i)->getDict();
AsciiString pname = d->getAsciiString(TheKey_playerName);
if (pname.isEmpty())
continue; // it's neutral, which we've already done, so skip it.
/// @todo The Player class should have a reset() method, instead of directly calling initFromDict() (MSB)
Player* p = m_players[m_playerCount++];
p->initFromDict(d);
// Multiplayer override
Bool exists; // throwaway, since we don't care if it exists
if (d->getBool(TheKey_multiplayerIsLocal, &exists))
{
DEBUG_LOG(("Player %s is multiplayer local\n", pname.str()));
setLocalPlayer(p);
setLocal = true;
}
if (!setLocal && !TheNetwork && d->getBool(TheKey_playerIsHuman))
{
setLocalPlayer(p);
setLocal = true;
}
// Set the build list.
p->setBuildList(TheSidesList->getSideInfo(i)->getBuildList());
// Build list is attached to player now, so release it from the side info.
TheSidesList->getSideInfo(i)->releaseBuildList();
}
if (!setLocal)
{
DEBUG_ASSERTCRASH(TheNetwork, ("*** Map has no human player... picking first nonneutral player for control\n"));
for( i = 0; i < TheSidesList->getNumSides(); i++)
{
Player* p = getNthPlayer(i);
if (p != getNeutralPlayer())
{
p->setPlayerType(PLAYER_HUMAN, false);
setLocalPlayer(p);
setLocal = true;
break;
}
}
}
// must reset teams *after* creating players.
TheTeamFactory->initFromSides(TheSidesList);
for( i = 0; i < TheSidesList->getNumSides(); i++)
{
Dict *d = TheSidesList->getSideInfo(i)->getDict();
Player* p = findPlayerWithNameKey(NAMEKEY(d->getAsciiString(TheKey_playerName)));
AsciiString tok;
AsciiString enemies = d->getAsciiString(TheKey_playerEnemies);
while (enemies.nextToken(&tok))
{
Player *p2 = findPlayerWithNameKey(NAMEKEY(tok));
if (p2)
{
p->setPlayerRelationship(p2, ENEMIES);
}
else
{
DEBUG_LOG(("unknown enemy %s\n",tok.str()));
}
}
AsciiString allies = d->getAsciiString(TheKey_playerAllies);
while (allies.nextToken(&tok))
{
Player *p2 = findPlayerWithNameKey(NAMEKEY(tok));
if (p2)
{
p->setPlayerRelationship(p2, ALLIES);
}
else
{
DEBUG_LOG(("unknown ally %s\n",tok.str()));
}
}
// finally, make sure self & neutral are correct.
p->setPlayerRelationship(p, ALLIES);
if (p != getNeutralPlayer())
p->setPlayerRelationship(getNeutralPlayer(), NEUTRAL);
p->setDefaultTeam();
}
}
//-----------------------------------------------------------------------------
void PlayerList::init()
{
m_playerCount = 1;
m_players[0]->init(NULL);
for (int i = 1; i < MAX_PLAYER_COUNT; i++)
m_players[i]->init(NULL);
// call setLocalPlayer so that becomingLocalPlayer() gets called appropriately
setLocalPlayer(m_players[0]);
}
//-----------------------------------------------------------------------------
void PlayerList::update()
{
// update all players
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
{
m_players[i]->update();
} // end for i
}
//-----------------------------------------------------------------------------
void PlayerList::newMap()
{
// update all players
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
{
m_players[i]->newMap();
} // end for i
}
// ------------------------------------------------------------------------
void PlayerList::teamAboutToBeDeleted(Team* team)
{
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
{
m_players[i]->removeTeamRelationship(team);
}
}
//=============================================================================
void PlayerList::updateTeamStates(void)
{
// Clear team flags for all players.
for( Int i = 0; i < MAX_PLAYER_COUNT; i++ )
{
m_players[i]->updateTeamStates();
} // end for i
}
//-----------------------------------------------------------------------------
Team *PlayerList::validateTeam( AsciiString owner )
{
// owner could be a player or team. first, check team names.
Team *t = TheTeamFactory->findTeam(owner);
if (t)
{
//DEBUG_LOG(("assigned obj %08lx to team %s\n",obj,owner.str()));
}
else
{
DEBUG_CRASH(("no team or player named %s could be found!\n", owner.str()));
t = getNeutralPlayer()->getDefaultTeam();
}
return t;
}
//-----------------------------------------------------------------------------
void PlayerList::setLocalPlayer(Player *player)
{
// can't set local player to null -- if you try, you get neutral.
if (player == NULL)
{
DEBUG_CRASH(("local player may not be null"));
player = getNeutralPlayer();
}
if (player != m_local)
{
// m_local can be null the very first time we call this.
if (m_local)
m_local->becomingLocalPlayer(false);
m_local = player;
player->becomingLocalPlayer(true);
}
#ifdef INTENSE_DEBUG
if (player)
{
DEBUG_LOG(("\n----------\n"));
// did you know? you can use "%ls" to print a doublebyte string, even in a single-byte printf...
DEBUG_LOG(("Switching local players. The new player is named '%ls' (%s) and owns the following objects:\n",
player->getPlayerDisplayName().str(),
TheNameKeyGenerator->keyToName(player->getPlayerNameKey()).str()
));
for (Object *obj = player->getFirstOwnedObject(); obj; obj = obj->getNextOwnedObject())
{
DEBUG_LOG(("Obj %08lx is of type %s",obj,obj->getTemplate()->getName().str()));
if (!player->canBuild(obj->getTemplate()))
{
DEBUG_LOG((" (NOT BUILDABLE)"));
}
DEBUG_LOG(("\n"));
}
DEBUG_LOG(("\n----------\n"));
}
#endif
}
//-----------------------------------------------------------------------------
Player *PlayerList::getPlayerFromMask( PlayerMaskType mask )
{
Player *player = NULL;
Int i;
for( i = 0; i < MAX_PLAYER_COUNT; i++ )
{
player = getNthPlayer( i );
if( player && player->getPlayerMask() == mask )
return player;
} // end for i
DEBUG_CRASH( ("Player does not exist for mask\n") );
return NULL; // mask not found
} // end getPlayerFromMask
//-----------------------------------------------------------------------------
Player *PlayerList::getEachPlayerFromMask( PlayerMaskType& maskToAdjust )
{
Player *player = NULL;
Int i;
for( i = 0; i < MAX_PLAYER_COUNT; i++ )
{
player = getNthPlayer( i );
if ( player && BitTest(player->getPlayerMask(), maskToAdjust ))
{
maskToAdjust &= (~player->getPlayerMask());
return player;
}
} // end for i
DEBUG_CRASH( ("No players found that contain any matching masks.\n") );
maskToAdjust = 0;
return NULL; // mask not found
}
//-------------------------------------------------------------------------------------------------
PlayerMaskType PlayerList::getPlayersWithRelationship( Int srcPlayerIndex, UnsignedInt allowedRelationships )
{
PlayerMaskType retVal = 0;
if (allowedRelationships == 0)
return retVal;
Player *srcPlayer = getNthPlayer(srcPlayerIndex);
if (!srcPlayer)
return retVal;
if (BitTest(allowedRelationships, ALLOW_SAME_PLAYER))
BitSet(retVal, srcPlayer->getPlayerMask());
for ( Int i = 0; i < getPlayerCount(); ++i )
{
Player *player = getNthPlayer(i);
if (!player)
continue;
if (player == srcPlayer)
continue;
switch (srcPlayer->getRelationship(player->getDefaultTeam()))
{
case ENEMIES:
if (BitTest(allowedRelationships, ALLOW_ENEMIES))
BitSet(retVal, player->getPlayerMask());
break;
case ALLIES:
if (BitTest(allowedRelationships, ALLOW_ALLIES))
BitSet(retVal, player->getPlayerMask());
break;
case NEUTRAL:
if (BitTest(allowedRelationships, ALLOW_NEUTRAL))
BitSet(retVal, player->getPlayerMask());
break;
}
}
return retVal;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void PlayerList::crc( Xfer *xfer )
{
xfer->xferInt( &m_playerCount );
for( Int i = 0; i < m_playerCount; ++i )
xfer->xferSnapshot( m_players[ i ] );
}
// ------------------------------------------------------------------------------------------------
/** Xfer Method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void PlayerList::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// xfer the player count
Int playerCount = m_playerCount;
xfer->xferInt( &playerCount );
//
// sanity, the player count read from the file should match our player count that
// was setup from the bare bones map load since that data can't change during run time
//
if( playerCount != m_playerCount )
{
DEBUG_CRASH(( "Invalid player count '%d', should be '%d'\n", playerCount, m_playerCount ));
throw SC_INVALID_DATA;
} // end if
// xfer each of the player data
for( Int i = 0; i < playerCount; ++i )
xfer->xferSnapshot( m_players[ i ] );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void PlayerList::loadPostProcess( void )
{
} // end postProcessLoad

View file

@ -0,0 +1,418 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: PlayerTemplate.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: PlayerTemplate.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#define DEFINE_VETERANCY_NAMES // for TheVeterancyNames[]
#include "Common/GameCommon.h"
#include "Common/PlayerTemplate.h"
#include "Common/Player.h"
#include "Common/INI.h"
#include "Common/Science.h"
#include "GameClient/Image.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/*static*/ const FieldParse* PlayerTemplate::getFieldParse()
{
static const FieldParse TheFieldParseTable[] =
{
{ "Side", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_side ) },
{ "BaseSide", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_baseSide ) },
{ "PlayableSide", INI::parseBool, NULL, offsetof( PlayerTemplate, m_playableSide ) },
{ "DisplayName", INI::parseAndTranslateLabel, NULL, offsetof( PlayerTemplate, m_displayName) },
{ "StartMoney", PlayerTemplate::parseStartMoney, NULL, offsetof( PlayerTemplate, m_money ) },
{ "PreferredColor", INI::parseRGBColor, NULL, offsetof( PlayerTemplate, m_preferredColor ) },
{ "StartingBuilding", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingBuilding ) },
{ "StartingUnit0", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[0] ) },
{ "StartingUnit1", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[1] ) },
{ "StartingUnit2", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[2] ) },
{ "StartingUnit3", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[3] ) },
{ "StartingUnit4", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[4] ) },
{ "StartingUnit5", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[5] ) },
{ "StartingUnit6", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[6] ) },
{ "StartingUnit7", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[7] ) },
{ "StartingUnit8", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[8] ) },
{ "StartingUnit9", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_startingUnits[9] ) },
{ "ProductionCostChange", PlayerTemplate::parseProductionCostChange, NULL, 0 },
{ "ProductionTimeChange", PlayerTemplate::parseProductionTimeChange, NULL, 0 },
{ "ProductionVeterancyLevel", PlayerTemplate::parseProductionVeterancyLevel, NULL, 0 },
{ "IntrinsicSciences", INI::parseScienceVector, NULL, offsetof( PlayerTemplate, m_intrinsicSciences ) },
{ "PurchaseScienceCommandSetRank1",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_purchaseScienceCommandSetRank1 ) },
{ "PurchaseScienceCommandSetRank3",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_purchaseScienceCommandSetRank3 ) },
{ "PurchaseScienceCommandSetRank8",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_purchaseScienceCommandSetRank8 ) },
{ "SpecialPowerShortcutCommandSet",INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_specialPowerShortcutCommandSet ) },
{ "SpecialPowerShortcutWinName" ,INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_specialPowerShortcutWinName) },
{ "SpecialPowerShortcutButtonCount",INI::parseInt, NULL, offsetof( PlayerTemplate, m_specialPowerShortcutButtonCount ) },
{ "IsObserver", INI::parseBool, NULL, offsetof( PlayerTemplate, m_observer ) },
{ "OldFaction", INI::parseBool, NULL, offsetof( PlayerTemplate, m_oldFaction ) },
{ "IntrinsicSciencePurchasePoints", INI::parseInt, NULL, offsetof( PlayerTemplate, m_intrinsicSPP ) },
{ "ScoreScreenImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_scoreScreenImage ) },
{ "LoadScreenImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_loadScreenImage ) },
{ "LoadScreenMusic", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_loadScreenMusic ) },
{ "ScoreScreenMusic", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_scoreScreenMusic ) },
{ "HeadWaterMark", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_headWaterMark ) },
{ "FlagWaterMark", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_flagWaterMark ) },
{ "EnabledImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_enabledImage ) },
//{ "DisabledImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_disabledImage ) },
//{ "HiliteImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_hiliteImage ) },
//{ "PushedImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_pushedImage ) },
{ "SideIconImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_sideIconImage ) },
{ "GeneralImage", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_generalImage ) },
{ "BeaconName", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_beaconTemplate ) },
{ "ArmyTooltip", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_tooltip ) },
{ "Features", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_strGeneralFeatures ) },
{ "MedallionRegular", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_strMedallionNormal ) },
{ "MedallionHilite", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_strMedallionHilite ) },
{ "MedallionSelect", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_strMedallionSelected ) },
{ NULL, NULL, NULL, 0 },
};
return TheFieldParseTable;
}
AsciiString PlayerTemplate::getStartingUnit( Int i ) const
{
if (i<0 || i>= MAX_MP_STARTING_UNITS)
return AsciiString::TheEmptyString;
return m_startingUnits[i];
}
//-------------------------------------------------------------------------------------------------
// This is is a Template, and a percent change to the cost of producing it.
/*static*/ void PlayerTemplate::parseProductionCostChange( INI* ini, void *instance, void *store, const void* /*userData*/ )
{
PlayerTemplate* self = (PlayerTemplate*)instance;
NameKeyType buildTemplateKey = NAMEKEY(ini->getNextToken());
Real percentChange = INI::scanPercentToReal(ini->getNextToken());
self->m_productionCostChanges[buildTemplateKey] = percentChange;
}
//-------------------------------------------------------------------------------------------------
/*static*/ void PlayerTemplate::parseProductionTimeChange( INI* ini, void *instance, void *store, const void* /*userData*/ )
{
PlayerTemplate* self = (PlayerTemplate*)instance;
NameKeyType buildTemplateKey = NAMEKEY(ini->getNextToken());
Real percentChange = INI::scanPercentToReal(ini->getNextToken());
self->m_productionTimeChanges[buildTemplateKey] = percentChange;
}
//-------------------------------------------------------------------------------------------------
/*static*/ void PlayerTemplate::parseProductionVeterancyLevel( INI* ini, void *instance, void *store, const void* /*userData*/ )
{
PlayerTemplate* self = (PlayerTemplate*)instance;
// Format is ThingTemplatename VeterancyName
AsciiString HACK = AsciiString(ini->getNextToken());
NameKeyType buildTemplateKey = NAMEKEY(HACK.str());
VeterancyLevel startLevel = (VeterancyLevel)INI::scanIndexList(ini->getNextToken(), TheVeterancyNames);
self->m_productionVeterancyLevels[buildTemplateKey] = startLevel;
}
//-------------------------------------------------------------------------------------------------
/** Parse integer money and deposit in the m_money */
//-------------------------------------------------------------------------------------------------
/*static*/ void PlayerTemplate::parseStartMoney( INI* ini, void *instance, void *store, const void* /*userData*/ )
{
Int money = 0;
// parse the money as a regular "FIELD = <integer>"
INI::parseInt( ini, instance, &money, NULL );
// assign the money into the 'Money' (m_money) pointed to at 'store'
Money *theMoney = (Money *)store;
theMoney->init();
theMoney->deposit( money );
} // end parseStartMoney
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
PlayerTemplate::PlayerTemplate() :
m_nameKey(NAMEKEY_INVALID),
m_observer(false),
m_playableSide(false),
m_oldFaction(false),
m_intrinsicSPP(0),
m_specialPowerShortcutButtonCount(0)
{
m_preferredColor.red = m_preferredColor.green = m_preferredColor.blue = 0.0f;
m_beaconTemplate.clear();
}
//-----------------------------------------------------------------------------
const Image *PlayerTemplate::getHeadWaterMarkImage( void ) const
{
return TheMappedImageCollection->findImageByName(m_headWaterMark);
}
//-----------------------------------------------------------------------------
const Image *PlayerTemplate::getFlagWaterMarkImage( void ) const
{
return TheMappedImageCollection->findImageByName(m_flagWaterMark);
}
//-----------------------------------------------------------------------------
const Image *PlayerTemplate::getSideIconImage( void ) const
{
return TheMappedImageCollection->findImageByName(m_sideIconImage);
}
//-----------------------------------------------------------------------------
const Image *PlayerTemplate::getGeneralImage( void ) const
{
return TheMappedImageCollection->findImageByName(m_generalImage);
}
//-----------------------------------------------------------------------------
const Image *PlayerTemplate::getEnabledImage( void ) const
{
return TheMappedImageCollection->findImageByName(m_enabledImage);
}
//-----------------------------------------------------------------------------
//const Image *PlayerTemplate::getDisabledImage( void ) const
//{
// return TheMappedImageCollection->findImageByName(m_disabledImage);
//}
//-----------------------------------------------------------------------------
//const Image *PlayerTemplate::getHiliteImage( void ) const
//{
// return TheMappedImageCollection->findImageByName(m_hiliteImage);
//}
//-----------------------------------------------------------------------------
//const Image *PlayerTemplate::getPushedImage( void ) const
//{
// return TheMappedImageCollection->findImageByName(m_pushedImage);
//}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/*extern*/ PlayerTemplateStore *ThePlayerTemplateStore = NULL;
//-----------------------------------------------------------------------------
PlayerTemplateStore::PlayerTemplateStore()
{
// nothing
}
//-----------------------------------------------------------------------------
PlayerTemplateStore::~PlayerTemplateStore()
{
// nothing
}
//-----------------------------------------------------------------------------
void PlayerTemplateStore::init()
{
m_playerTemplates.clear();
}
//-----------------------------------------------------------------------------
void PlayerTemplateStore::reset()
{
// don't reset this list here; we want to retain this info.
// m_playerTemplates.clear();
}
//-----------------------------------------------------------------------------
void PlayerTemplateStore::update()
{
// nothing
}
Int PlayerTemplateStore::getTemplateNumByName(AsciiString name) const
{
for (Int num = 0; num < m_playerTemplates.size(); num++)
{
if (m_playerTemplates[num].getName().compareNoCase(name.str()) == 0)
return num;
}
DEBUG_ASSERTCRASH(NULL, ("Template doesn't exist for given name"));
return -1;
}
//-----------------------------------------------------------------------------
const PlayerTemplate* PlayerTemplateStore::findPlayerTemplate(NameKeyType namekey) const
{
// begin ugly, hokey code to quietly load old maps...
static NameKeyType a0 = NAMEKEY("FactionAmerica");
static NameKeyType a1 = NAMEKEY("FactionAmericaChooseAGeneral");
static NameKeyType a2 = NAMEKEY("FactionAmericaTankCommand");
static NameKeyType a3 = NAMEKEY("FactionAmericaSpecialForces");
static NameKeyType a4 = NAMEKEY("FactionAmericaAirForce");
static NameKeyType c0 = NAMEKEY("FactionChina");
static NameKeyType c1 = NAMEKEY("FactionChinaChooseAGeneral");
static NameKeyType c2 = NAMEKEY("FactionChinaRedArmy");
static NameKeyType c3 = NAMEKEY("FactionChinaSpecialWeapons");
static NameKeyType c4 = NAMEKEY("FactionChinaSecretPolice");
static NameKeyType g0 = NAMEKEY("FactionGLA");
static NameKeyType g1 = NAMEKEY("FactionGLAChooseAGeneral");
static NameKeyType g2 = NAMEKEY("FactionGLATerrorCell");
static NameKeyType g3 = NAMEKEY("FactionGLABiowarCommand");
static NameKeyType g4 = NAMEKEY("FactionGLAWarlordCommand");
if (namekey == a1 || namekey == a2 || namekey == a3 || namekey == a4)
namekey = a0;
else if (namekey == c1 || namekey == c2 || namekey == c3 || namekey == c4)
namekey = c0;
else if (namekey == g1 || namekey == g2 || namekey == g3 || namekey == g4)
namekey = g0;
// end ugly, hokey code to quietly load old maps...
#ifdef _DEBUG
AsciiString nn = KEYNAME(namekey);
#endif
for (PlayerTemplateVector::const_iterator it = m_playerTemplates.begin(); it != m_playerTemplates.end(); ++it)
{
#ifdef _DEBUG
AsciiString n = KEYNAME((*it).getNameKey());
#endif
if ((*it).getNameKey() == namekey)
return &(*it);
}
return NULL;
}
//-----------------------------------------------------------------------------
const PlayerTemplate* PlayerTemplateStore::getNthPlayerTemplate(Int i) const
{
if (i >= 0 && i < m_playerTemplates.size())
return &m_playerTemplates[i];
return NULL;
}
//-------------------------------------------------------------------------------------------------
// @todo: PERF_EVALUATE Get a perf timer on this.
// If this function is called frequently, there are some relatively trivial changes we could make to
// have it run a lot faster.
void PlayerTemplateStore::getAllSideStrings(AsciiStringList *outStringList)
{
if (!outStringList)
return;
// should outStringList be cleared first? If so, that would go here
AsciiStringList tmpList;
Int numTemplates = getPlayerTemplateCount();
for ( Int i = 0; i < numTemplates; ++i )
{
const PlayerTemplate *pt = getNthPlayerTemplate(i);
// Sanity
if (!pt)
continue;
if (std::find(tmpList.begin(), tmpList.end(), pt->getSide()) == tmpList.end())
tmpList.push_back(pt->getSide());
}
// tmpList is now filled with all unique sides found in the player templates.
// splice is a constant-time function which takes all elements from tmpList and
// inserts them before outStringList->end(), and also removes them from tmpList
outStringList->splice(outStringList->end(), tmpList);
// all done
}
//-------------------------------------------------------------------------------------------------
/*static*/ void PlayerTemplateStore::parsePlayerTemplateDefinition( INI* ini )
{
const char* c = ini->getNextToken();
NameKeyType namekey = NAMEKEY(c);
PlayerTemplate* pt = const_cast<PlayerTemplate*>(ThePlayerTemplateStore->findPlayerTemplate(namekey));
if (pt)
{
ini->initFromINI(pt, pt->getFieldParse() );
pt->setNameKey(namekey);
}
else
{
PlayerTemplate npt;
ini->initFromINI( &npt, npt.getFieldParse() );
npt.setNameKey(namekey);
ThePlayerTemplateStore->m_playerTemplates.push_back(npt);
}
}
//-------------------------------------------------------------------------------------------------
void INI::parsePlayerTemplateDefinition( INI* ini )
{
PlayerTemplateStore::parsePlayerTemplateDefinition(ini);
}

View file

@ -0,0 +1,322 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: ProductionPrerequisite.cpp /////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: ProductionPrerequisite.cpp
//
// Created: Steven Johnson, October 2001
//
// Desc: @todo
//
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/ProductionPrerequisite.h"
#include "Common/Player.h"
#include "Common/ThingFactory.h"
#include "Common/ThingTemplate.h"
#include "GameLogic/Object.h"
#include "GameClient/Drawable.h"
#include "GameClient/GameText.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-----------------------------------------------------------------------------
ProductionPrerequisite::ProductionPrerequisite()
{
init();
}
//-----------------------------------------------------------------------------
ProductionPrerequisite::~ProductionPrerequisite()
{
}
//-----------------------------------------------------------------------------
void ProductionPrerequisite::init()
{
m_prereqUnits.clear();
m_prereqSciences.clear();
}
//=============================================================================
void ProductionPrerequisite::resolveNames()
{
for (Int i = 0; i < m_prereqUnits.size(); i++)
{
//
// note that this will find the template at the "top most" level (not override
// sub-temlates), which is what we want ... we conceptually only have one
// template for any given thing, it's only the *data* that is overridden
//
if( m_prereqUnits[ i ].name.isNotEmpty() )
{
m_prereqUnits[i].unit = TheThingFactory->findTemplate(m_prereqUnits[i].name); // might be null
/** @todo for now removing this assert until we can completely remove
the GDF stuff, the problem is that some INI files refer to GDF names, and they
aren't yet loaded in the world builder but will all go away later anyway etc */
DEBUG_ASSERTCRASH(m_prereqUnits[i].unit,("could not find prereq %s\n",m_prereqUnits[i].name.str()));
m_prereqUnits[i].name.clear(); // we're done with it
}
}
}
//-----------------------------------------------------------------------------
Int ProductionPrerequisite::calcNumPrereqUnitsOwned(const Player *player, Int counts[MAX_PREREQ]) const
{
const ThingTemplate *tmpls[MAX_PREREQ];
Int cnt = m_prereqUnits.size();
if (cnt > MAX_PREREQ)
cnt = MAX_PREREQ;
for (int i = 0; i < cnt; i++)
tmpls[i] = m_prereqUnits[i].unit;
player->countObjectsByThingTemplate(cnt, tmpls, false, counts);
return cnt;
}
//-----------------------------------------------------------------------------
Int ProductionPrerequisite::getAllPossibleBuildFacilityTemplates(const ThingTemplate* tmpls[], Int maxtmpls) const
{
Int count = 0;
for (int i = 0; i < m_prereqUnits.size(); i++)
{
if (i > 0 && !(m_prereqUnits[i].flags & UNIT_OR_WITH_PREV))
break;
if (count >= maxtmpls)
break;
tmpls[count++] = m_prereqUnits[i].unit;
}
return count;
}
//-----------------------------------------------------------------------------
const ThingTemplate *ProductionPrerequisite::getExistingBuildFacilityTemplate( const Player *player ) const
{
DEBUG_ASSERTCRASH(player, ("player may not be null"));
if (m_prereqUnits.size())
{
Int ownCount[MAX_PREREQ];
Int cnt = calcNumPrereqUnitsOwned(player, ownCount);
for (int i = 0; i < cnt; i++)
{
if (i > 0 && !(m_prereqUnits[i].flags & UNIT_OR_WITH_PREV))
break;
if (ownCount[i])
return m_prereqUnits[i].unit;
}
}
return NULL;
}
//-----------------------------------------------------------------------------
Bool ProductionPrerequisite::isSatisfied(const Player *player) const
{
Int i;
if (!player)
return false;
// gotta have all the prereq sciences.
for (i = 0; i < m_prereqSciences.size(); i++)
{
if (!player->hasScience(m_prereqSciences[i]))
return false;
}
// the player must have at least one instance of each prereq unit.
Int ownCount[MAX_PREREQ];
Int cnt = calcNumPrereqUnitsOwned(player, ownCount);
// fix up the "or" cases. (start at 1!)
for (i = 1; i < cnt; i++)
{
if (m_prereqUnits[i].flags & UNIT_OR_WITH_PREV)
{
ownCount[i] += ownCount[i-1]; // lump 'em together for prereq purposes
ownCount[i-1] = -1; // flag for "ignore me"
}
}
for (i = 0; i < cnt; i++)
{
if (ownCount[i] == -1) // the magic "ignore me" flag
continue;
if (ownCount[i] == 0) // everything not ignored, is required
return false;
}
return true;
}
//-------------------------------------------------------------------------------------------------
/** Add a unit prerequisite, if 'orWithPrevious' is set then this unit is said
* to be an alternate prereq to the previously added unit, otherwise this becomes
* a new 'block' and is required in ADDDITION to other entries.
* Return FALSE if no space left to add unit */
//-------------------------------------------------------------------------------------------------
void ProductionPrerequisite::addUnitPrereq( AsciiString unit, Bool orUnitWithPrevious )
{
PrereqUnitRec info;
info.name = unit;
info.flags = orUnitWithPrevious ? UNIT_OR_WITH_PREV : 0;
info.unit = NULL;
m_prereqUnits.push_back(info);
} // end addUnitPrereq
//-------------------------------------------------------------------------------------------------
/** Add a unit prerequisite, if 'orWithPrevious' is set then this unit is said
* to be an alternate prereq to the previously added unit, otherwise this becomes
* a new 'block' and is required in ADDDITION to other entries.
* Return FALSE if no space left to add unit */
//-------------------------------------------------------------------------------------------------
void ProductionPrerequisite::addUnitPrereq( const std::vector<AsciiString>& units )
{
Bool orWithPrevious = false;
for (int i = 0; i < units.size(); ++i)
{
addUnitPrereq(units[i], orWithPrevious);
orWithPrevious = true;
}
} // end addUnitPrereq
//-------------------------------------------------------------------------------------------------
// returns an asciistring which is a list of all the prerequisites
// not satisfied yet
UnicodeString ProductionPrerequisite::getRequiresList(const Player *player) const
{
// if player is invalid, return empty string
if (!player)
return UnicodeString::TheEmptyString;
UnicodeString requiresList = UnicodeString::TheEmptyString;
// check the prerequired units
Int ownCount[MAX_PREREQ];
Int cnt = calcNumPrereqUnitsOwned(player, ownCount);
Int i;
Bool orRequirements[MAX_PREREQ];
//Added for fix below in getRequiresList
//By Sadullah Nader
//Initializes the OR_WITH_PREV structures
for (i = 0; i < MAX_PREREQ; i++)
{
orRequirements[i] = FALSE;
}
//
// account for the "or" unit cases, start for loop at 1
for (i = 1; i < cnt; i++)
{
if (m_prereqUnits[i].flags & UNIT_OR_WITH_PREV)
{
orRequirements[i] = TRUE; // set the flag for this unit to be "ored" with previous
ownCount[i] += ownCount[i-1]; // lump 'em together for prereq purposes
ownCount[i-1] = -1; // flag for "ignore me"
}
}
// check to see if anything is required
const ThingTemplate *unit;
UnicodeString unitName;
Bool firstRequirement = true;
for (i = 0; i < cnt; i++)
{
// we have an unfulfilled requirement
if (ownCount[i] == 0) {
if(orRequirements[i])
{
unit = m_prereqUnits[i-1].unit;
unitName = unit->getDisplayName();
unitName.concat( L" " );
unitName.concat(TheGameText->fetch("CONTROLBAR:OrRequirement", NULL));
unitName.concat( L" " );
requiresList.concat(unitName);
}
// get the requirement and then its name
unit = m_prereqUnits[i].unit;
unitName = unit->getDisplayName();
// gets command button, and then modifies unitName
//CommandButton *cmdButton = TheControlBar->findCommandButton(unit->getName());
//if (cmdButton)
//unitName.translate(TheGameText->fetch(cmdButton->m_textLabel.str()));
// format name appropriately with 'returns' if necessary
if (firstRequirement)
firstRequirement = false;
else
unitName.concat(L"\n");
// add it to the list
requiresList.concat(unitName);
}
}
Bool hasSciences = TRUE;
// gotta have all the prereq sciences.
for (i = 0; i < m_prereqSciences.size(); i++)
{
if (!player->hasScience(m_prereqSciences[i]))
hasSciences = FALSE;
}
if (hasSciences == FALSE) {
if (firstRequirement) {
firstRequirement = false;
} else {
unitName.concat(L"\n");
}
requiresList.concat(TheGameText->fetch("CONTROLBAR:GeneralsPromotion", NULL));
}
// return final list
return requiresList;
}

View file

@ -0,0 +1,291 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: ResourceGatheringManager.cpp ///////////////////////////////////////////////////////////
// The part of a Player's brain that keeps track of all Resource type Objects and makes
// gathering type decisions based on them.
// Author: Graham Smallwood, January, 2002
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/ResourceGatheringManager.h"
#include "Common/ActionManager.h"
#include "Common/Xfer.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#include "GameLogic/PartitionManager.h"
#include "GameLogic/Module/SupplyTruckAIUpdate.h"
#include "GameLogic/Module/SupplyCenterDockUpdate.h"
#include "GameLogic/Module/SupplyWarehouseDockUpdate.h"
#include "GameLogic/Module/UpdateModule.h"
ResourceGatheringManager::ResourceGatheringManager()
{
}
ResourceGatheringManager::~ResourceGatheringManager()
{
m_supplyWarehouses.erase( m_supplyWarehouses.begin(), m_supplyWarehouses.end() );
m_supplyCenters.erase( m_supplyCenters.begin(), m_supplyCenters.end() );
}
void ResourceGatheringManager::addSupplyCenter( Object *newCenter )
{
if( newCenter == NULL )
return;
m_supplyCenters.push_back( newCenter->getID() );
}
void ResourceGatheringManager::removeSupplyCenter( Object *oldCenter )
{
if( oldCenter == NULL )
return;
ObjectID targetID = oldCenter->getID();
objectIDListIterator iterator = m_supplyCenters.begin();
while( iterator != m_supplyCenters.end() )
{
if( targetID == *iterator )
{
iterator = m_supplyCenters.erase( iterator );
}
else
iterator++;
}
}
void ResourceGatheringManager::addSupplyWarehouse( Object *newWarehouse )
{
if( newWarehouse == NULL )
return;
m_supplyWarehouses.push_back( newWarehouse->getID() );
}
void ResourceGatheringManager::removeSupplyWarehouse( Object *oldWarehouse )
{
if( oldWarehouse == NULL )
return;
ObjectID targetID = oldWarehouse->getID();
objectIDListIterator iterator = m_supplyWarehouses.begin();
while( iterator != m_supplyWarehouses.end() )
{
if( targetID == *iterator )
{
iterator = m_supplyWarehouses.erase( iterator );
}
else
iterator++;
}
}
static Real computeRelativeCost( Object *queryObject, Object *destObject, Real *pureDistanceSquared )
{
/** @todo This gets filled with Pathfinding computations, analysis of Boxes remaining,
Threat calculations, paths of other trucks, and other fancy stuff.
*/
//A good score is a very small number.
if( queryObject == NULL || destObject == NULL )
return FLT_MAX;
if( !TheActionManager->canTransferSuppliesAt(queryObject, destObject) )
return FLT_MAX;// Handles emptyness and alliances
DockUpdateInterface *dockInterface = destObject->getDockUpdateInterface();
if( !dockInterface->isClearToApproach( queryObject ) )
return FLT_MAX;
// since we don't care about the distance as a distance per se, but rather as
// a goodness-factor, save some time by getting the dist-sqr (srj)
Real distSquared = ThePartitionManager->getDistanceSquared(queryObject, destObject, FROM_CENTER_3D);
// I need the distance, but I don't want to count on the coincidence that
// the abstract 'cost' this function returns happens to be just the distance, since it could
// become more complicated
if( pureDistanceSquared )
*pureDistanceSquared = distSquared;
return distSquared;
}
Object *ResourceGatheringManager::findBestSupplyWarehouse( Object *queryObject )
{
Object *bestWarehouse = NULL;
Real maxDistanceSquared = 100000;
if( ( queryObject == NULL ) || ( queryObject->getAI() == NULL ) )
return NULL;
SupplyTruckAIInterface *supplyTruckAI = queryObject->getAI()->getSupplyTruckAIInterface();
if( supplyTruckAI )
{
// Check for a dock override being set.
ObjectID dockID = supplyTruckAI->getPreferredDockID();
Object *dock = TheGameLogic->findObjectByID(dockID);
if( dock )
{
static const NameKeyType key_warehouseUpdate = NAMEKEY("SupplyWarehouseDockUpdate");
SupplyWarehouseDockUpdate *warehouseModule = (SupplyWarehouseDockUpdate*)dock->findUpdateModule( key_warehouseUpdate );
//If remotely okay, let User win.
if( warehouseModule && computeRelativeCost( queryObject, dock, NULL ) != FLT_MAX )
return dock;
}
// Please note, there is not a separate Warehouse and Center memory by Design. Because
// we lack a UI way to click Warehouse and drag to center to set up a specific path, the
// practical realization has been made that you do not want separate memory.
// Design wants a harvester to give up and return to base if it is "too far" to the warehouse.
// Note, the "PreferedDock" will override this, and there is no distance max on Centers.
maxDistanceSquared = supplyTruckAI->getWarehouseScanDistance() * supplyTruckAI->getWarehouseScanDistance();
}
//Otherwise, search for a good one.
Real bestCost = FLT_MAX;
objectIDListIterator iterator = m_supplyWarehouses.begin();
while( iterator != m_supplyWarehouses.end() )
{
ObjectID currentID = *iterator;
Object *currentWarehouse =TheGameLogic->findObjectByID(currentID);
if( currentWarehouse == NULL )
{
iterator = m_supplyWarehouses.erase( iterator );
}
else
{
Real distanceSquared;
Real currentCost = computeRelativeCost( queryObject, currentWarehouse, &distanceSquared );
if( (currentCost < bestCost) && (distanceSquared < maxDistanceSquared) )
{
bestWarehouse = currentWarehouse;
bestCost = currentCost;
}
iterator++;
}
}
return bestWarehouse;
}
Object *ResourceGatheringManager::findBestSupplyCenter( Object *queryObject )
{
Object *bestCenter = NULL;
if( ( queryObject == NULL ) || ( queryObject->getAI() == NULL ) )
return NULL;
SupplyTruckAIInterface *supplyTruckAI = queryObject->getAI()->getSupplyTruckAIInterface();
if( supplyTruckAI )
{
// Check for a dock override being set.
ObjectID dockID = supplyTruckAI->getPreferredDockID();
Object *dock = TheGameLogic->findObjectByID(dockID);
if( dock )
{
static const NameKeyType key_centerUpdate = NAMEKEY("SupplyCenterDockUpdate");
SupplyWarehouseDockUpdate *centerModule = (SupplyWarehouseDockUpdate*)dock->findUpdateModule( key_centerUpdate );
//If remotely okay, let User win.
if( centerModule && computeRelativeCost( queryObject, dock, NULL ) != FLT_MAX )
return dock;
}
// Please note, there is not a separate Warehouse and Center memory by Design. Because
// we lack a UI way to click Warehouse and drag to center to set up a specific path, the
// practical realization has been made that you do not want separate memory.
}
//Otherwise, search for a good one.
Real bestCost = FLT_MAX;
objectIDListIterator iterator = m_supplyCenters.begin();
while( iterator != m_supplyCenters.end() )
{
ObjectID currentID = *iterator;
Object *currentCenter =TheGameLogic->findObjectByID(currentID);
if( currentCenter == NULL )
{
iterator = m_supplyWarehouses.erase( iterator );
}
else
{
Real currentCost = computeRelativeCost( queryObject, currentCenter, NULL );
if( currentCost < bestCost )
{
bestCenter = currentCenter;
bestCost = currentCost;
}
iterator++;
}
}
return bestCenter;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void ResourceGatheringManager::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void ResourceGatheringManager::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// supply warehouses
xfer->xferSTLObjectIDList( &m_supplyWarehouses );
// supply centers
xfer->xferSTLObjectIDList( &m_supplyCenters );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void ResourceGatheringManager::loadPostProcess( void )
{
} // end loadPostProcess

View file

@ -0,0 +1,383 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: Science.cpp /////////////////////////////////////////////////////////
// Created: Steven Johnson, October 2001
// Desc: @todo
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/INI.h"
#include "Common/Player.h"
#include "Common/Science.h"
ScienceStore* TheScienceStore = NULL;
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-----------------------------------------------------------------------------
void ScienceStore::init()
{
DEBUG_ASSERTCRASH(m_sciences.empty(), ("Hmm"));
m_sciences.clear();
}
//-----------------------------------------------------------------------------
ScienceStore::~ScienceStore()
{
// nope.
//m_sciences.clear();
// go through all sciences and delete any overrides
for (ScienceInfoVec::iterator it = m_sciences.begin(); it != m_sciences.end(); /*++it*/)
{
ScienceInfo* si = *it;
++it;
if (si) {
si->deleteInstance();
}
}
}
//-----------------------------------------------------------------------------
void ScienceStore::reset()
{
// nope.
//m_sciences.clear();
// go through all sciences and delete any overrides
for (ScienceInfoVec::iterator it = m_sciences.begin(); it != m_sciences.end(); /*++it*/)
{
ScienceInfo* si = *it;
Overridable* temp = si->deleteOverrides();
if (!temp)
{
it = m_sciences.erase(it);
}
else
{
++it;
}
}
}
//-----------------------------------------------------------------------------
ScienceType ScienceStore::getScienceFromInternalName(const AsciiString& name) const
{
if (name.isEmpty())
return SCIENCE_INVALID;
NameKeyType nkt = TheNameKeyGenerator->nameToKey(name);
ScienceType st = (ScienceType)nkt;
return st;
}
//-----------------------------------------------------------------------------
AsciiString ScienceStore::getInternalNameForScience(ScienceType science) const
{
if (science == SCIENCE_INVALID)
return AsciiString::TheEmptyString;
NameKeyType nk = (NameKeyType)(science);
return TheNameKeyGenerator->keyToName(nk);
}
//-----------------------------------------------------------------------------
// return a vector of all the currently-known science names
// NOTE: this is really only for use by WorldBuilder! Please
// do not use it in RTS!
std::vector<AsciiString> ScienceStore::friend_getScienceNames() const
{
std::vector<AsciiString> v;
for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it)
{
const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride();
NameKeyType nk = (NameKeyType)(si->m_science);
v.push_back(TheNameKeyGenerator->keyToName(nk));
}
return v;
}
//-----------------------------------------------------------------------------
void ScienceInfo::addRootSciences(ScienceVec& v) const
{
if (m_prereqSciences.empty())
{
// we're a root. add ourselves.
if (std::find(v.begin(), v.end(), m_science) == v.end())
v.push_back(m_science);
}
else
{
// we're not a root. add the roots of all our prereqs.
for (ScienceVec::const_iterator it = m_prereqSciences.begin(); it != m_prereqSciences.end(); ++it)
{
const ScienceInfo* si = TheScienceStore->findScienceInfo(*it);
if (si)
si->addRootSciences(v);
}
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
const ScienceInfo* ScienceStore::findScienceInfo(ScienceType st) const
{
for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it)
{
const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride();
if (si->m_science == st)
{
return si;
}
}
return NULL;
}
//-----------------------------------------------------------------------------
/*static*/ void ScienceStore::friend_parseScienceDefinition( INI* ini )
{
const char* c = ini->getNextToken();
NameKeyType nkt = NAMEKEY(c);
ScienceType st = (ScienceType)nkt;
if (TheScienceStore)
{
static const FieldParse myFieldParse[] =
{
{ "PrerequisiteSciences", INI::parseScienceVector, NULL, offsetof( ScienceInfo, m_prereqSciences ) },
{ "SciencePurchasePointCost", INI::parseInt, NULL, offsetof( ScienceInfo, m_sciencePurchasePointCost ) },
{ "IsGrantable", INI::parseBool, NULL, offsetof( ScienceInfo, m_grantable ) },
{ "DisplayName", INI::parseAndTranslateLabel, NULL, offsetof( ScienceInfo, m_name) },
{ "Description", INI::parseAndTranslateLabel, NULL, offsetof( ScienceInfo, m_description) },
{ 0, 0, 0, 0 }
};
ScienceInfo* info = NULL;
// see if the science already exists. (can't use findScienceInfo() since it is const and should remain so.)
for (ScienceInfoVec::iterator it = TheScienceStore->m_sciences.begin(); it != TheScienceStore->m_sciences.end(); ++it)
{
// note that we don't use getFinalOverride here. this is correct and as-desired.
if ((*it)->m_science == st)
{
info = *it;
break;
}
}
if (ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES)
{
ScienceInfo* newInfo = newInstance(ScienceInfo);
if (info == NULL)
{
// only add if it's not overriding an existing one.
info = newInfo;
info->markAsOverride(); // yep, so we will get cleared on reset()
TheScienceStore->m_sciences.push_back(info);
}
else
{
// copy data from final override to 'newInfo' as a set of initial default values
info = (ScienceInfo*)(info->friend_getFinalOverride());
*newInfo = *info;
info->setNextOverride(newInfo);
newInfo->markAsOverride(); // must do AFTER the copy
// use the newly created override for us to set values with etc
info = newInfo;
//TheScienceStore->m_sciences.push_back(info); // NO, BAD, WRONG -- don't add in this case.
}
}
else
{
if (info != NULL)
{
DEBUG_CRASH(("duplicate science %s!\n",c));
throw INI_INVALID_DATA;
}
info = newInstance(ScienceInfo);
TheScienceStore->m_sciences.push_back(info);
}
ini->initFromINI(info, myFieldParse);
info->m_science = st;
info->addRootSciences(info->m_rootSciences);
}
}
//-----------------------------------------------------------------------------
Int ScienceStore::getSciencePurchaseCost(ScienceType st) const
{
const ScienceInfo* si = findScienceInfo(st);
if (si)
{
return si->m_sciencePurchasePointCost;
}
else
{
return 0;
}
}
//-----------------------------------------------------------------------------
Bool ScienceStore::isScienceGrantable(ScienceType st) const
{
const ScienceInfo* si = findScienceInfo(st);
if (si)
{
return si->m_grantable;
}
else
{
return false;
}
}
//-----------------------------------------------------------------------------
Bool ScienceStore::getNameAndDescription(ScienceType st, UnicodeString& name, UnicodeString& description) const
{
const ScienceInfo* si = findScienceInfo(st);
if (si)
{
name = si->m_name;
description = si->m_description;
return true;
}
else
{
return false;
}
}
//-----------------------------------------------------------------------------
Bool ScienceStore::playerHasPrereqsForScience(const Player* player, ScienceType st) const
{
const ScienceInfo* si = findScienceInfo(st);
if (si)
{
for (ScienceVec::const_iterator it2 = si->m_prereqSciences.begin(); it2 != si->m_prereqSciences.end(); ++it2)
{
if (!player->hasScience(*it2))
{
return false;
}
}
return true;
}
else
{
return false;
}
}
//-----------------------------------------------------------------------------
Bool ScienceStore::playerHasRootPrereqsForScience(const Player* player, ScienceType st) const
{
const ScienceInfo* si = findScienceInfo(st);
if (si)
{
for (ScienceVec::const_iterator it2 = si->m_rootSciences.begin(); it2 != si->m_rootSciences.end(); ++it2)
{
if (!player->hasScience(*it2))
{
return false;
}
}
return true;
}
else
{
return false;
}
}
//-----------------------------------------------------------------------------
/** return a list of the sciences the given player can purchase now, and a list he might be able to purchase in the future,
but currently lacks prereqs or points for. (either might be an empty list) */
void ScienceStore::getPurchasableSciences(const Player* player, ScienceVec& purchasable, ScienceVec& potentiallyPurchasable) const
{
purchasable.clear();
potentiallyPurchasable.clear();
for (ScienceInfoVec::const_iterator it = m_sciences.begin(); it != m_sciences.end(); ++it)
{
const ScienceInfo* si = (const ScienceInfo*)(*it)->getFinalOverride();
if (si->m_sciencePurchasePointCost == 0)
{
// 0 means "cannot be purchased"
continue;
}
if (player->hasScience(si->m_science))
{
continue;
}
if (playerHasPrereqsForScience(player, si->m_science))
{
purchasable.push_back(si->m_science);
}
else if (playerHasRootPrereqsForScience(player, si->m_science))
{
potentiallyPurchasable.push_back(si->m_science);
}
}
}
//-----------------------------------------------------------------------------
// this is intended ONLY for use by INI::scanScience.
// Don't use it anywhere else. In particular, never, ever, ever
// call this with a hardcoded science name. (srj)
ScienceType ScienceStore::friend_lookupScience(const char* scienceName) const
{
NameKeyType nkt = NAMEKEY(scienceName);
ScienceType st = (ScienceType)nkt;
if (!isValidScience(st))
{
DEBUG_CRASH(("Science name %s not known! (Did you define it in Science.ini?)",scienceName));
throw INI_INVALID_DATA;
}
return st;
}
//-----------------------------------------------------------------------------
Bool ScienceStore::isValidScience(ScienceType st) const
{
const ScienceInfo* si = findScienceInfo(st);
return si != NULL;
}
//-----------------------------------------------------------------------------
void INI::parseScienceDefinition( INI* ini )
{
ScienceStore::friend_parseScienceDefinition(ini);
}

View file

@ -0,0 +1,568 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: ScoreKeeper.cpp /////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Electronic Arts Pacific.
//
// Confidential Information
// Copyright (C) 2002 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// created: Jun 2002
//
// Filename: ScoreKeeper.cpp
//
// author: Chris Huybregts
//
// purpose: Score Keeper class will be an object attached to each player
// that will maintain accurate counts for the various stats we
// want to show on the score screen. The information in here
// could also be used for the observer screen
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// USER INCLUDES //////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/GameState.h"
#include "Common/KindOf.h"
#include "Common/Player.h"
#include "Common/ScoreKeeper.h"
#include "Common/ThingFactory.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameLogic/Object.h"
#include "GameLogic/GameLogic.h"
//-----------------------------------------------------------------------------
// DEFINES ////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
ScoreKeeper::ScoreKeeper( void )
{
reset(0);
}
ScoreKeeper::~ScoreKeeper( void )
{
}
static KindOfMaskType scoringBuildingMask;
static KindOfMaskType scoringBuildingDestroyMask;
static KindOfMaskType scoringBuildingCreateMask;
void ScoreKeeper::reset( Int playerIdx )
{
scoringBuildingMask.set(KINDOF_STRUCTURE);
scoringBuildingMask.set(KINDOF_SCORE);
scoringBuildingCreateMask.set(KINDOF_STRUCTURE);
scoringBuildingCreateMask.set(KINDOF_SCORE_CREATE);
scoringBuildingDestroyMask.set(KINDOF_STRUCTURE);
scoringBuildingDestroyMask.set(KINDOF_SCORE_DESTROY);
m_totalMoneyEarned = m_totalMoneySpent = 0;
m_totalUnitsLost = m_totalUnitsBuilt = 0;
m_totalBuildingsLost = m_totalBuildingsBuilt = 0;
//Added By Sadullah Nader
//Initializtion(s) inserted
m_totalFactionBuildingsCaptured = m_totalTechBuildingsCaptured = 0;
//
m_currentScore = 0;
m_objectsBuilt.clear();
m_objectsCaptured.clear();
m_objectsLost.clear();
for(int i = 0; i < MAX_PLAYER_COUNT; ++i)
{
m_objectsDestroyed[i].clear();
m_totalBuildingsDestroyed[i] = m_totalUnitsDestroyed[i] = 0;
}
m_myPlayerIdx = playerIdx;
}
void ScoreKeeper::addObjectBuilt( const Object *o)
{
Bool addToCount = FALSE;
if (TheGameLogic->isScoringEnabled() == FALSE) {
return;
}
if(o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
{
++m_totalBuildingsBuilt;
addToCount = TRUE;
}
else if (o->getTemplate()->isKindOfMulti(scoringBuildingCreateMask, KINDOFMASK_NONE))
{
++m_totalBuildingsBuilt;
addToCount = TRUE;
}
else if(o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
{
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_CREATE))
{
++m_totalUnitsBuilt;
addToCount = TRUE;
}
}
if(addToCount)
{
Int existingCount = 0;
ObjectCountMapIt it = m_objectsBuilt.find(o->getTemplate());
if (it != m_objectsBuilt.end())
existingCount = it->second;
m_objectsBuilt[o->getTemplate()] = existingCount + 1;
}
}
Int ScoreKeeper::getTotalUnitsBuilt( KindOfMaskType validMask, KindOfMaskType invalidMask )
{
Int count = 0;
for (ObjectCountMapIt it = m_objectsBuilt.begin(); it != m_objectsBuilt.end(); ++it)
{
const ThingTemplate *theTemplate = it->first;
Int numBuilt = it->second;
if (theTemplate && theTemplate->isKindOfMulti(validMask, invalidMask))
count += numBuilt;
}
return count;
}
Int ScoreKeeper::getTotalObjectsBuilt( const ThingTemplate *pTemplate )
{
Int count = 0;
for (ObjectCountMapIt it = m_objectsBuilt.begin(); it != m_objectsBuilt.end(); ++it)
{
const ThingTemplate *theTemplate = it->first;
if (theTemplate->isEquivalentTo(pTemplate))
++count;
}
return count;
}
void ScoreKeeper::removeObjectBuilt( const Object *o)
{
if (TheGameLogic->isScoringEnabled() == FALSE) {
return;
}
Bool removeFromCount = FALSE;
if (o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
{
--m_totalBuildingsBuilt;
removeFromCount = TRUE;
}
else if (o->getTemplate()->isKindOfMulti(scoringBuildingCreateMask, KINDOFMASK_NONE))
{
--m_totalBuildingsBuilt;
removeFromCount = TRUE;
}
else if (o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
{
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_CREATE))
{
--m_totalUnitsBuilt;
removeFromCount = TRUE;
}
}
if (removeFromCount)
{
Int existingCount = 0;
ObjectCountMapIt it = m_objectsBuilt.find(o->getTemplate());
if (it != m_objectsBuilt.end())
existingCount = it->second;
m_objectsBuilt[o->getTemplate()] = existingCount - 1;
}
}
void ScoreKeeper::addObjectCaptured( const Object *o )
{
if (TheGameLogic->isScoringEnabled() == FALSE) {
return;
}
Bool addToCount = FALSE;
if(o->getTemplate()->isKindOf(KINDOF_STRUCTURE))
{
if (o->getTemplate()->isKindOf(KINDOF_SCORE))
{
++m_totalFactionBuildingsCaptured;
}
else
{
++m_totalTechBuildingsCaptured;
}
addToCount = TRUE;
}
if(addToCount)
{
Int existingCount = 0;
ObjectCountMapIt it = m_objectsCaptured.find(o->getTemplate());
if (it != m_objectsCaptured.end())
existingCount = it->second;
m_objectsCaptured[o->getTemplate()] = existingCount + 1;
}
}
void ScoreKeeper::addObjectDestroyed( const Object *o)
{
if (TheGameLogic->isScoringEnabled() == FALSE) {
return;
}
Int playerIdx = o->getControllingPlayer()->getPlayerIndex();
Bool addToCount = FALSE;
if(o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
++m_totalBuildingsDestroyed[playerIdx];
addToCount = TRUE;
}
}
else if (o->getTemplate()->isKindOfMulti(scoringBuildingDestroyMask, KINDOFMASK_NONE))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
++m_totalBuildingsDestroyed[playerIdx];
addToCount = TRUE;
}
}
else if(o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
{
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_DESTROY))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
m_totalUnitsDestroyed[playerIdx]++;
addToCount = TRUE;
}
}
}
if(addToCount)
{
Int existingCount = 0;
ObjectCountMapIt it = m_objectsDestroyed[playerIdx].find(o->getTemplate());
if (it != m_objectsDestroyed[playerIdx].end())
existingCount = it->second;
m_objectsDestroyed[playerIdx][o->getTemplate()] = existingCount + 1;
}
}
void ScoreKeeper::addObjectLost( const Object *o )
{
if (TheGameLogic->isScoringEnabled() == FALSE) {
return;
}
Bool addToCount = FALSE;
if(o->getTemplate()->isKindOfMulti(scoringBuildingMask, KINDOFMASK_NONE))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
++m_totalBuildingsLost;
addToCount = TRUE;
}
}
else if (o->getTemplate()->isKindOfMulti(scoringBuildingDestroyMask, KINDOFMASK_NONE))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
++m_totalBuildingsLost;
addToCount = TRUE;
}
}
else if(o->getTemplate()->isKindOf(KINDOF_INFANTRY) || o->getTemplate()->isKindOf(KINDOF_VEHICLE))
{
if (o->getTemplate()->isKindOf(KINDOF_SCORE) || o->getTemplate()->isKindOf(KINDOF_SCORE_DESTROY))
{
if (!(o->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))) {
++m_totalUnitsLost;
addToCount = TRUE;
}
}
}
if(addToCount)
{
Int existingCount = 0;
ObjectCountMapIt it = m_objectsLost.find(o->getTemplate());
if (it != m_objectsLost.end())
existingCount = it->second;
m_objectsLost[o->getTemplate()] = existingCount + 1;
}
}
Int ScoreKeeper::calculateScore( void )
{
Int score = 0;
score += m_totalUnitsBuilt * 100;
score += m_totalMoneyEarned;
score += m_totalBuildingsBuilt * 100;
for (Int i = 0; i < MAX_PLAYER_COUNT; ++i)
{
if(i == m_myPlayerIdx)
continue;
score += m_totalUnitsDestroyed[i] * 100;
score += m_totalBuildingsDestroyed[i] * 100;
}
m_currentScore = score;
return m_currentScore;
}
//-----------------------------------------------------------------------------
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
Int ScoreKeeper::getTotalBuildingsDestroyed( void )
{
int count = 0;
for (int i = 0; i< MAX_PLAYER_COUNT; ++i)
{
// Design change, display even if we killed our own
// if(i == m_myPlayerIdx)
// continue;
count += m_totalBuildingsDestroyed[i];
//for (ObjectCountMapIt it = m_objectsDestroyed[i].begin(); it != m_objectsDestroyed[i].end(); ++it)
// {
//
// count += it->second;
// }
}
return count;
}
Int ScoreKeeper::getTotalUnitsDestroyed( void )
{
int count = 0;
for (int i = 0; i< MAX_PLAYER_COUNT; ++i)
{
// Design change, display even if we killed our own
// if(i == m_myPlayerIdx)
// continue;
count += m_totalUnitsDestroyed[i];
// for (ObjectCountMapIt it = m_objectsDestroyed[i].begin(); it != m_objectsDestroyed[i].end(); ++it)
// {
// }
}
return count;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void ScoreKeeper::crc( Xfer *xfer )
{
} // end ScoreKeeper
// ------------------------------------------------------------------------------------------------
/** Xfer of an object count map
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void ScoreKeeper::xferObjectCountMap( Xfer *xfer, ObjectCountMap *map )
{
// sanity
if( map == NULL )
{
DEBUG_CRASH(( "xferObjectCountMap - Invalid map parameter\n" ));
throw SC_INVALID_DATA;
} // end if
// version info
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// size of the map
UnsignedShort mapSize = map->size();
xfer->xferUnsignedShort( &mapSize );
// map data
Int count;
const ThingTemplate *thingTemplate;
AsciiString thingTemplateName;
if( xfer->getXferMode() == XFER_SAVE )
{
ObjectCountMapIt it;
// save all entries
for( it = map->begin(); it != map->end(); ++it )
{
// thing template
thingTemplate = it->first;
thingTemplateName = thingTemplate->getName();
xfer->xferAsciiString( &thingTemplateName );
// the count
count = it->second;
xfer->xferInt( &count );
} // end for, it
} // end if, save
else
{
// read all entries
for( UnsignedShort i = 0; i < mapSize; ++i )
{
// read thing template name
xfer->xferAsciiString( &thingTemplateName );
thingTemplate = TheThingFactory->findTemplate( thingTemplateName );
if( thingTemplate == NULL )
{
DEBUG_CRASH(( "xferObjectCountMap - Unknown thing template '%s'\n", thingTemplateName.str() ));
throw SC_INVALID_DATA;
} // end if
// read count
xfer->xferInt( &count );
// add to map
(*map)[ thingTemplate ] = count;
} // end for, i
} // end else
} // end xferObjectCountMap
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void ScoreKeeper::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// money earned
xfer->xferInt( &m_totalMoneyEarned );
// money spent
xfer->xferInt( &m_totalMoneySpent );
// units destroyed
xfer->xferUser( m_totalUnitsDestroyed, sizeof( Int ) * MAX_PLAYER_COUNT );
// units built
xfer->xferInt( &m_totalUnitsBuilt );
// units lost
xfer->xferInt( &m_totalUnitsLost );
// buildings destroyed
xfer->xferUser( m_totalBuildingsDestroyed, sizeof( Int ) * MAX_PLAYER_COUNT );
// buildings built
xfer->xferInt( &m_totalBuildingsBuilt );
// buildings lost
xfer->xferInt( &m_totalBuildingsLost );
// tech buildings captured
xfer->xferInt( &m_totalTechBuildingsCaptured );
// faction buildings captured
xfer->xferInt( &m_totalFactionBuildingsCaptured );
// current score
xfer->xferInt( &m_currentScore );
// player index
xfer->xferInt( &m_myPlayerIdx );
// objects built
xferObjectCountMap( xfer, &m_objectsBuilt );
// objects destroyed
UnsignedShort destroyedArraySize = MAX_PLAYER_COUNT;
xfer->xferUnsignedShort( &destroyedArraySize );
if( destroyedArraySize != MAX_PLAYER_COUNT )
{
DEBUG_CRASH(( "ScoreKeeper::xfer - size of objects destroyed array has changed\n" ));
throw SC_INVALID_DATA;
} // end if
for( UnsignedShort i = 0; i < destroyedArraySize; ++i )
{
// xfer map data
xferObjectCountMap( xfer, &m_objectsDestroyed[ i ] );
} // end for i
// objects lost
xferObjectCountMap( xfer, &m_objectsLost );
// objects captured
xferObjectCountMap( xfer, &m_objectsCaptured );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void ScoreKeeper::loadPostProcess( void )
{
} // end loadPostProcess

View file

@ -0,0 +1,381 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: SpecialPower.cpp /////////////////////////////////////////////////////////////////////////
// Author: Colin Day, April 2002
// Desc: Special power templates and the system that holds them
// Edited: Kris Morness -- July 2002 (added BitFlag system)
///////////////////////////////////////////////////////////////////////////////////////////////////
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/Player.h"
#include "Common/Science.h"
#include "Common/SpecialPower.h"
#include "GameLogic/Object.h"
#include "Common/BitFlagsIO.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// GLOBAL /////////////////////////////////////////////////////////////////////////////////////////
SpecialPowerStore *TheSpecialPowerStore = NULL;
#define DEFAULT_DEFECTION_DETECTION_PROTECTION_TIME_LIMIT (LOGICFRAMES_PER_SECOND * 10)
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// Externs ////////////////////////////////////////////////////////////////////////////////////////
const char* SpecialPowerMaskType::s_bitNameList[] =
{
"SPECIAL_INVALID",
//Superweapons
"SPECIAL_DAISY_CUTTER",
"SPECIAL_PARADROP_AMERICA",
"SPECIAL_CARPET_BOMB",
"SPECIAL_CLUSTER_MINES",
"SPECIAL_EMP_PULSE",
"SPECIAL_NAPALM_STRIKE",
"SPECIAL_CASH_HACK",
"SPECIAL_NEUTRON_MISSILE",
"SPECIAL_SPY_SATELLITE",
"SPECIAL_DEFECTOR",
"SPECIAL_TERROR_CELL",
"SPECIAL_AMBUSH",
"SPECIAL_BLACK_MARKET_NUKE",
"SPECIAL_ANTHRAX_BOMB",
"SPECIAL_SCUD_STORM",
#ifdef ALLOW_DEMORALIZE
"SPECIAL_DEMORALIZE",
#else
"SPECIAL_DEMORALIZE_OBSOLETE",
#endif
"SPECIAL_CRATE_DROP",
"SPECIAL_A10_THUNDERBOLT_STRIKE",
"SPECIAL_DETONATE_DIRTY_NUKE",
"SPECIAL_ARTILLERY_BARRAGE",
//Special abilities
"SPECIAL_MISSILE_DEFENDER_LASER_GUIDED_MISSILES",
"SPECIAL_REMOTE_CHARGES",
"SPECIAL_TIMED_CHARGES",
"SPECIAL_HELIX_NAPALM_BOMB",
"SPECIAL_HACKER_DISABLE_BUILDING",
"SPECIAL_TANKHUNTER_TNT_ATTACK",
"SPECIAL_BLACKLOTUS_CAPTURE_BUILDING",
"SPECIAL_BLACKLOTUS_DISABLE_VEHICLE_HACK",
"SPECIAL_BLACKLOTUS_STEAL_CASH_HACK",
"SPECIAL_INFANTRY_CAPTURE_BUILDING",
"SPECIAL_RADAR_VAN_SCAN",
"SPECIAL_SPY_DRONE",
"SPECIAL_DISGUISE_AS_VEHICLE",
"SPECIAL_BOOBY_TRAP",
"SPECIAL_REPAIR_VEHICLES",
"SPECIAL_PARTICLE_UPLINK_CANNON",
"SPECIAL_CASH_BOUNTY",
"SPECIAL_CHANGE_BATTLE_PLANS",
"SPECIAL_CIA_INTELLIGENCE",
"SPECIAL_CLEANUP_AREA",
"SPECIAL_LAUNCH_BAIKONUR_ROCKET",
"SPECIAL_SPECTRE_GUNSHIP",
"SPECIAL_GPS_SCRAMBLER",
"SPECIAL_FRENZY",
"SPECIAL_SNEAK_ATTACK",
"SPECIAL_CHINA_CARPET_BOMB",
"EARLY_SPECIAL_CHINA_CARPET_BOMB",
"SPECIAL_LEAFLET_DROP",
"EARLY_SPECIAL_LEAFLET_DROP",
"EARLY_SPECIAL_FRENZY",
"SPECIAL_COMMUNICATIONS_DOWNLOAD",
"EARLY_SPECIAL_REPAIR_VEHICLES",
"SPECIAL_TANK_PARADROP",
"SUPW_SPECIAL_PARTICLE_UPLINK_CANNON",
"AIRF_SPECIAL_DAISY_CUTTER",
"NUKE_SPECIAL_CLUSTER_MINES",
"NUKE_SPECIAL_NEUTRON_MISSILE",
"AIRF_SPECIAL_A10_THUNDERBOLT_STRIKE",
"AIRF_SPECIAL_SPECTRE_GUNSHIP",
"INFA_SPECIAL_PARADROP_AMERICA",
"SLTH_SPECIAL_GPS_SCRAMBLER",
"AIRF_SPECIAL_CARPET_BOMB",
"SUPR_SPECIAL_CRUISE_MISSILE",
"LAZR_SPECIAL_PARTICLE_UPLINK_CANNON",
"SUPW_SPECIAL_NEUTRON_MISSILE",
"SPECIAL_BATTLESHIP_BOMBARDMENT",
NULL
};
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void SpecialPowerStore::parseSpecialPowerDefinition( INI *ini )
{
// read the name
AsciiString name = ini->getNextToken();
SpecialPowerTemplate* specialPower = TheSpecialPowerStore->findSpecialPowerTemplatePrivate( name );
if ( ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES )
{
if (specialPower)
{
SpecialPowerTemplate* child = (SpecialPowerTemplate*)specialPower->friend_getFinalOverride();
specialPower = newInstance(SpecialPowerTemplate);
*specialPower = *child;
child->setNextOverride(specialPower);
specialPower->markAsOverride();
//TheSpecialPowerStore->m_specialPowerTemplates.push_back(specialPower); // nope, do NOT do this
}
else
{
specialPower = newInstance(SpecialPowerTemplate);
const SpecialPowerTemplate *defaultTemplate = TheSpecialPowerStore->findSpecialPowerTemplate( "DefaultSpecialPower" );
if( defaultTemplate )
*specialPower = *defaultTemplate;
specialPower->friend_setNameAndID(name, ++TheSpecialPowerStore->m_nextSpecialPowerID);
specialPower->markAsOverride();
TheSpecialPowerStore->m_specialPowerTemplates.push_back(specialPower);
}
}
else
{
if (specialPower)
{
throw INI_INVALID_DATA;
}
else
{
specialPower = newInstance(SpecialPowerTemplate);
const SpecialPowerTemplate *defaultTemplate = TheSpecialPowerStore->findSpecialPowerTemplate( "DefaultSpecialPower" );
if( defaultTemplate )
*specialPower = *defaultTemplate;
specialPower->friend_setNameAndID(name, ++TheSpecialPowerStore->m_nextSpecialPowerID);
TheSpecialPowerStore->m_specialPowerTemplates.push_back(specialPower);
}
}
// parse the ini definition
if (specialPower)
ini->initFromINI( specialPower, specialPower->getFieldParse() );
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/* static */ const FieldParse SpecialPowerTemplate::m_specialPowerFieldParse[] =
{
{ "ReloadTime", INI::parseDurationUnsignedInt, NULL, offsetof( SpecialPowerTemplate, m_reloadTime ) },
{ "RequiredScience", INI::parseScience, NULL, offsetof( SpecialPowerTemplate, m_requiredScience ) },
{ "InitiateSound", INI::parseAudioEventRTS, NULL, offsetof( SpecialPowerTemplate, m_initiateSound ) },
{ "InitiateAtLocationSound", INI::parseAudioEventRTS, NULL, offsetof( SpecialPowerTemplate, m_initiateAtLocationSound ) },
{ "PublicTimer", INI::parseBool, NULL, offsetof( SpecialPowerTemplate, m_publicTimer ) },
{ "Enum", INI::parseIndexList, SpecialPowerMaskType::getBitNames(), offsetof( SpecialPowerTemplate, m_type ) },
{ "DetectionTime", INI::parseDurationUnsignedInt, NULL, offsetof( SpecialPowerTemplate, m_detectionTime ) },
{ "SharedSyncedTimer", INI::parseBool, NULL, offsetof( SpecialPowerTemplate, m_sharedNSync ) },
{ "ViewObjectDuration", INI::parseDurationUnsignedInt, NULL, offsetof( SpecialPowerTemplate, m_viewObjectDuration ) },
{ "ViewObjectRange", INI::parseReal, NULL, offsetof( SpecialPowerTemplate, m_viewObjectRange ) },
{ "RadiusCursorRadius", INI::parseReal, NULL, offsetof( SpecialPowerTemplate, m_radiusCursorRadius ) },
{ "ShortcutPower", INI::parseBool, NULL, offsetof( SpecialPowerTemplate, m_shortcutPower ) },
{ "AcademyClassify", INI::parseIndexList, TheAcademyClassificationTypeNames, offsetof( SpecialPowerTemplate, m_academyClassificationType ) },
{ NULL, NULL, NULL, 0 } // keep this last
};
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
SpecialPowerTemplate::SpecialPowerTemplate()
{
m_id = 0;
m_type = SPECIAL_INVALID;
m_reloadTime = 0;
m_requiredScience = SCIENCE_INVALID;
m_publicTimer = FALSE;
m_detectionTime = DEFAULT_DEFECTION_DETECTION_PROTECTION_TIME_LIMIT;
m_sharedNSync = FALSE;
m_viewObjectDuration = 0;
m_viewObjectRange = 0;
m_radiusCursorRadius = 0;
m_shortcutPower = FALSE;
} // end SpecialPowerTemplate
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
SpecialPowerTemplate::~SpecialPowerTemplate()
{
} // end ~SpecialPowerTemplate
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
SpecialPowerStore::SpecialPowerStore( void )
{
m_nextSpecialPowerID = 0;
} // end SpecialPowerStore
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
SpecialPowerStore::~SpecialPowerStore( void )
{
// delete all templates
for( Int i = 0; i < m_specialPowerTemplates.size(); ++i )
m_specialPowerTemplates[ i ]->deleteInstance();
// erase the list
m_specialPowerTemplates.clear();
// set our count to zero
m_nextSpecialPowerID = 0;
} // end ~SpecialPowerStore
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
SpecialPowerTemplate* SpecialPowerStore::findSpecialPowerTemplatePrivate( AsciiString name )
{
// search the template list for matching name
for( Int i = 0; i < m_specialPowerTemplates.size(); ++i )
if( m_specialPowerTemplates[ i ]->getName() == name )
return m_specialPowerTemplates[ i ];
return NULL; // not found
}
//-------------------------------------------------------------------------------------------------
/** Find a special power template given unique ID */
//-------------------------------------------------------------------------------------------------
const SpecialPowerTemplate *SpecialPowerStore::findSpecialPowerTemplateByID( UnsignedInt id )
{
// search the template list for matching name
for( Int i = 0; i < m_specialPowerTemplates.size(); ++i )
if( m_specialPowerTemplates[ i ]->getID() == id )
return m_specialPowerTemplates[ i ];
return NULL; // not found
}
//-------------------------------------------------------------------------------------------------
/** Find a special power template given index (WB) */
//-------------------------------------------------------------------------------------------------
const SpecialPowerTemplate *SpecialPowerStore::getSpecialPowerTemplateByIndex( UnsignedInt index )
{
if (index >= 0 && index < m_specialPowerTemplates.size())
return m_specialPowerTemplates[ index ];
return NULL; // not found
} // end getSpecialPowerTemplateByIndex
//-------------------------------------------------------------------------------------------------
/** Return the size of the store (WB) */
//-------------------------------------------------------------------------------------------------
Int SpecialPowerStore::getNumSpecialPowers( void )
{
return m_specialPowerTemplates.size();
} // end getNumSpecialPowers
//-------------------------------------------------------------------------------------------------
/** does the object (and therefore the player) meet all the requirements to use this power */
//-------------------------------------------------------------------------------------------------
Bool SpecialPowerStore::canUseSpecialPower( Object *obj, const SpecialPowerTemplate *specialPowerTemplate )
{
// sanity
if( obj == NULL || specialPowerTemplate == NULL )
return FALSE;
// as a first sanity check, the object must have a module capable of executing the power
if( obj->getSpecialPowerModule( specialPowerTemplate ) == NULL )
return FALSE;
//
// in order to execute the special powers we have attached special power modules to the objects
// that can use them. However, just because an object has a module that is capable of
// doing the power, does not mean the object and the player can actually execute the
// power because some powers require a specialized science that the player must select and
// they cannot have all of them.
//
// check for requried science
ScienceType requiredScience = specialPowerTemplate->getRequiredScience();
if( requiredScience != SCIENCE_INVALID )
{
Player *player = obj->getControllingPlayer();
if( player->hasScience( requiredScience ) == FALSE )
return FALSE;
} // end if
// I THINK THIS IS WHERE WE BAIL OUT IF A DIFFERENT CONYARD IS ALREADY CHARGIN THIS SPECIAL RIGHT NOW //LORENZEN
// all is well
return TRUE;
} // end canUseSpecialPower
//-------------------------------------------------------------------------------------------------
/** Reset */
//-------------------------------------------------------------------------------------------------
void SpecialPowerStore::reset( void )
{
for (SpecialPowerTemplatePtrVector::iterator it = m_specialPowerTemplates.begin(); it != m_specialPowerTemplates.end(); /*++it*/)
{
SpecialPowerTemplate* si = *it;
Overridable* temp = si->deleteOverrides();
if (temp == NULL)
{
it = m_specialPowerTemplates.erase(it);
}
else
{
++it;
}
}
} // end reset

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,412 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: TunnelTracker.cpp ///////////////////////////////////////////////////////////
// The part of a Player's brain that holds the communal Passenger list of all tunnels.
// Author: Graham Smallwood, March, 2002
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/GameState.h"
#include "Common/GlobalData.h"
#include "Common/KindOf.h"
#include "Common/TunnelTracker.h"
#include "Common/Xfer.h"
#include "GameClient/ControlBar.h"
#include "GameClient/Drawable.h"
#include "GameLogic/AI.h"
#include "GameLogic/AIPathfind.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#include "GameLogic/PartitionManager.h"
#include "GameLogic/Module/BodyModule.h"
#include "GameLogic/Module/TunnelContain.h"
// ------------------------------------------------------------------------
TunnelTracker::TunnelTracker()
{
m_tunnelCount = 0;
m_containListSize = 0;
m_curNemesisID = INVALID_ID;
m_nemesisTimestamp = 0;
}
// ------------------------------------------------------------------------
TunnelTracker::~TunnelTracker()
{
m_tunnelIDs.clear();
}
// ------------------------------------------------------------------------
void TunnelTracker::iterateContained( ContainIterateFunc func, void *userData, Bool reverse )
{
if (reverse)
{
// note that this has to be smart enough to handle items in the list being deleted
// via the callback function.
for(ContainedItemsList::reverse_iterator it = m_containList.rbegin(); it != m_containList.rend(); )
{
// save the obj...
Object* obj = *it;
// incr the iterator BEFORE calling the func (if the func removes the obj,
// the iterator becomes invalid)
++it;
// call it
(*func)( obj, userData );
}
}
else
{
// note that this has to be smart enough to handle items in the list being deleted
// via the callback function.
for(ContainedItemsList::iterator it = m_containList.begin(); it != m_containList.end(); )
{
// save the obj...
Object* obj = *it;
// incr the iterator BEFORE calling the func (if the func removes the obj,
// the iterator becomes invalid)
++it;
// call it
(*func)( obj, userData );
}
}
}
// ------------------------------------------------------------------------
Int TunnelTracker::getContainMax() const
{
return TheGlobalData->m_maxTunnelCapacity;
}
// ------------------------------------------------------------------------
void TunnelTracker::updateNemesis(const Object *target)
{
if (getCurNemesis()==NULL) {
if (target) {
if (target->isKindOf(KINDOF_VEHICLE) || target->isKindOf(KINDOF_STRUCTURE) ||
target->isKindOf(KINDOF_INFANTRY) || target->isKindOf(KINDOF_AIRCRAFT)) {
m_curNemesisID = target->getID();
m_nemesisTimestamp = TheGameLogic->getFrame();
}
}
} else if (getCurNemesis()==target) {
m_nemesisTimestamp = TheGameLogic->getFrame();
}
}
// ------------------------------------------------------------------------
Object *TunnelTracker::getCurNemesis(void)
{
if (m_curNemesisID == INVALID_ID) {
return NULL;
}
if (m_nemesisTimestamp + 4*LOGICFRAMES_PER_SECOND < TheGameLogic->getFrame()) {
m_curNemesisID = INVALID_ID;
return NULL;
}
Object *target = TheGameLogic->findObjectByID(m_curNemesisID);
if (target) {
//If the enemy unit is stealthed and not detected, then we can't attack it!
if( target->testStatus( OBJECT_STATUS_STEALTHED ) &&
!target->testStatus( OBJECT_STATUS_DETECTED ) &&
!target->testStatus( OBJECT_STATUS_DISGUISED ) )
{
target = NULL;
}
}
if (target && target->isEffectivelyDead()) {
target = NULL;
}
if (target == NULL) {
m_curNemesisID = INVALID_ID;
}
return target;
}
// ------------------------------------------------------------------------
Bool TunnelTracker::isValidContainerFor(const Object* obj, Bool checkCapacity) const
{
//October 11, 2002 -- Kris : Dustin wants ALL units to be able to use tunnels!
// srj sez: um, except aircraft.
if (obj && !obj->isKindOf(KINDOF_AIRCRAFT))
{
if (checkCapacity)
{
Int containMax = getContainMax();
Int containCount = getContainCount();
return ( containCount < containMax );
}
else
{
return true;
}
}
return false;
}
// ------------------------------------------------------------------------
void TunnelTracker::addToContainList( Object *obj )
{
m_containList.push_back(obj);
++m_containListSize;
}
// ------------------------------------------------------------------------
void TunnelTracker::removeFromContain( Object *obj, Bool exposeStealthUnits )
{
ContainedItemsList::iterator it = std::find(m_containList.begin(), m_containList.end(), obj);
if (it != m_containList.end())
{
// note that this invalidates the iterator!
m_containList.erase(it);
--m_containListSize;
}
}
// ------------------------------------------------------------------------
Bool TunnelTracker::isInContainer( Object *obj )
{
return (std::find(m_containList.begin(), m_containList.end(), obj) != m_containList.end()) ;
}
// ------------------------------------------------------------------------
void TunnelTracker::onTunnelCreated( const Object *newTunnel )
{
m_tunnelCount++;
m_tunnelIDs.push_back( newTunnel->getID() );
}
// ------------------------------------------------------------------------
void TunnelTracker::onTunnelDestroyed( const Object *deadTunnel )
{
m_tunnelCount--;
m_tunnelIDs.remove( deadTunnel->getID() );
if( m_tunnelCount == 0 )
{
// Kill everyone in our contain list. Cave in!
iterateContained( destroyObject, NULL, FALSE );
m_containList.clear();
m_containListSize = 0;
}
else
{
Object *validTunnel = TheGameLogic->findObjectByID( m_tunnelIDs.front() );
// Otherwise, make sure nobody inside remembers the dead tunnel as the one they entered
// (scripts need to use so there must be something valid here)
for(ContainedItemsList::iterator it = m_containList.begin(); it != m_containList.end(); )
{
Object* obj = *it;
++it;
if( obj->getContainedBy() == deadTunnel )
obj->onContainedBy( validTunnel );
}
}
}
// ------------------------------------------------------------------------
void TunnelTracker::destroyObject( Object *obj, void * )
{
// Now that tunnels consider ContainedBy to be "the tunnel you entered", I need to say goodbye
// llike other contain types so they don't look us up on their deletion and crash
obj->onRemovedFrom( obj->getContainedBy() );
TheGameLogic->destroyObject( obj );
}
// ------------------------------------------------------------------------
// heal all the objects within the tunnel system using the iterateContained function
void TunnelTracker::healObjects(Real frames)
{
iterateContained(healObject, &frames, FALSE);
}
// ------------------------------------------------------------------------
// heal one object within the tunnel network system
void TunnelTracker::healObject( Object *obj, void *frames)
{
//get the number of frames to heal
Real *framesForFullHeal = (Real*)frames;
// setup the healing damageInfo structure with all but the amount
DamageInfo healInfo;
healInfo.in.m_damageType = DAMAGE_HEALING;
healInfo.in.m_deathType = DEATH_NONE;
//healInfo.in.m_sourceID = getObject()->getID();
// get body module of the thing to heal
BodyModuleInterface *body = obj->getBodyModule();
// if we've been in here long enough ... set our health to max
if( TheGameLogic->getFrame() - obj->getContainedByFrame() >= *framesForFullHeal )
{
// set the amount to max just to be sure we're at the top
healInfo.in.m_amount = body->getMaxHealth();
// set max health
body->attemptHealing( &healInfo );
} // end if
else
{
//
// given the *whole* time it would take to heal this object, lets pretend that the
// object is at zero health ... and give it a sliver of health as if it were at 0 health
// and would be fully healed at 'framesForFullHeal'
//
healInfo.in.m_amount = body->getMaxHealth() / *framesForFullHeal;
// do the healing
body->attemptHealing( &healInfo );
} // end else
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void TunnelTracker::crc( Xfer *xfer )
{
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void TunnelTracker::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// tunnel object id list
xfer->xferSTLObjectIDList( &m_tunnelIDs );
// contain list count
xfer->xferInt( &m_containListSize );
// contain list data
ObjectID objectID;
if( xfer->getXferMode() == XFER_SAVE )
{
ContainedItemsList::const_iterator it;
for( it = m_containList.begin(); it != m_containList.end(); ++it )
{
objectID = (*it)->getID();
xfer->xferObjectID( &objectID );
} // end for, it
} // end if, save
else
{
for( UnsignedShort i = 0; i < m_containListSize; ++i )
{
xfer->xferObjectID( &objectID );
m_xferContainList.push_back( objectID );
} // end for, i
} // end else, load
// tunnel count
xfer->xferUnsignedInt( &m_tunnelCount );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void TunnelTracker::loadPostProcess( void )
{
// sanity, the contain list should be empty until we post process the id list
if( m_containList.size() != 0 )
{
DEBUG_CRASH(( "TunnelTracker::loadPostProcess - m_containList should be empty but is not\n" ));
throw SC_INVALID_DATA;
} // end if
// translate each object ids on the xferContainList into real object pointers in the contain list
Object *obj;
std::list< ObjectID >::const_iterator it;
for( it = m_xferContainList.begin(); it != m_xferContainList.end(); ++it )
{
obj = TheGameLogic->findObjectByID( *it );
if( obj == NULL )
{
DEBUG_CRASH(( "TunnelTracker::loadPostProcess - Unable to find object ID '%d'\n", *it ));
throw SC_INVALID_DATA;
} // end if
// push on the back of the contain list
m_containList.push_back( obj );
// Crap. This is in OpenContain as a fix, but not here.
{
// remove object from its group (if any)
obj->leaveGroup();
// remove rider from partition manager
ThePartitionManager->unRegisterObject( obj );
// hide the drawable associated with rider
if( obj->getDrawable() )
obj->getDrawable()->setDrawableHidden( true );
// remove object from pathfind map
if( TheAI )
TheAI->pathfinder()->removeObjectFromPathfindMap( obj );
}
} // end for, it
// we're done with the xfer contain list now
m_xferContainList.clear();
} // end loadPostProcess