/* ** 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: CaveContain.cpp //////////////////////////////////////////////////////////////////////////// // Author: Graham Smallwood, July 2002 // Desc: A version of OpenContain that overrides where the passengers are stored: one of CaveSystem's // entries. Changing entry is a script or ini command. All queries about capacity and // contents are also redirected. They change sides like Garrison too. /////////////////////////////////////////////////////////////////////////////////////////////////// // INCLUDES /////////////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/GameState.h" #include "Common/Player.h" #include "Common/PlayerList.h" #include "Common/Team.h" #include "Common/ThingTemplate.h" #include "Common/TunnelTracker.h" #include "Common/Xfer.h" #include "GameClient/Drawable.h" #include "GameLogic/Module/AIUpdate.h" #include "GameLogic/Module/CaveContain.h" #include "GameLogic/CaveSystem.h" #include "GameLogic/Object.h" #include "GameLogic/PartitionManager.h" /////////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC FUNCTIONS /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- CaveContain::CaveContain( Thing *thing, const ModuleData* moduleData ) : OpenContain( thing, moduleData ) { m_needToRunOnBuildComplete = true; m_caveIndex = 0; m_originalTeam = NULL; } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- CaveContain::~CaveContain() { } void CaveContain::addToContainList( Object *obj ) { TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); myTracker->addToContainList( obj ); } //------------------------------------------------------------------------------------------------- /** Remove 'obj' from the m_containList of objects in this module. * This will trigger an onRemoving event for the object that this module * is a part of and an onRemovedFrom event for the object being removed */ //------------------------------------------------------------------------------------------------- void CaveContain::removeFromContain( Object *obj, Bool exposeStealthUnits ) { // sanity if( obj == NULL ) return; // // we can only remove this object from the contains list of this module if // it is actually contained by this module // TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); if( ! myTracker->isInContainer( obj ) ) { return; } // This must come before the onRemov*, because CaveContain's version has a edge-0 triggered event. // If that were to go first, the number would still be 1 at that time. Noone else cares about // order. myTracker->removeFromContain( obj, exposeStealthUnits ); // trigger an onRemoving event for 'm_object' no longer containing 'itemToRemove->m_object' if (getObject()->getContain()) getObject()->getContain()->onRemoving( obj ); // trigger an onRemovedFrom event for 'remove' obj->onRemovedFrom( getObject() ); } //------------------------------------------------------------------------------------------------- /** Remove all contained objects from the contained list */ //------------------------------------------------------------------------------------------------- void CaveContain::removeAllContained( Bool exposeStealthUnits ) { TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); const ContainedItemsList *fullList = myTracker->getContainedItemsList(); Object *obj; ContainedItemsList::const_iterator it; it = (*fullList).begin(); while( it != (*fullList).end() ) { obj = *it; it++; removeFromContain( obj, exposeStealthUnits ); } } //------------------------------------------------------------------------------------------------- /** Iterate the contained list and call the callback on each of the objects */ //------------------------------------------------------------------------------------------------- void CaveContain::iterateContained( ContainIterateFunc func, void *userData, Bool reverse ) { TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); myTracker->iterateContained( func, userData, reverse ); } //------------------------------------------------------------------------------------------------- void CaveContain::onContaining( Object *obj ) { OpenContain::onContaining(obj); // objects inside a building are held obj->setDisabled( DISABLED_HELD ); // // the team of the building is now the same as those that have garrisoned it, be sure // to save our original team tho so that we can revert back to it when all the // occupants are gone // recalcApparentControllingPlayer(); } //------------------------------------------------------------------------------------------------- void CaveContain::onRemoving( Object *obj ) { OpenContain::onRemoving(obj); // object is no longer held inside a garrisoned building obj->clearDisabled( DISABLED_HELD ); /// place the object in the world at position of the container m_object ThePartitionManager->registerObject( obj ); obj->setPosition( getObject()->getPosition() ); if( obj->getDrawable() ) { obj->getDrawable()->setDrawableHidden( false ); } doUnloadSound(); if( getContainCount() == 0 ) { // put us back on our original team // (hokey exception: if our team is null, don't bother -- this // usually means we are being called during game-teardown and // the teams are no longer valid...) if (getObject()->getTeam() != NULL) { changeTeamOnAllConnectedCaves( m_originalTeam, FALSE ); m_originalTeam = NULL; } // change the state back from garrisoned Drawable *draw = getObject()->getDrawable(); if( draw ) { draw->clearModelConditionState( MODELCONDITION_GARRISONED ); } } // end if } Bool CaveContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const { TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); return myTracker->isValidContainerFor( obj, checkCapacity ); } UnsignedInt CaveContain::getContainCount() const { TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); return myTracker->getContainCount(); } Int CaveContain::getContainMax( void ) const { TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); return myTracker->getContainMax(); } const ContainedItemsList* CaveContain::getContainedItemsList() const { TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); return myTracker->getContainedItemsList(); } //------------------------------------------------------------------------------------------------- /** The die callback. */ //------------------------------------------------------------------------------------------------- void CaveContain::onDie( const DamageInfo * damageInfo ) { // override the onDie we inherit from OpenContain. no super call. if (!getCaveContainModuleData()->m_dieMuxData.isDieApplicable(getObject(), damageInfo)) return; if( BitTest( getObject()->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) ) return;//it never registered itself as a tunnel TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); TheCaveSystem->unregisterCave( m_caveIndex ); myTracker->onTunnelDestroyed( getObject() ); } //------------------------------------------------------------------------------------------------- void CaveContain::onCreate( void ) { m_caveIndex = getCaveContainModuleData()->m_caveIndexData; } //------------------------------------------------------------------------------------------------- void CaveContain::onBuildComplete( void ) { if( ! shouldDoOnBuildComplete() ) return; m_needToRunOnBuildComplete = false; TheCaveSystem->registerNewCave( m_caveIndex ); TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); myTracker->onTunnelCreated( getObject() ); } //------------------------------------------------------------------------------------------------- void CaveContain::tryToSetCaveIndex( Int newIndex ) { if( TheCaveSystem->canSwitchIndexToIndex( m_caveIndex, newIndex ) ) { TunnelTracker *myOldTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); TheCaveSystem->unregisterCave( m_caveIndex ); myOldTracker->onTunnelDestroyed( getObject() ); m_caveIndex = newIndex; TheCaveSystem->registerNewCave( m_caveIndex ); TunnelTracker *myNewTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); myNewTracker->onTunnelCreated( getObject() ); } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void CaveContain::recalcApparentControllingPlayer( void ) { //Record original team first time through. if( m_originalTeam == NULL ) { m_originalTeam = getObject()->getTeam(); } // (hokey trick: if our team is null, nuke originalTeam -- this // usually means we are being called during game-teardown and // the teams are no longer valid...) if (getObject()->getTeam() == NULL) m_originalTeam = NULL; // This is called from onContaining, so a one is the edge trigger to do capture stuff if( getContainCount() == 1 ) { ContainedItemsList::const_iterator it = getContainedItemsList()->begin(); Object *rider = *it; // This also gets called during reset from the PlayerList, so we might not actually have players. // Check like the hokey trick mentioned above if( rider->getControllingPlayer() ) changeTeamOnAllConnectedCaves( rider->getControllingPlayer()->getDefaultTeam(), TRUE ); } else if( getContainCount() == 0 ) { // And a 0 is the edge trigger to do uncapture stuff changeTeamOnAllConnectedCaves( m_originalTeam, FALSE ); } // Handle the team color that is rendered const Player* controller = getApparentControllingPlayer(ThePlayerList->getLocalPlayer()); if (controller) { if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT) getObject()->getDrawable()->setIndicatorColor( controller->getPlayerNightColor() ); else getObject()->getDrawable()->setIndicatorColor( controller->getPlayerColor() ); } } ///////////////////////////////////////////////////////////////////////////////////// static CaveInterface* findCave(Object* obj) { for (BehaviorModule** i = obj->getBehaviorModules(); *i; ++i) { CaveInterface* c = (*i)->getCaveInterface(); if (c != NULL) return c; } return NULL; } ///////////////////////////////////////////////////////////////////////////////////// void CaveContain::changeTeamOnAllConnectedCaves( Team *newTeam, Bool setOriginalTeams ) { TunnelTracker *myTracker = TheCaveSystem->getTunnelTrackerForCaveIndex( m_caveIndex ); const std::list *allCaves = myTracker->getContainerList(); for( std::list::const_iterator iter = allCaves->begin(); iter != allCaves->end(); iter++ ) { // For each ID, look it up and change its team. We all get captured together. Object *currentCave = TheGameLogic->findObjectByID( *iter ); if( currentCave ) { // This is a distributed Garrison in terms of capturing, so when one node // triggers the change, he needs to tell everyone, so anyone can do the un-change. CaveInterface *caveModule = findCave(currentCave); if( caveModule == NULL ) continue; if( setOriginalTeams ) caveModule->setOriginalTeam( currentCave->getTeam() ); else caveModule->setOriginalTeam( NULL ); // Now do the actual switch for this one. currentCave->defect( newTeam, 0 ); // currentCave->setTeam( newTeam ); } } } ///////////////////////////////////////////////////////////////////////////////////// void CaveContain::setOriginalTeam( Team *oldTeam ) { m_originalTeam = oldTeam; } // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void CaveContain::crc( Xfer *xfer ) { // extend base class OpenContain::crc( xfer ); } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void CaveContain::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // extend base class OpenContain::xfer( xfer ); // need to run on build complete xfer->xferBool( &m_needToRunOnBuildComplete ); // cave index xfer->xferInt( &m_caveIndex ); // original team TeamID teamID = m_originalTeam ? m_originalTeam->getID() : TEAM_ID_INVALID; xfer->xferUser( &teamID, sizeof( TeamID ) ); if( xfer->getXferMode() == XFER_LOAD ) { if( teamID != TEAM_ID_INVALID ) { m_originalTeam = TheTeamFactory->findTeamByID( teamID ); if( m_originalTeam == NULL ) { DEBUG_CRASH(( "CaveContain::xfer - Unable to find original team by id\n" )); throw SC_INVALID_DATA; } // end if } // end if else m_originalTeam = NULL; } // end if } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void CaveContain::loadPostProcess( void ) { // extend base class OpenContain::loadPostProcess(); } // end loadPostProcess