Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.
This commit is contained in:
parent
2e338c00cb
commit
3d0ee53a05
6072 changed files with 2283311 additions and 0 deletions
1875
Generals/Code/GameEngine/Source/Common/RTS/ActionManager.cpp
Normal file
1875
Generals/Code/GameEngine/Source/Common/RTS/ActionManager.cpp
Normal file
File diff suppressed because it is too large
Load diff
259
Generals/Code/GameEngine/Source/Common/RTS/Energy.cpp
Normal file
259
Generals/Code/GameEngine/Source/Common/RTS/Energy.cpp
Normal file
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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/Object.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Real Energy::getEnergySupplyRatio() const
|
||||
{
|
||||
DEBUG_ASSERTCRASH(m_energyProduction >= 0 && m_energyConsumption >= 0, ("neg Energy numbers\n"));
|
||||
|
||||
if (m_energyConsumption == 0)
|
||||
return (Real)m_energyProduction;
|
||||
|
||||
return (Real)m_energyProduction / (Real)m_energyConsumption;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
Bool Energy::hasSufficientPower(void) const
|
||||
{
|
||||
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 = 2;
|
||||
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 );
|
||||
|
||||
} // end xfer
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** Load post process */
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
void Energy::loadPostProcess( void )
|
||||
{
|
||||
|
||||
} // end loadPostProcess
|
126
Generals/Code/GameEngine/Source/Common/RTS/Handicap.cpp
Normal file
126
Generals/Code/GameEngine/Source/Common/RTS/Handicap.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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];
|
||||
}
|
113
Generals/Code/GameEngine/Source/Common/RTS/MissionStats.cpp
Normal file
113
Generals/Code/GameEngine/Source/Common/RTS/MissionStats.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
125
Generals/Code/GameEngine/Source/Common/RTS/Money.cpp
Normal file
125
Generals/Code/GameEngine/Source/Common/RTS/Money.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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/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;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
/** 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
|
||||
|
4135
Generals/Code/GameEngine/Source/Common/RTS/Player.cpp
Normal file
4135
Generals/Code/GameEngine/Source/Common/RTS/Player.cpp
Normal file
File diff suppressed because it is too large
Load diff
493
Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp
Normal file
493
Generals/Code/GameEngine/Source/Common/RTS/PlayerList.cpp
Normal file
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
||||
|
389
Generals/Code/GameEngine/Source/Common/RTS/PlayerTemplate.cpp
Normal file
389
Generals/Code/GameEngine/Source/Common/RTS/PlayerTemplate.cpp
Normal file
|
@ -0,0 +1,389 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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 ) },
|
||||
{ "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 ) },
|
||||
{ "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 ) },
|
||||
|
||||
{ "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 ) },
|
||||
|
||||
{ "BeaconName", INI::parseAsciiString, NULL, offsetof( PlayerTemplate, m_beaconTemplate ) },
|
||||
{ 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_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::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
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
||||
//
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
||||
|
366
Generals/Code/GameEngine/Source/Common/RTS/Science.cpp
Normal file
366
Generals/Code/GameEngine/Source/Common/RTS/Science.cpp
Normal file
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
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);
|
||||
}
|
||||
|
568
Generals/Code/GameEngine/Source/Common/RTS/ScoreKeeper.cpp
Normal file
568
Generals/Code/GameEngine/Source/Common/RTS/ScoreKeeper.cpp
Normal file
|
@ -0,0 +1,568 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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
|
349
Generals/Code/GameEngine/Source/Common/RTS/SpecialPower.cpp
Normal file
349
Generals/Code/GameEngine/Source/Common/RTS/SpecialPower.cpp
Normal file
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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_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_REPAIR_VEHICLES",
|
||||
"SPECIAL_PARTICLE_UPLINK_CANNON",
|
||||
"SPECIAL_CASH_BOUNTY",
|
||||
"SPECIAL_CHANGE_BATTLE_PLANS",
|
||||
"SPECIAL_CIA_INTELLIGENCE",
|
||||
"SPECIAL_CLEANUP_AREA",
|
||||
"SPECIAL_LAUNCH_BAIKONUR_ROCKET",
|
||||
|
||||
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 ) },
|
||||
{ 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;
|
||||
|
||||
} // 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
|
2748
Generals/Code/GameEngine/Source/Common/RTS/Team.cpp
Normal file
2748
Generals/Code/GameEngine/Source/Common/RTS/Team.cpp
Normal file
File diff suppressed because it is too large
Load diff
410
Generals/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp
Normal file
410
Generals/Code/GameEngine/Source/Common/RTS/TunnelTracker.cpp
Normal file
|
@ -0,0 +1,410 @@
|
|||
/*
|
||||
** Command & Conquer Generals(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** This program is free software: you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation, either version 3 of the License, or
|
||||
** (at your option) any later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program. If not, see <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!
|
||||
UnsignedInt status = target->getStatusBits();
|
||||
if( (status & OBJECT_STATUS_STEALTHED) && !(status & OBJECT_STATUS_DETECTED) ) {
|
||||
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
|
Reference in a new issue