| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: RiderChangeContain.cpp //////////////////////////////////////////////////////////////////////
- // Author: Kris Morness, May 2003
- // Desc: Contain module for the combat bike (transport that switches units).
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #define DEFINE_LOCOMOTORSET_NAMES //Gain access to TheLocomotorSetNames[]
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/ThingTemplate.h"
- #include "Common/ThingFactory.h"
- #include "Common/Xfer.h"
- #include "GameClient/ControlBar.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/InGameUI.h"
- #include "GameLogic/AI.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/ExperienceTracker.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/Module/StealthUpdate.h"
- #include "GameLogic/Module/RiderChangeContain.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- RiderChangeContainModuleData::RiderChangeContainModuleData()
- {
- m_scuttleFrames = 0;
- m_scuttleState = MODELCONDITION_TOPPLED;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RiderChangeContainModuleData::parseRiderInfo( INI* ini, void *instance, void *store, const void* /*userData*/ )
- {
- RiderInfo* rider = (RiderInfo*)store;
- const char* name = ini->getNextToken();
- //Template name
- rider->m_templateName.format( name );
- //Model condition state
- INI::parseIndexList( ini, instance, &(rider->m_modelConditionFlagType), ModelConditionFlags::getBitNames() );
- //Weaponset
- INI::parseIndexList( ini, instance, &(rider->m_weaponSetFlag), WeaponSetFlags::getBitNames() );
- //Object status
- INI::parseIndexList( ini, instance, &(rider->m_objectStatusType), ObjectStatusMaskType::getBitNames() );
- //Command set override
- name = ini->getNextToken();
- rider->m_commandSet.format( name );
- //Locomotor set type
- rider->m_locomotorSetType = (LocomotorSetType)INI::scanIndexList( ini->getNextToken(), TheLocomotorSetNames );
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RiderChangeContainModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- TransportContainModuleData::buildFieldParse(p);
- static const FieldParse dataFieldParse[] =
- {
- { "Rider1", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[0] ) },
- { "Rider2", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[1] ) },
- { "Rider3", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[2] ) },
- { "Rider4", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[3] ) },
- { "Rider5", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[4] ) },
- { "Rider6", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[5] ) },
- { "Rider7", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[6] ) },
- { "Rider8", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[7] ) },
- { "ScuttleDelay", INI::parseDurationUnsignedInt, NULL, offsetof( RiderChangeContainModuleData, m_scuttleFrames ) },
- { "ScuttleStatus", INI::parseIndexList, ModelConditionFlags::getBitNames(), offsetof( RiderChangeContainModuleData, m_scuttleState ) },
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- }
- // PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Int RiderChangeContain::getContainMax( void ) const
- {
- if (getRiderChangeContainModuleData())
- return getRiderChangeContainModuleData()->m_slotCapacity;
- return 0;
- }
- // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- RiderChangeContain::RiderChangeContain( Thing *thing, const ModuleData *moduleData ) :
- TransportContain( thing, moduleData )
- {
- m_extraSlotsInUse = 0;
- m_frameExitNotBusy = 0;
- m_containing = FALSE;
- m_scuttledOnFrame = 0;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- RiderChangeContain::~RiderChangeContain( void )
- {
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /**
- can this container contain this kind of object?
- and, if checkCapacity is TRUE, does this container have enough space left to hold the given unit?
- */
- Bool RiderChangeContain::isValidContainerFor(const Object* rider, Bool checkCapacity) const
- {
- //Don't check capacity because our rider will kick the other rider out!
- if( TransportContain::isValidContainerFor( rider, FALSE ) )
- {
- if( m_scuttledOnFrame != 0 )
- {
- //Scuttled... too late!
- return FALSE;
- }
- //We can enter this bike... but now we need to extend the base functionality by limiting
- //which infantry can enter.
- const RiderChangeContainModuleData *data = getRiderChangeContainModuleData();
- for( int i = 0; i < MAX_RIDERS; i++ )
- {
- const ThingTemplate *thing = TheThingFactory->findTemplate( data->m_riders[ i ].m_templateName );
- if( thing->isEquivalentTo( rider->getTemplate() ) )
- {
- //We found a valid rider, so return success.
- return TRUE;
- }
- }
- }
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void RiderChangeContain::onContaining( Object *rider, Bool wasSelected )
- {
- Object *obj = getObject();
- m_containing = TRUE;
- //Remove our existing rider
- if( m_payloadCreated )
- {
- obj->getAI()->aiEvacuateInstantly( TRUE, CMD_FROM_AI );
- }
- //If the rider is currently selected, transfer selection to the container and preserve other units
- //that may be already selected. Note that containing the rider will automatically cause it to be
- //deselected, so all we have to do is select the container (if not already selected)!
- Drawable *containDraw = getObject()->getDrawable();
- if( containDraw && wasSelected && !containDraw->isSelected() )
- {
- //Create the selection message
- GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP );
- teamMsg->appendBooleanArgument( FALSE );// not creating new team so pass false
- teamMsg->appendObjectIDArgument( getObject()->getID() );
- TheInGameUI->selectDrawable( containDraw );
- TheInGameUI->setDisplayedMaxWarning( FALSE );
- }
- //Find the rider in the list and set the appropriate model condition
- const RiderChangeContainModuleData *data = getRiderChangeContainModuleData();
- for( int i = 0; i < MAX_RIDERS; i++ )
- {
- const ThingTemplate *thing = TheThingFactory->findTemplate( data->m_riders[ i ].m_templateName );
- if( thing->isEquivalentTo( rider->getTemplate() ) )
- {
- //This is our rider, so set the correct model condition.
- obj->setModelConditionState( data->m_riders[ i ].m_modelConditionFlagType );
- //Also set the correct weaponset flag
- obj->setWeaponSetFlag( data->m_riders[ i ].m_weaponSetFlag );
- //Also set the object status
- obj->setStatus( MAKE_OBJECT_STATUS_MASK( data->m_riders[ i ].m_objectStatusType ) );
- //Set the new commandset override
- obj->setCommandSetStringOverride( data->m_riders[ i ].m_commandSet );
- TheControlBar->markUIDirty(); // Refresh the UI in case we are selected
- //Change the locomotor.
- AIUpdateInterface* ai = obj->getAI();
- if( ai )
- {
- ai->chooseLocomotorSet( data->m_riders[ i ].m_locomotorSetType );
- }
- if( obj->getStatusBits().test( OBJECT_STATUS_STEALTHED ) )
- {
- StealthUpdate* stealth = obj->getStealth();
- if( stealth )
- {
- stealth->markAsDetected();
- }
- }
- //Transfer experience from the rider to the bike.
- ExperienceTracker *riderTracker = rider->getExperienceTracker();
- ExperienceTracker *bikeTracker = obj->getExperienceTracker();
- bikeTracker->setVeterancyLevel( riderTracker->getVeterancyLevel(), FALSE );
- riderTracker->setExperienceAndLevel( 0, FALSE );
- break;
- }
- }
-
- //Extend base class
- TransportContain::onContaining( rider, wasSelected );
- m_containing = FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void RiderChangeContain::onRemoving( Object *rider )
- {
- Object *bike = getObject();
- //Note if the bike dies, the rider dies too.
- if( bike->isEffectivelyDead() )
- {
- TheGameLogic->destroyObject( rider );
- return;
- }
- if( m_payloadCreated )
- {
- //Extend base class
- TransportContain::onRemoving( rider );
- }
- //Find the rider in the list and clear various data.
- const RiderChangeContainModuleData *data = getRiderChangeContainModuleData();
- for( int i = 0; i < MAX_RIDERS; i++ )
- {
- const ThingTemplate *thing = TheThingFactory->findTemplate( data->m_riders[ i ].m_templateName );
- if( thing->isEquivalentTo( rider->getTemplate() ) )
- {
- //This is our rider, so clear the current model condition.
- bike->clearModelConditionFlags( MAKE_MODELCONDITION_MASK2( data->m_riders[ i ].m_modelConditionFlagType, MODELCONDITION_DOOR_1_CLOSING ) );
- //Also clear the current weaponset flag
- bike->clearWeaponSetFlag( data->m_riders[ i ].m_weaponSetFlag );
- //Also clear the object status
- bike->clearStatus( MAKE_OBJECT_STATUS_MASK( data->m_riders[ i ].m_objectStatusType ) );
-
- if( rider->getControllingPlayer() != NULL )
- {
- //Wow, completely unforseeable game teardown order crash. SetVeterancyLevel results in a call to player
- //about upgrade masks. So if we have a null player, it is game teardown, so don't worry about transfering exp.
- //Transfer experience from the bike to the rider.
- ExperienceTracker *riderTracker = rider->getExperienceTracker();
- ExperienceTracker *bikeTracker = bike->getExperienceTracker();
- riderTracker->setVeterancyLevel( bikeTracker->getVeterancyLevel(), FALSE );
- bikeTracker->setExperienceAndLevel( 0, FALSE );
- }
- break;
- }
- }
- //If we're not replacing the rider, then if the cycle is selected, transfer selection
- //to the rider getting off (because the bike is gonna blow).
- if( !m_containing )
- {
- Drawable *containDraw = bike->getDrawable();
- Drawable *riderDraw = rider->getDrawable();
- if( containDraw && riderDraw )
- {
- //Create the selection message for the rider if it's ours and SELECTED!
- if( bike->getControllingPlayer() == ThePlayerList->getLocalPlayer() && containDraw->isSelected() )
- {
- GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP );
- teamMsg->appendBooleanArgument( FALSE );// not creating new team so pass false
- teamMsg->appendObjectIDArgument( rider->getID() );
- TheInGameUI->selectDrawable( riderDraw );
- TheInGameUI->setDisplayedMaxWarning( FALSE );
- //Create the de-selection message for the container
- teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_REMOVE_FROM_SELECTED_GROUP );
- teamMsg->appendObjectIDArgument( bike->getID() );
- TheInGameUI->deselectDrawable( containDraw );
- }
- //Finally, scuttle the bike so nobody else can use it! <Design Spec>
- m_scuttledOnFrame = TheGameLogic->getFrame();
- bike->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNSELECTABLE ) );
- bike->setModelConditionState( data->m_scuttleState );
- if( !bike->getAI()->isMoving() )
- {
- bike->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_IMMOBILE ) );
- }
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RiderChangeContain::createPayload()
- {
- // extend base class
- TransportContain::createPayload();
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- UpdateSleepTime RiderChangeContain::update()
- {
- if( m_scuttledOnFrame != 0 )
- {
- //Bike in the process of getting scuttled.
- const RiderChangeContainModuleData *data = getRiderChangeContainModuleData();
- UnsignedInt now = TheGameLogic->getFrame();
- if( m_scuttledOnFrame + data->m_scuttleFrames <= now )
- {
- //We have scuttled the bike (at least as far as tipping it over via scuttle animation. Now
- //kill the bike in a way that will cause it to sink into the ground without any real destruction.
- getObject()->kill( DAMAGE_UNRESISTABLE, DEATH_TOPPLED ); //Sneaky, eh? Toppled heheh.
- }
- }
- // extend base class
- return TransportContain::update(); //extend
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RiderChangeContain::unreserveDoorForExit( ExitDoorType exitDoor )
- {
- /* nothing */
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RiderChangeContain::killRidersWhoAreNotFreeToExit()
- {
- // extend base class
- TransportContain::killRidersWhoAreNotFreeToExit();
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool RiderChangeContain::isSpecificRiderFreeToExit(Object* specificObject)
- {
- // extend base class
- return TransportContain::isSpecificRiderFreeToExit( specificObject );
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- ExitDoorType RiderChangeContain::reserveDoorForExit( const ThingTemplate* objType, Object *specificObject )
- {
- // extend base class
- return TransportContain::reserveDoorForExit( objType, specificObject );
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool RiderChangeContain::isExitBusy() const ///< Contain style exiters are getting the ability to space out exits, so ask this before reserveDoor as a kind of no-commitment check.
- {
- // extend base class
- return FALSE; //return TransportContain::isExitBusy();
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void RiderChangeContain::onCapture( Player *oldOwner, Player *newOwner )
- {
- // extend base class
- TransportContain::onCapture( oldOwner, newOwner );
- }
- //-------------------------------------------------------------------------------------------------
- Bool RiderChangeContain::getContainerPipsToShow(Int& numTotal, Int& numFull)
- {
- //Don't show any pips for motorcycles as they always have one rider unless dead!
- numTotal = 0;
- numFull = 0;
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- const Object *RiderChangeContain::friend_getRider() const
- {
- if( m_containListSize > 0 ) // Yes, this does assume that infantry never ride double on the bike
- return m_containList.front();
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void RiderChangeContain::crc( Xfer *xfer )
- {
- // extend base class
- TransportContain::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void RiderChangeContain::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- TransportContain::xfer( xfer );
- // payload created
- xfer->xferBool( &m_payloadCreated );
- // extra slots in use
- xfer->xferInt( &m_extraSlotsInUse );
- // frame exit not busy
- xfer->xferUnsignedInt( &m_frameExitNotBusy );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void RiderChangeContain::loadPostProcess( void )
- {
- // extend base class
- TransportContain::loadPostProcess();
- } // end loadPostProcess
|