/* ** Command & Conquer Generals(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: Upgrade.cpp ////////////////////////////////////////////////////////////////////////////// // Author: Colin Day, March 2002 // Desc: Upgrade system for players /////////////////////////////////////////////////////////////////////////////////////////////////// // USER INCLUDES ////////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #define DEFINE_UPGRADE_TYPE_NAMES #define DEFINE_VETERANCY_NAMES #include "Common/Upgrade.h" #include "Common/Player.h" #include "Common/Xfer.h" #include "GameClient/InGameUI.h" #include "GameClient/Image.h" // PUBLIC ///////////////////////////////////////////////////////////////////////////////////////// class UpgradeCenter *TheUpgradeCenter = NULL; /////////////////////////////////////////////////////////////////////////////////////////////////// // UPGRADE //////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- Upgrade::Upgrade( const UpgradeTemplate *upgradeTemplate ) { m_template = upgradeTemplate; m_status = UPGRADE_STATUS_INVALID; m_next = NULL; m_prev = NULL; } // end Upgrade //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- Upgrade::~Upgrade( void ) { } // end ~Upgrade // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void Upgrade::crc( Xfer *xfer ) { } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void Upgrade::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // status xfer->xferUser( &m_status, sizeof( UpgradeStatusType ) ); } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void Upgrade::loadPostProcess( void ) { } // end loadPostProcess /////////////////////////////////////////////////////////////////////////////////////////////////// // UPGRADE TEMPLATE /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- const FieldParse UpgradeTemplate::m_upgradeFieldParseTable[] = { { "DisplayName", INI::parseAsciiString, NULL, offsetof( UpgradeTemplate, m_displayNameLabel ) }, { "Type", INI::parseIndexList, UpgradeTypeNames, offsetof( UpgradeTemplate, m_type ) }, { "BuildTime", INI::parseReal, NULL, offsetof( UpgradeTemplate, m_buildTime ) }, { "BuildCost", INI::parseInt, NULL, offsetof( UpgradeTemplate, m_cost ) }, { "ButtonImage", INI::parseAsciiString, NULL, offsetof( UpgradeTemplate, m_buttonImageName ) }, { "ResearchSound", INI::parseAudioEventRTS, NULL, offsetof( UpgradeTemplate, m_researchSound ) }, { "UnitSpecificSound", INI::parseAudioEventRTS, NULL, offsetof( UpgradeTemplate, m_unitSpecificSound ) }, { NULL, NULL, NULL, 0 } // keep this last }; //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- UpgradeTemplate::UpgradeTemplate( void ) { //Added By Sadullah Nader //Initialization(s) inserted m_cost = 0; // m_type = UPGRADE_TYPE_PLAYER; m_nameKey = NAMEKEY_INVALID; m_buildTime = 0.0f; m_upgradeMask = 0; m_next = NULL; m_prev = NULL; m_buttonImage = NULL; } // end UpgradeTemplate //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- UpgradeTemplate::~UpgradeTemplate( void ) { } // end ~UpgradeTemplate //------------------------------------------------------------------------------------------------- /** Calculate the time it takes (in logic frames) for a player to build this UpgradeTemplate */ //------------------------------------------------------------------------------------------------- Int UpgradeTemplate::calcTimeToBuild( Player *player ) const { #if defined(_DEBUG) || defined(_INTERNAL) if( player->buildsInstantly() ) { return 1; } #endif ///@todo modify this by power state of player return m_buildTime * LOGICFRAMES_PER_SECOND; } // end calcTimeToBuild //------------------------------------------------------------------------------------------------- /** Calculate the cost takes this player to build this upgrade */ //------------------------------------------------------------------------------------------------- Int UpgradeTemplate::calcCostToBuild( Player *player ) const { ///@todo modify this by any player handicaps return m_cost; } // end calcCostToBuild //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- static AsciiString getVetUpgradeName(VeterancyLevel v) { AsciiString tmp; tmp = "Upgrade_Veterancy_"; tmp.concat(TheVeterancyNames[v]); return tmp; } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void UpgradeTemplate::friend_makeVeterancyUpgrade(VeterancyLevel v) { m_type = UPGRADE_TYPE_OBJECT; // veterancy "upgrades" are always per-object, not per-player m_name = getVetUpgradeName(v); m_nameKey = TheNameKeyGenerator->nameToKey( m_name ); m_displayNameLabel.clear(); // should never be displayed m_buildTime = 0.0f; m_cost = 0.0f; // leave this alone. //m_upgradeMask = ???; } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void UpgradeTemplate::cacheButtonImage() { if( m_buttonImageName.isNotEmpty() ) { m_buttonImage = TheMappedImageCollection->findImageByName( m_buttonImageName ); DEBUG_ASSERTCRASH( m_buttonImage, ("UpgradeTemplate: %s is looking for button image %s but can't find it. Skipping...", m_name.str(), m_buttonImageName.str() ) ); m_buttonImageName.clear(); // we're done with this, so nuke it } } /////////////////////////////////////////////////////////////////////////////////////////////////// // UPGRADE CENTER ///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- UpgradeCenter::UpgradeCenter( void ) { m_upgradeList = NULL; m_nextTemplateMaskBit = 0; buttonImagesCached = FALSE; } // end UpgradeCenter //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- UpgradeCenter::~UpgradeCenter( void ) { // delete all the upgrades loaded from the INI database UpgradeTemplate *next; while( m_upgradeList ) { // get next next = m_upgradeList->friend_getNext(); // delete head of list m_upgradeList->deleteInstance(); // set head to next element m_upgradeList = next; } // end while } // end ~UpgradeCenter //------------------------------------------------------------------------------------------------- /** Upgrade center initialization */ //------------------------------------------------------------------------------------------------- void UpgradeCenter::init( void ) { UpgradeTemplate* up; // name will be overridden by friend_makeVeterancyUpgrade // no, there ISN'T an upgrade for this one... //up = newUpgrade(""); //up->friend_makeVeterancyUpgrade(LEVEL_REGULAR); up = newUpgrade(""); up->friend_makeVeterancyUpgrade(LEVEL_VETERAN); up = newUpgrade(""); up->friend_makeVeterancyUpgrade(LEVEL_ELITE); up = newUpgrade(""); up->friend_makeVeterancyUpgrade(LEVEL_HEROIC); } //------------------------------------------------------------------------------------------------- /** Upgrade center system reset */ //------------------------------------------------------------------------------------------------- void UpgradeCenter::reset( void ) { if( TheMappedImageCollection && !buttonImagesCached ) { UpgradeTemplate *upgrade; for( upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() ) { upgrade->cacheButtonImage(); } buttonImagesCached = TRUE; } } //------------------------------------------------------------------------------------------------- /** Find upgrade matching name key */ //------------------------------------------------------------------------------------------------- const UpgradeTemplate *UpgradeCenter::findVeterancyUpgrade( VeterancyLevel level ) const { AsciiString tmp = getVetUpgradeName(level); return findUpgrade(tmp); } //------------------------------------------------------------------------------------------------- /** Find upgrade matching name key */ //------------------------------------------------------------------------------------------------- UpgradeTemplate *UpgradeCenter::findNonConstUpgradeByKey( NameKeyType key ) { UpgradeTemplate *upgrade; // search list for( upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() ) if( upgrade->getUpgradeNameKey() == key ) return upgrade; // item not found return NULL; } // ------------------------------------------------------------------------------------------------ /** Return the first upgrade template */ // ------------------------------------------------------------------------------------------------ UpgradeTemplate *UpgradeCenter::firstUpgradeTemplate( void ) { return m_upgradeList; } // end firstUpgradeTemplate //------------------------------------------------------------------------------------------------- /** Find upgrade matching name key */ //------------------------------------------------------------------------------------------------- const UpgradeTemplate *UpgradeCenter::findUpgradeByKey( NameKeyType key ) const { const UpgradeTemplate *upgrade; // search list for( upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() ) if( upgrade->getUpgradeNameKey() == key ) return upgrade; // item not found return NULL; } //------------------------------------------------------------------------------------------------- /** Find upgrade matching name */ //------------------------------------------------------------------------------------------------- const UpgradeTemplate *UpgradeCenter::findUpgrade( const AsciiString& name ) const { return findUpgradeByKey( TheNameKeyGenerator->nameToKey( name ) ); } // end findUpgrade //------------------------------------------------------------------------------------------------- /** Allocate a new upgrade template */ //------------------------------------------------------------------------------------------------- UpgradeTemplate *UpgradeCenter::newUpgrade( const AsciiString& name ) { UpgradeTemplate *newUpgrade = newInstance(UpgradeTemplate); // copy data from the default upgrade const UpgradeTemplate *defaultUpgrade = findUpgrade( "DefaultUpgrade" ); if( defaultUpgrade ) *newUpgrade = *defaultUpgrade; // assign name and starting data newUpgrade->setUpgradeName( name ); newUpgrade->setUpgradeNameKey( TheNameKeyGenerator->nameToKey( name ) ); // Make a unique bitmask for this new template by keeping track of what bits have been assigned // damn MSFT! proper ANSI syntax for a proper 64-bit constant is "1LL", but MSVC doesn't recognize it Int64 newMask = 1i64 << m_nextTemplateMaskBit; m_nextTemplateMaskBit++; DEBUG_ASSERTCRASH( m_nextTemplateMaskBit < 64, ("Can't have over 64 types of Upgrades and have a Bitfield function.") ); newUpgrade->friend_setUpgradeMask( newMask ); // link upgrade linkUpgrade( newUpgrade ); // return new upgrade return newUpgrade; } // end newUnlinkedUpgrade //------------------------------------------------------------------------------------------------- /** Link an upgrade to our list */ //------------------------------------------------------------------------------------------------- void UpgradeCenter::linkUpgrade( UpgradeTemplate *upgrade ) { // sanity if( upgrade == NULL ) return; // link upgrade->friend_setPrev( NULL ); upgrade->friend_setNext( m_upgradeList ); if( m_upgradeList ) m_upgradeList->friend_setPrev( upgrade ); m_upgradeList = upgrade; } // end linkUpgrade //------------------------------------------------------------------------------------------------- /** Unlink an upgrade from our list */ //------------------------------------------------------------------------------------------------- void UpgradeCenter::unlinkUpgrade( UpgradeTemplate *upgrade ) { // sanity if( upgrade == NULL ) return; if( upgrade->friend_getNext() ) upgrade->friend_getNext()->friend_setPrev( upgrade->friend_getPrev() ); if( upgrade->friend_getPrev() ) upgrade->friend_getPrev()->friend_setNext( upgrade->friend_getNext() ); else m_upgradeList = upgrade->friend_getNext(); } // end unlinkUpgrade //------------------------------------------------------------------------------------------------- /** does this player have all the necessary things to make this upgrade */ //------------------------------------------------------------------------------------------------- Bool UpgradeCenter::canAffordUpgrade( Player *player, const UpgradeTemplate *upgradeTemplate, Bool displayReason ) const { // sanity if( player == NULL || upgradeTemplate == NULL ) return FALSE; // money check Money *money = player->getMoney(); if( money->countMoney() < upgradeTemplate->calcCostToBuild( player ) ) { //Post reason why we can't make upgrade! if( displayReason ) { TheInGameUI->message( "GUI:NotEnoughMoneyToUpgrade" ); } return FALSE; } /// @todo maybe have prereq checks for upgrades??? return TRUE; // all is well } // end canAffordUpgrade //------------------------------------------------------------------------------------------------- /** generate a list of upgrade names for WorldBuilder */ //------------------------------------------------------------------------------------------------- std::vector UpgradeCenter::getUpgradeNames( void ) const { std::vector upgradeNames; for( UpgradeTemplate *upgrade = m_upgradeList; upgrade; upgrade = upgrade->friend_getNext() ) upgradeNames.push_back(upgrade->getUpgradeName()); return upgradeNames; } // end getUpgradeNames //------------------------------------------------------------------------------------------------- /** Parse an upgrade definition */ //------------------------------------------------------------------------------------------------- void UpgradeCenter::parseUpgradeDefinition( INI *ini ) { // read the name const char* c = ini->getNextToken(); AsciiString name = c; // find existing item if present UpgradeTemplate* upgrade = TheUpgradeCenter->findNonConstUpgradeByKey( NAMEKEY(name) ); if( upgrade == NULL ) { // allocate a new item upgrade = TheUpgradeCenter->newUpgrade( name ); } // end if // sanity DEBUG_ASSERTCRASH( upgrade, ("parseUpgradeDefinition: Unable to allocate upgrade '%s'\n", name.str()) ); // parse the ini definition ini->initFromINI( upgrade, upgrade->getFieldParse() ); }