| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656 |
- /*
- ** 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: GarrisonContain.cpp //////////////////////////////////////////////////////////////////////
- // Author: Colin Day, February 2002
- // Desc: Contain module for structures that can be garrisoned
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/GameState.h"
- #include "Common/PerfTimer.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/RandomValue.h"
- #include "Common/Team.h"
- #include "Common/ThingFactory.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Xfer.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/GarrisonContain.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/Weapon.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/GameClient.h"
- #include "GameClient/InGameUI.h"
- #include "GameClient/View.h"
- #ifdef _INTERNAL
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- enum { MUZZLE_FLASH_LIFETIME = LOGICFRAMES_PER_SECOND / 7 };
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- GarrisonContainModuleData::GarrisonContainModuleData( void )
- {
- //
- // by default we say that transports can have infantry inside them, this will be totally
- // overwritten by any data provided from the INI entry tho
- //
- m_allowInsideKindOf = MAKE_KINDOF_MASK( KINDOF_INFANTRY );
- m_mobileGarrison = FALSE;
- m_doIHealObjects = false; ///< if T, then I heal objects that are inside of me
- m_framesForFullHeal = 1.0f; ///< the number of frames something inside of me takes to heal
- m_immuneToClearBuildingAttacks = false;
- m_initialRoster.count = 0;
- } // end if
- //-----------------------------------------------------------------------------
- inline Real calcDistSqr(const Coord3D& a, const Coord3D& b)
- {
- return sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z);
- }
- // ------------------------------------------------------------------------------------------------
- /** Given the target position, find the garrison point that is closest to it */
- // ------------------------------------------------------------------------------------------------
- Int GarrisonContain::findClosestFreeGarrisonPointIndex( Int conditionIndex,
- const Coord3D *targetPos )
- {
- DEBUG_ASSERTCRASH(m_garrisonPointsInitialized, ("garrisonPoints are not inited"));
- // sanity
- if( targetPos == NULL || m_garrisonPointsInUse == MAX_GARRISON_POINTS )
- return GARRISON_INDEX_INVALID;
- Int closestIndex = GARRISON_INDEX_INVALID;
- Real closestDistSq = -1.0f;
- Real distSq;
- for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
- {
- // only consider free garrison points
- if( m_garrisonPointData[ i ].object == NULL )
- {
- // compute the squared distance between these two points
- distSq = calcDistSqr(*targetPos, m_garrisonPoint[ conditionIndex ][ i ]);
- if( distSq < closestDistSq || closestDistSq == -1.0f )
- {
- closestDistSq = distSq;
- closestIndex = i;
- } // end if
- } // end if
- } // end for i
- return closestIndex;
- } // end findClosestFreeGarrisonPointIndex
- // ------------------------------------------------------------------------------------------------
- /** Given the object, return the garrison point index the object is placed at ... if any */
- // ------------------------------------------------------------------------------------------------
- Int GarrisonContain::getObjectGarrisonPointIndex( Object *obj )
- {
- // sanity
- if( obj == NULL )
- return GARRISON_INDEX_INVALID;
- for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
- if( m_garrisonPointData[ i ].object == obj )
- return i;
- return GARRISON_INDEX_INVALID;
- } // end getObjectGarrisonPointIndex
- // ------------------------------------------------------------------------------------------------
- /** Put the object at the specified garrison point by index */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::putObjectAtGarrisonPoint( Object *obj,
- ObjectID targetID,
- Int conditionIndex,
- Int pointIndex )
- {
- DEBUG_ASSERTCRASH(m_garrisonPointsInitialized, ("garrisonPoints are not inited"));
- // sanity
- if( obj == NULL || pointIndex < 0 || pointIndex >= MAX_GARRISON_POINTS ||
- conditionIndex < 0 || conditionIndex >= MAX_GARRISON_POINT_CONDITIONS )
- {
- DEBUG_CRASH(( "GarrisionContain::putObjectAtGarrisionPoint - Invalid arguments\n" ));
- return;
- } // end if
- // make sure this point is empty
- if( m_garrisonPointData[ pointIndex ].object != NULL )
- {
- DEBUG_CRASH(( "GarrisonContain::putObjectAtGarrisonPoint - Garrison Point '%d' is not empty\n",
- pointIndex ));
- return;
- } // end if
- // get the position we're going to use
- Coord3D pos = m_garrisonPoint[ conditionIndex ][ pointIndex ];
- // set the object position
- obj->setPosition( &pos );
- // save the data for being place at this point
- m_garrisonPointData[ pointIndex ].object = obj;
- m_garrisonPointData[ pointIndex ].targetID = targetID;
- m_garrisonPointData[ pointIndex ].placeFrame = TheGameLogic->getFrame();
- ++m_garrisonPointsInUse;
- //
- // create a drawable that has a gun barrel which will show there is an object at this
- // garrison point ready to shoot
- //
- static const ThingTemplate *muzzle = TheThingFactory->findTemplate( "GarrisonGun" );
- DEBUG_ASSERTCRASH( muzzle, ("Warning, Object 'GarrisonGun' not found and is need for Garrison gun effects\n") );
- if( muzzle )
- {
- Drawable *draw = TheThingFactory->newDrawable( muzzle );
- if( draw )
- {
- // set position of the drawable at the garrison fire point
- draw->setPosition( &pos );
- // record the drawable in our data array
- m_garrisonPointData[ pointIndex ].effect = draw;
- m_garrisonPointData[ pointIndex ].lastEffectFrame = 0;
- //Copy shroud status from our container.
- Drawable *containerDrawable=getObject()->getDrawable();
- if (containerDrawable)
- draw->setFullyObscuredByShroud(containerDrawable->getFullyObscuredByShroud());
- } // end if
- } // end if
- /*
- UnicodeString msg;
- msg.format( L"Added object '%S'(%d) to point '%d'",
- obj->getTemplate()->getName().str(),
- obj->getID(),
- pointIndex );
- TheInGameUI->message( msg );
- */
- } // end putObjectAtGarrisonPoint
- // ------------------------------------------------------------------------------------------------
- /** Given the current state of the structure, return the condition index we are to use
- * from the garrison point position arrays */
- // ------------------------------------------------------------------------------------------------
- Int GarrisonContain::findConditionIndex( void )
- {
- BodyModuleInterface *body = getObject()->getBodyModule();
- BodyDamageType bodyDamage = body->getDamageState();
- Int index = GARRISON_INDEX_INVALID;
- switch( bodyDamage )
- {
- // --------------------------------------------------------------------------------------------
- case BODY_PRISTINE:
- index = GARRISON_POINT_PRISTINE;
- break;
- // --------------------------------------------------------------------------------------------
- case BODY_DAMAGED:
- index = GARRISON_POINT_DAMAGED;
- break;
- // --------------------------------------------------------------------------------------------
- case BODY_REALLYDAMAGED:
- case BODY_RUBBLE:
- index = GARRISON_POINT_REALLY_DAMAGED;
- break;
- // --------------------------------------------------------------------------------------------
- default:
- DEBUG_CRASH(( "GarrisonContain::findConditionIndex - Unknown body damage type '%d'\n",
- bodyDamage ));
- break;
- } // end switch
- return index;
- } // end findConditionIndex
- //-------------------------------------------------------------------------------------------------
- //The weapon system would like to perform a range check assuming the object is placed in the
- //best possible available garrison position. If found, we change sourcePos to that position.
- //-------------------------------------------------------------------------------------------------
- Bool GarrisonContain::calcBestGarrisonPosition( Coord3D *sourcePos, const Coord3D *targetPos )
- {
- // sanity
- if( !sourcePos || !targetPos )
- return FALSE;
- // find which garrison point position array we will used based on body condition
- Int conditionIndex = findConditionIndex();
- // get the index of the garrison point that is closest to the target position
- Int placeIndex = findClosestFreeGarrisonPointIndex( conditionIndex, targetPos );
- if( placeIndex == GARRISON_INDEX_INVALID )
- {
- DEBUG_CRASH( ("GarrisonContain::calcBestGarrisonPosition - Unable to find suitable garrison point.\n") );
- return FALSE;
- }
- sourcePos->set( &(m_garrisonPoint[ conditionIndex ][ placeIndex ]) );
- return TRUE;
- }
- //-------------------------------------------------------------------------------------------------
- //The AI is entering the aim state and would like to move the unit to the best position, perform
- //a range check, and if it succeeds, leave him there -- otherwise, remove him immediately.
- //-------------------------------------------------------------------------------------------------
- Bool GarrisonContain::attemptBestFirePointPosition( Object *source, Weapon *weapon, Object *victim )
- {
- //Sanity
- if( !source || !victim || !weapon )
- {
- return FALSE;
- }
- //If this object is already at a garrison point, remove him.
- Int existingIndex = getObjectGarrisonPointIndex( source );
- if( existingIndex != GARRISON_INDEX_INVALID )
- {
- removeObjectFromGarrisonPoint( source, existingIndex );
- }
- putObjectAtBestGarrisonPoint( source, victim, NULL );
- //Okay, now we have positioned the object in the best position for the victim.
- //Now check if we are able to fire on our victim.
- if( weapon->isWithinAttackRange( source, victim ) )
- {
- return TRUE;
- }
- //Crap, we failed... so remove the object from the garrison point.
- existingIndex = getObjectGarrisonPointIndex( source );
- if( existingIndex != GARRISON_INDEX_INVALID )
- {
- removeObjectFromGarrisonPoint( source, existingIndex );
- }
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- //The AI is entering the aim state and would like to move the unit to the best position, perform
- //a range check, and if it succeeds, leave him there -- otherwise, remove him immediately.
- //-------------------------------------------------------------------------------------------------
- Bool GarrisonContain::attemptBestFirePointPosition( Object *source, Weapon *weapon, const Coord3D *targetPos )
- {
- //Sanity
- if( !source || !targetPos || !weapon )
- {
- return FALSE;
- }
- //If this object is already at a garrison point, remove him.
- Int existingIndex = getObjectGarrisonPointIndex( source );
- if( existingIndex != GARRISON_INDEX_INVALID )
- {
- removeObjectFromGarrisonPoint( source, existingIndex );
- }
- putObjectAtBestGarrisonPoint( source, NULL, targetPos );
- //Okay, now we have positioned the object in the best position for the targetPos.
- //Now check if we are able to fire on our targetPos.
- if( weapon->isWithinAttackRange( source, targetPos ) )
- {
- return TRUE;
- }
- //Crap, we failed... so remove the object from the garrison point.
- existingIndex = getObjectGarrisonPointIndex( source );
- if( existingIndex != GARRISON_INDEX_INVALID )
- {
- removeObjectFromGarrisonPoint( source, existingIndex );
- }
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- /** Place the object at the "best" garrison point position so it's on the same "side" of
- * the structure that its target is */
- //-------------------------------------------------------------------------------------------------
- void GarrisonContain::putObjectAtBestGarrisonPoint( Object *obj, Object *target, const Coord3D *targetPos )
- {
- // sanity
- if( obj == NULL || (target == NULL && targetPos == NULL) )
- return;
- // if obj target, override pos
- if (target != NULL)
- targetPos = target->getPosition();
- // if this object is already at a garrison point do nothing
- if( getObjectGarrisonPointIndex( obj ) != GARRISON_INDEX_INVALID )
- return;
- // find which garrison point position array we will used based on body condition
- Int conditionIndex = findConditionIndex();
- // get the index of the garrison point that is closest to the target position
- Int placeIndex = findClosestFreeGarrisonPointIndex( conditionIndex, targetPos );
- DEBUG_ASSERTCRASH( placeIndex != GARRISON_INDEX_INVALID,
- ("GarrisonContain::putObjectAtBestGarrisonPoint - Unable to find suitable garrison point for '%s'\n",
- obj->getTemplate()->getName().str()) );
- // put it here
- putObjectAtGarrisonPoint( obj, target ? target->getID() : INVALID_ID, conditionIndex, placeIndex );
- } // end putObjectAtBestGarrisonPoint
- // ------------------------------------------------------------------------------------------------
- /** Remove the object from the garrison point position and replace at the center of the building */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::removeObjectFromGarrisonPoint( Object *obj, Int index )
- {
-
- // sanity
- if( obj == NULL )
- return;
- // search for the object in the garrison point data, if found, remove it
- Int removeIndex = index;
- if( removeIndex == SEARCH_FOR_REMOVE )
- {
- for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
- {
- if( m_garrisonPointData[ i ].object == obj )
- {
- removeIndex = i;
- break;
- } // end if
- } // end for i
- } // end if
- // validate the index slot to remove
- if( removeIndex < 0 || removeIndex >= MAX_GARRISON_POINTS )
- {
- //
- // this is not an error, if a search was ordered, we may very well not find the
- // object at a garrison point and therefore can't remove it
- //
- return;
- } // end if
- // remove from this spot
- m_garrisonPointData[ removeIndex ].object = NULL;
- m_garrisonPointData[ removeIndex ].targetID = INVALID_ID;
- m_garrisonPointData[ removeIndex ].placeFrame = 0;
- m_garrisonPointData[ removeIndex ].lastEffectFrame = 0;
- --m_garrisonPointsInUse;
- // destroy drawable for gun barrel and effects if present
- if( m_garrisonPointData[ removeIndex ].effect )
- TheGameClient->destroyDrawable( m_garrisonPointData[ removeIndex ].effect );
- m_garrisonPointData[ removeIndex ].effect = NULL;
- // set the position of the object to back to the center of the garrisoned building
- obj->setPosition( getObject()->getPosition() );
- /*
- UnicodeString msg;
- msg.format( L"Removed object '%S'(%d) from point '%d'",
- obj->getTemplate()->getName().str(),
- obj->getID(),
- removeIndex );
- TheInGameUI->message( msg );
- */
- } // end removeObjectFromGarrisonPoint
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- GarrisonContain::GarrisonContain( Thing *thing, const ModuleData *moduleData ) :
- OpenContain( thing, moduleData )
- {
- Int i, j;
- m_originalTeam = NULL;
- m_hideGarrisonedStateFromNonallies = FALSE;
- m_garrisonPointsInUse = 0;
- m_garrisonPointsInitialized = FALSE;
- for( i = 0; i < MAX_GARRISON_POINTS; i++ )
- {
-
- m_garrisonPointData[ i ].object = NULL;
- m_garrisonPointData[ i ].targetID = INVALID_ID;
- m_garrisonPointData[ i ].placeFrame = 0;
- m_garrisonPointData[ i ].lastEffectFrame = 0;
- m_garrisonPointData[ i ].effect = NULL;
- for( j = 0; j < MAX_GARRISON_POINT_CONDITIONS; ++j )
- m_garrisonPoint[ j ][ i ].zero();
- } // end for i
- m_rallyValid = FALSE;
- m_exitRallyPoint.zero();
- } // end GarrisonContain
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- GarrisonContain::~GarrisonContain( void )
- {
- } // end ~GarrisonContain
- //-------------------------------------------------------------------------------------------------
- /** 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 GarrisonContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const
- {
- // extend functionality // this just tests kindof masks
- if( OpenContain::isValidContainerFor( obj, checkCapacity ) == false )
- return false;
- // zero-health buildings are not garrisonable.
- if (getObject()->getBodyModule()->getHealth() <= 0.0f)
- return false;
- // ReallyDamaged buildings are not garrisonable as well.
- if( getObject()->getBodyModule()->getDamageState() == BODY_REALLYDAMAGED && !getObject()->isKindOf( KINDOF_GARRISONABLE_UNTIL_DESTROYED ) )
- return false;
- if (obj && !obj->isKindOf(KINDOF_NO_GARRISON))
- {
- if (checkCapacity)
- {
- Int garrisonMax = getContainMax();
- Int containCount = getContainCount();
- return ( containCount < garrisonMax );
- }
- else
- {
- return true;
- }
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- /** Any objects that are sitting at the garrison points which no longer have targets need
- * to be moved to the center of the building and taken off the garrison point */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::removeInvalidObjectsFromGarrisonPoints( void )
- {
- Object *obj;
- if (m_garrisonPointsInUse == 0)
- return; // my, that was easy
- for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
- {
- obj = m_garrisonPointData[ i ].object;
- if( obj )
- {
- AIUpdateInterface *ai = obj->getAIUpdateInterface();
- Bool targetIsValid = true; // assume true for now...
- Object *goalObject = ai->getGoalObject();
- if( goalObject )
- {
- Weapon *weapon = obj->getCurrentWeapon();
- if( !weapon || !weapon->isWithinAttackRange( obj, goalObject ) )
- {
- //As a garrisoned member, if our target is out of range,
- //then get out of the space, because someone else might
- //be able to shoot it.
- targetIsValid = false;
- }
- }
-
- // note that we can be attacking a position, rather than an object...
- if( !obj->testStatus(OBJECT_STATUS_IS_ATTACKING) || !targetIsValid )
- {
- removeObjectFromGarrisonPoint( obj, i );
- }
- } // end if
- } // end for i
- } // end removeInvalidObjectsFromGarrisonPoints
- // ------------------------------------------------------------------------------------------------
- /** Are there any objects in the center that have now obtained targets and need to move to
- * a garrison point */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::addValidObjectsToGarrisonPoints( void )
- {
- const ContainedItemsList& containList = getContainList();
- if (containList.empty())
- return;
- for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
- {
- Object* obj = *it;
- AIUpdateInterface* ai = obj->getAIUpdateInterface();
- if( ai )
- {
- Object *victim = ai->getCurrentVictim();
- const Coord3D *victimPos = ai->getCurrentVictimPos();
-
- //
- // add this object to the garrison point that is closest to its target if it's not
- // already in there
- //
- if( victim )
- putObjectAtBestGarrisonPoint( obj, victim, NULL );
- else if( victimPos )
- putObjectAtBestGarrisonPoint( obj, NULL, victimPos );
- } // end if
- } // end for it
- } // end addValidObjectsToGarrisonPoints
- // ------------------------------------------------------------------------------------------------
- /** Every frame this method is called. It keeps any of the attacking units at any of the
- * fire points closest to their active target and shuffles them around to any open garrison
- * points that are available if they are closer. We will also track our targets position
- * and orient any effect stuff we need to (gun barrel / muzzle flash) */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::trackTargets( void )
- {
- Int conditionIndex = findConditionIndex();
- const ContainedItemsList& containList = getContainList();
- AIUpdateInterface *ai;
- Object *obj;
- for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
- {
- DEBUG_ASSERTCRASH(m_garrisonPointsInitialized, ("garrisonPoints are not inited"));
- // get the object
- obj = *it;
- // only consider objects that are actually at garrison points for re-shuffling
- Int ourIndex = getObjectGarrisonPointIndex( obj );
- if( ourIndex != GARRISON_INDEX_INVALID )
- {
- // does this object have a target?
- ai = obj->getAIUpdateInterface();
- if( ai )
- {
- Object *victim = ai->getCurrentVictim();
- // even though the target position can't change in some cases, still must do this code at least once.
- const Coord3D *victimPos = ai->getCurrentVictimPos();
- if( victim || victimPos )
- {
- if (victim)
- victimPos = victim->getPosition();
- const Coord3D *ourPos = obj->getPosition();
- // find the closest free (of all remaining) garrison points to our target
- Int newIndex = findClosestFreeGarrisonPointIndex( conditionIndex,
- victimPos );
- // if unable to find another garrison point, don't bother
- if( newIndex != GARRISON_INDEX_INVALID )
- {
- // get the distance from our current index to the target
- Real currentDistSq = calcDistSqr(*victimPos, *ourPos );
- // get the distance from the newly chosen index
- Real newDistSq = calcDistSqr(*victimPos, m_garrisonPoint[ conditionIndex ][ newIndex ] );
- // if the newly chosen index is closer than our current index, switch
- if( newDistSq < currentDistSq )
- {
- // remove from the old index
- removeObjectFromGarrisonPoint( obj, ourIndex );
- // place at the new index
- putObjectAtGarrisonPoint( obj, victim ? victim->getID() : INVALID_ID, conditionIndex, newIndex );
- } // end if, new index is closer
- } // end if, possible closer index was found
- //
- // we are now either at a new garrison fire point, or we have remained at our
- // existing point still tracking our target. Orient the effect drawable which
- // shows the gun barrel and muzzle flash towards our target position
- //
- if( m_garrisonPointData[ ourIndex ].effect )
- {
- Coord2D v;
- v.x = victimPos->x - ourPos->x;
- v.y = victimPos->y - ourPos->y;
- // v.z = victomPos->z - ourPos.z;
- // orient the effect object towards the victim position
- m_garrisonPointData[ ourIndex ].effect->setOrientation( v.toAngle() );
- } // end if
- } // end if, victim present
- } // end if, ai
- } // end if, we're at a garrison point
- } // end for it
- } // end trackTargets
- // ------------------------------------------------------------------------------------------------
- /** Remove all the objects at garrison points back to the center and redeploy them among the
- * garrison points. NOTE that we are preserving the frame in which the object was put
- * at the garrison point originally as this method is used when the model condition changes
- * which could shuffle the garrison point positions but that shouldn't logically change
- * when an object was placed at the point */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::redeployOccupants( void )
- {
- GarrisonPointData garrisonPointDataCopy[ MAX_GARRISON_POINTS ];
- Int i;
- // copy the current set of garrison point data sets
- for( i = 0; i < MAX_GARRISON_POINTS; ++i )
- garrisonPointDataCopy[ i ] = m_garrisonPointData[ i ];
- // remove the occupants
- removeInvalidObjectsFromGarrisonPoints();
- // redeploy them
- addValidObjectsToGarrisonPoints();
- // restore the frame markers that things were recorded as entering their point
- Int index;
- for( i = 0; i < MAX_GARRISON_POINTS; ++i )
- {
- if( garrisonPointDataCopy[ i ].object )
- {
- // where was this object redeployed
- index = getObjectGarrisonPointIndex( garrisonPointDataCopy[ i ].object );
- if( index != GARRISON_INDEX_INVALID )
- m_garrisonPointData[ index ].placeFrame = garrisonPointDataCopy[ i ].placeFrame;
- } // end if
- } // end for i
- } // end redeployOccupants
- // ------------------------------------------------------------------------------------------------
- /** Do any effects during an update cycle that we need to */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::updateEffects( void )
- {
- UnsignedInt currentFrame = TheGameLogic->getFrame();
- const ContainedItemsList& containList = getContainList();
- for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
- {
- Object *obj;
- // get the object
- obj = *it;
- //
- // did the object fire last frame, if so make a muzzle flash if needed at the
- // garrison point
- //
- if( obj->getLastShotFiredFrame() == currentFrame - 1 )
- {
- Int garrisonIndex = getObjectGarrisonPointIndex( obj );
- // only consider doing muzzle flash logic if the object is actually at a garrison point
- if( garrisonIndex != GARRISON_INDEX_INVALID )
- {
- // set the model condition for the effect object to show the muzzle flash
- Drawable *effect = m_garrisonPointData[ garrisonIndex ].effect;
- if( effect )
- {
-
- // set the model condition
- effect->setModelConditionState( MODELCONDITION_FIRING_A );
- // mark this "fire frame" so we can turn it off in a little while
- m_garrisonPointData[ garrisonIndex ].lastEffectFrame = currentFrame;
- } // end if
- } // end if, object is at garrision point
-
- } // end if, object shot last frame
- } // end for containment iterator
- // remove any firing effects for time that has passed
- for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
- {
- if( m_garrisonPointData[ i ].effect &&
- m_garrisonPointData[ i ].lastEffectFrame != 0 &&
- currentFrame - m_garrisonPointData[ i ].lastEffectFrame > MUZZLE_FLASH_LIFETIME )
- {
- // clear the model condition
- m_garrisonPointData[ i ].effect->clearModelConditionState( MODELCONDITION_FIRING_A );
- // clear the last effect frame
- m_garrisonPointData[ i ].lastEffectFrame = 0;
- } // end if
- } // end for i
- } // end updateEffects
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime GarrisonContain::update( void )
- {
- const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
- // extend functionality
- UpdateSleepTime result;
- result = OpenContain::update();
- // remove effectively dead objects from this garrison container
- const ContainedItemsList& containList = getContainList();
- Object *contained;
- for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); /*empty*/ )
- {
- // get object
- contained = *it;
- // increment iterator, we may delete the object
- ++it;
- // remove if dead
- if( contained->isEffectivelyDead() )
- {
- // remove from container
- removeFromContain( contained );
- // set the safe occlusion frame to way way way in the future so we never see it during death
- #define HUGE_FRAME_IN_FUTURE (LOGICFRAMES_PER_SECOND * 1000)
- contained->setSafeOcclusionFrame( TheGameLogic->getFrame() + HUGE_FRAME_IN_FUTURE );
- } // end if
- } // end for, it
-
- // are there any objects at the garrison points who now need to go back to the center of the structure
- removeInvalidObjectsFromGarrisonPoints();
- //
- // are there any objects in the center that have now obtained targets and need to move to
- // a garrison point
- //
- addValidObjectsToGarrisonPoints();
- // any units that have just fired need to have a muzzle flash display out of the fire point
- updateEffects();
- //
- // given all the objects that are at the garrison points shooting at something, if their
- // target moves around the structure and closer to another open garrison point we want
- // to shuffle our object to the new closest garrison point. We'll also track the target
- // here and set orientation for any effects we need to
- //
- trackTargets();
- healObjects();
- if (modData->m_mobileGarrison && (getObject()->isMobile() == TRUE) )
- {
- moveObjectsWithMe();
- }
- else
- {
- // sanity information
- DEBUG_ASSERTCRASH( getObject()->isMobile() == FALSE,
- ("GarrisonContain::update - Objects with garrison contain can be spec'd as 'mobile' in the INI. Do you really want to do this? \n") );
- }
- return UPDATE_SLEEP_NONE;
- } // end update
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void GarrisonContain::healObjects( void )
- {
- const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
- if (!modData->m_doIHealObjects)
- return;
- const ContainedItemsList& containList = getContainList();
- for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
- {
- Object *obj;
- // get the object
- obj = *it;
- healSingleObject(obj, modData->m_framesForFullHeal);
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void GarrisonContain::healSingleObject( Object *obj, Real framesForFullHeal)
- {
- // 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
- }
- //-------------------------------------------------------------------------------------------------
- /** return the player that *appears* to control this unit. if null,
- use getObject()->getControllingPlayer() instead. */
- // ------------------------------------------------------------------------------------------------
- const Player* GarrisonContain::getApparentControllingPlayer( const Player* observingPlayer ) const
- {
- const Player* myPlayer = getObject()->getControllingPlayer();
- if ( m_hideGarrisonedStateFromNonallies && m_originalTeam && myPlayer && observingPlayer )
- {
- Relationship r = myPlayer->getRelationship(observingPlayer->getDefaultTeam());
- if (r != ALLIES)
- return m_originalTeam->getControllingPlayer();
- }
- return myPlayer;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void GarrisonContain::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;
- // Check to see if we have any units contained in our object
- if( getContainCount() > 0 )
- {
- ContainedItemsList::const_iterator it = getContainList().begin();
- Object *rider = *it;
- // Check to see if all the contained units are stealthy. Need to set this flag before the capture,
- // since the Radar refresh in setTeam will want to use it to decide our color.
- Bool detected = ( rider->getStatusBits() & OBJECT_STATUS_DETECTED );
- m_hideGarrisonedStateFromNonallies = ( !detected && ( getStealthUnitsContained() == getContainCount() ) );
-
- Player* controller = rider->getControllingPlayer();
- Team *team = controller ? controller->getDefaultTeam() : NULL;
- if( team )
- {
- getObject()->setTeam( team );
- }
- }
- else
- {
- //Nothing in object, so set team to original team.
- getObject()->setTeam( m_originalTeam );
- m_hideGarrisonedStateFromNonallies = false;
- }
- //Only allow the garrison state to be set if the client team knows that it is garrisoned.
- Drawable *draw = getObject()->getDrawable();
- if( draw )
- {
- Bool setModelGarrisoned = FALSE;
- if ( getContainCount() > 0 )
- {
- ContainedItemsList::const_iterator it = getContainList().begin();
- Object *occupant = *it;
- Bool detected = ( occupant->getStatusBits() & OBJECT_STATUS_DETECTED );
- if( detected || (getApparentControllingPlayer(ThePlayerList->getLocalPlayer()) == getObject()->getControllingPlayer()) )
- {
- setModelGarrisoned = TRUE;
- }
- }
- if ( setModelGarrisoned )
- draw->setModelConditionState( MODELCONDITION_GARRISONED );
- else
- draw->clearModelConditionState( MODELCONDITION_GARRISONED );
- // Handle the team color that is rendered
- const Player* controller = getApparentControllingPlayer(ThePlayerList->getLocalPlayer());
- if (controller)
- {
- if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
- draw->setIndicatorColor( controller->getPlayerNightColor() );
- else
- draw->setIndicatorColor( controller->getPlayerColor() );
- }
- // now that we have an object inside us, we need to get all the garrison point positions
- // if we don't already have them.
- if( getContainCount() > 0 && m_garrisonPointsInitialized == FALSE )
- {
- loadGarrisonPoints();
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Load the garrison point position data and save for use later */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::loadGarrisonPoints( void )
- {
- const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
- Object *structure = getObject();
- Int i, j;
- Bool gBonesFound = FALSE;
- //
- // initialize all the garrison points to the center of the object, this assumes that we
- // will never move (these points are cached)
- //
- for( i = 0; i < MAX_GARRISON_POINT_CONDITIONS; ++i )
- for( j = 0; j < MAX_GARRISON_POINTS; ++j )
- m_garrisonPoint[ i ][ j ] = *(structure->getPosition());
- //
- // in order to get all the garrison point positions we will actually switch the model
- // condition through the garrisoned pristine, damaged, and really damaged states. This
- // is especially important because at the time or "garrisoning" a model condition may
- // not actually switch to a garrisoned state because of units that garrison things
- // all stealthy. By caching all the bone positions once we will have access to
- // any of the points for any condition state at any time
- //
- {
- Int conditionIndex;
- Int count = 0;
- // save the original paramters for the model condition
- Drawable* draw = structure->getDrawable();
- const ModelConditionFlags originalFlags = draw->getModelConditionFlags();
- ModelConditionFlags clearFlags;
- ModelConditionFlags setFlags;
- // pristine garrisoned
- clearFlags.clear();
- setFlags.clear();
- clearFlags.set( MODELCONDITION_REALLY_DAMAGED );
- clearFlags.set( MODELCONDITION_RUBBLE );
- clearFlags.set( MODELCONDITION_SPECIAL_DAMAGED );
- clearFlags.set( MODELCONDITION_DAMAGED );
- setFlags.set( MODELCONDITION_GARRISONED );
- structure->clearAndSetModelConditionFlags( clearFlags, setFlags );
- conditionIndex = GARRISON_POINT_PRISTINE;
- count = structure->getMultiLogicalBonePosition("FIREPOINT", MAX_GARRISON_POINTS, m_garrisonPoint[ conditionIndex ], NULL);
-
- if ( count > 0) gBonesFound = TRUE;
- // damaged garrisoned
- clearFlags.clear();
- setFlags.clear();
- clearFlags.set( MODELCONDITION_REALLY_DAMAGED );
- clearFlags.set( MODELCONDITION_RUBBLE );
- clearFlags.set( MODELCONDITION_SPECIAL_DAMAGED );
- setFlags.set( MODELCONDITION_DAMAGED );
- structure->clearAndSetModelConditionFlags( clearFlags, setFlags );
- conditionIndex = GARRISON_POINT_DAMAGED;
- count = structure->getMultiLogicalBonePosition("FIREPOINT", MAX_GARRISON_POINTS, m_garrisonPoint[ conditionIndex ], NULL);
- if ( count > 0) gBonesFound = TRUE;
- // really damaged garrisoned
- clearFlags.clear();
- setFlags.clear();
- clearFlags.set( MODELCONDITION_RUBBLE );
- clearFlags.set( MODELCONDITION_SPECIAL_DAMAGED );
- clearFlags.set( MODELCONDITION_DAMAGED );
- setFlags.set( MODELCONDITION_REALLY_DAMAGED );
- structure->clearAndSetModelConditionFlags( clearFlags, setFlags );
- conditionIndex = GARRISON_POINT_REALLY_DAMAGED;
- count = structure->getMultiLogicalBonePosition("FIREPOINT", MAX_GARRISON_POINTS, m_garrisonPoint[ conditionIndex ], NULL);
- if ( count > 0) gBonesFound = TRUE;
- // restore the original condition flags
- draw->replaceModelConditionFlags( originalFlags );
-
- } // end if, draw
- // garrison points are now initialized
- m_garrisonPointsInitialized = TRUE;
- if (gBonesFound && modData->m_mobileGarrison && (getObject()->isMobile() == TRUE) )
- {
- DEBUG_ASSERTCRASH( getObject()->isMobile() == FALSE,
- ("GarrisonContain::update - You have specified this garrisonContain as mobile,\n yet you want garrison point placement bones... \n what are you thinking?") );
- }
- } // end loadGarrisonPoints
- // ------------------------------------------------------------------------------------------------
- /** Validate any exit rally point that has been chosen (if any). If it's not valid,
- * try to find a new one */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::validateRallyPoint( void )
- {
- // if we have a rally point already picked, make sure it's valid
- if( m_rallyValid == TRUE )
- {
- Coord3D result;
- FindPositionOptions options;
- // ask for a valid position exactly at the rally point
- options.flags = FPF_IGNORE_ALLY_OR_NEUTRAL_UNITS;
- options.minRadius = 0.0f;
- options.maxRadius = 0.0f;
- options.ignoreObject = getObject();
- options.relationshipObject = getObject();
- if( ThePartitionManager->findPositionAround( &m_exitRallyPoint, &options, &result ) == FALSE )
- m_rallyValid = FALSE;
- } // end if
- // if no rally point is present, try to find one
- if( m_rallyValid == FALSE )
- {
- FindPositionOptions options;
- // pick a location for everybody to rally at
- options.flags = FPF_IGNORE_ALLY_OR_NEUTRAL_UNITS;
- options.minRadius = getObject()->getGeometryInfo().getBoundingCircleRadius();
- options.maxRadius = options.minRadius * 1.8f; // arbitrary max distance away, change as needed
- options.ignoreObject = getObject();
- options.relationshipObject = getObject();
- m_rallyValid = ThePartitionManager->findPositionAround( getObject()->getPosition(),
- &options,
- &m_exitRallyPoint );
- } // end if
- } // end validateRallyPoint
- // ------------------------------------------------------------------------------------------------
- /** Remove all contents of this container. We will try to do so with intelligent garrison
- * logic, but if all else fails no matter, we need to get all things out after this
- * call is complete */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::removeAllContained( Bool exposeStealthUnits )
- {
- //
- // we will call this when we are destroying the object (either normally or through a game
- // reset/exit etc) *and* also when we have received an "evacuate" command from the player.
- // We will attempt to find a spot for all the contents to scatter to that is around the
- // structure, but if such a spot is not found, we'll just scatter in a random direction
- // from the building.
- //
- // only even bother doing this if we have contents inside us just cause it's a waste
- if( getContainCount() > 0 )
- {
- // validate the current rally point is still a good one, or pick a new one
- validateRallyPoint();
- } // end if
- // call the base class to extend functionality and do the actual removal
- OpenContain::removeAllContained( exposeStealthUnits );
- recalcApparentControllingPlayer();
- } // end removeAllContained
- // ------------------------------------------------------------------------------------------------
- /** 'exitObj' is one of the things we contain, it needs to 'exit' us */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::exitObjectViaDoor( Object *exitObj, ExitDoorType exitDoor )
- {
- DEBUG_ASSERTCRASH(exitDoor == DOOR_1, ("multiple exit doors not supported here"));
- // We don't use the ExitPath system of the general OpenContain, we just send people out. The
- // direction of outing has been picked by Design to be the Screen Down at the default camera angle.
- removeFromContain( exitObj );
- Coord3D startPosition;
- Coord3D endPosition;
- Real exitAngle = getObject()->getOrientation();
- // Garrison doesn't have reserveDoor or exitDelay, so if we do nothing, everyone will appear on top
- // of each other and get stuck inside each others' extent (except for the first guy). So we'll
- // scatter the start point around a little to make it better.
- startPosition = *getObject()->getPosition();
- // In the case of cliff bunkers, the units start in a cliff. So we want to adjust.
- AIUpdateInterface *ai = exitObj->getAI();
- if (ai) {
- Locomotor *loco = ai->getCurLocomotor();
- if (loco && !TheAI->pathfinder()->validMovementTerrain( LAYER_GROUND, loco, &startPosition)) {
- // try front & back.
- Real offset = getObject()->getGeometryInfo().getMajorRadius();
- startPosition.x -= offset*Cos(exitAngle);
- startPosition.y -= offset*Sin(exitAngle);
- if (!TheAI->pathfinder()->validMovementTerrain(LAYER_GROUND, loco, &startPosition)) {
- startPosition.x += 2*offset*Cos(exitAngle);
- startPosition.y += 2*offset*Sin(exitAngle);
- if (!TheAI->pathfinder()->validMovementTerrain(LAYER_GROUND, loco, &startPosition)) {
- startPosition = *getObject()->getPosition();
- }
- }
- }
- }
- exitObj->setPosition( &startPosition );
- exitObj->setOrientation( exitAngle );
- ///< @todo This really should be automatically wrapped up in an activation sequence for objects in general
- // tell the AI about it
- TheAI->pathfinder()->addObjectToPathfindMap( exitObj );
- endPosition = startPosition;
- if( ai )
- {
- TheAI->pathfinder()->adjustToPossibleDestination(exitObj, ai->getLocomotorSet(), &endPosition);
- std::vector<Coord3D> exitPath;
- exitPath.push_back(endPosition);
- ai->aiFollowPath( &exitPath, getObject(), CMD_FROM_AI );
- TheAI->pathfinder()->updateGoal(exitObj, &endPosition, TheTerrainLogic->getLayerForDestination(&endPosition));
- }
- recalcApparentControllingPlayer();
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void GarrisonContain::onContaining( Object *obj )
- {
- // extend base class
- OpenContain::onContaining( obj );
- // get the structure object
- Object *structure = getObject();
- // objects inside a building are held
- obj->setDisabled( DISABLED_HELD );
- // the building can now attack, since it has soldiers inside of it
- structure->setStatus( OBJECT_STATUS_CAN_ATTACK );
- // give the object a garrisoned version of its weapon
- obj->setWeaponBonusCondition( WEAPONBONUSCONDITION_GARRISONED );
- // put the object in the center of the building
- obj->setPosition( structure->getPosition() );
- //
- // 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();
- } // end onContaining
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void GarrisonContain::onRemoving( Object *obj )
- {
- OpenContain::onRemoving(obj);
- // first remove the object from any garrison fire point if it's at one
- removeObjectFromGarrisonPoint( obj );
- // give the object back a regular weapon
- obj->clearWeaponBonusCondition( WEAPONBONUSCONDITION_GARRISONED );
- // object is no longer held inside a garrisoned building
- obj->clearDisabled( DISABLED_HELD );
- // if we have nothing left inside of us then we are once again back to our original team
- 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)
- {
- getObject()->setTeam( m_originalTeam );
- m_originalTeam = NULL;
- }
- // we also lose our transient attack ability
- getObject()->clearStatus( OBJECT_STATUS_CAN_ATTACK );
- m_hideGarrisonedStateFromNonallies = false;
- // change the state back from garrisoned
- Drawable *draw = getObject()->getDrawable();
- if( draw )
- {
- draw->clearModelConditionState( MODELCONDITION_GARRISONED );
- }
- } // end if
- else if( getStealthUnitsContained() != getContainCount() )
- {
- m_hideGarrisonedStateFromNonallies = false;
- }
- // disable occlusion while the unit walks out of the building
- ///@todo: we should probably not draw the unit to begin with and just have them pop out.
- obj->setSafeOcclusionFrame(TheGameLogic->getFrame()+obj->getTemplate()->getOcclusionDelay());
- recalcApparentControllingPlayer();
- } // end onRemoving
- // ------------------------------------------------------------------------------------------------
- /** A GarrisonContain always lets people shoot out */
- // ------------------------------------------------------------------------------------------------
- Bool GarrisonContain::isPassengerAllowedToFire() const
- {
- return TRUE;
- } // end isPassengerAllowedToFire
- // ------------------------------------------------------------------------------------------------
- /** A Mobile garrison keeps its occupants with it when it moves */
- //-------------------------------------------------------------------------------------------------
- void GarrisonContain::moveObjectsWithMe( void )
- {
- const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
- if (!modData->m_mobileGarrison)
- return;
- const ContainedItemsList& containList = getContainList();
- for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
- {
- Object *obj;
- // get the object
- obj = *it;
- obj->setPosition(getObject()->getPosition());
- }
- }
- //-------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::onBodyDamageStateChange( const DamageInfo* , BodyDamageType , BodyDamageType newState )
- {
- // This is an event triggered on edge, so we just need to look at the newState. We know a change happened.
- // And we don't need to check Rubble, since death exiting is already handled. This is Garrison specific.
- if( newState == BODY_REALLYDAMAGED && !getObject()->isKindOf( KINDOF_GARRISONABLE_UNTIL_DESTROYED ) )
- {
- if( getContainCount() > 0 )
- orderAllPassengersToExit(CMD_FROM_AI);
- }
- }
- //-------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::onObjectCreated()
- {
- GarrisonContainModuleData* self = (GarrisonContainModuleData*)getGarrisonContainModuleData();
- Int count = self->m_initialRoster.count;
- const ThingTemplate* rosterTemplate = TheThingFactory->findTemplate( self->m_initialRoster.templateName );
- Object* object = getObject();
- for( int i = 0; i < count; i++ )
- {
- //We are creating a garrison that comes with an initial roster, so add it now!
- Object* payload = TheThingFactory->newObject( rosterTemplate, object->getControllingPlayer()->getDefaultTeam() );
- if( object->getContain() && object->getContain()->isValidContainerFor( payload, true ) )
- {
- object->getContain()->addToContain( payload );
- }
- else
- {
- DEBUG_CRASH( ( "DeliverPayload: PutInContainer %s is full, or not valid for the payload %s!",
- object->getName().str(), self->m_initialRoster.templateName.str() ) );
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::crc( Xfer *xfer )
- {
- // extend base class
- OpenContain::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::xfer( Xfer *xfer )
- {
- Int i;
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- OpenContain::xfer( xfer );
- // 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(( "GarrisonContain::xfer - Unable to find original team by id\n" ));
- throw SC_INVALID_DATA;
- } // end if
- } // end if
- else
- m_originalTeam = NULL;
- } // end if
- xfer->xferBool( &m_hideGarrisonedStateFromNonallies );
- // garrison point data
- UnsignedShort pointDataCount = MAX_GARRISON_POINTS;
- xfer->xferUnsignedShort( &pointDataCount );
- for( i = 0; i < pointDataCount; ++i )
- {
- if( xfer->getXferMode() == XFER_SAVE )
- {
- // object at this point
- Object *obj = m_garrisonPointData[ i ].object;
- ObjectID objectID = obj ? obj->getID() : INVALID_ID;
- xfer->xferObjectID( &objectID );
- // target
- xfer->xferObjectID( &m_garrisonPointData[ i ].targetID );
-
- // placement frame
- xfer->xferUnsignedInt( &m_garrisonPointData[ i ].placeFrame );
- // last effect frame
- xfer->xferUnsignedInt( &m_garrisonPointData[ i ].lastEffectFrame );
- // effect drawable id
- Drawable *draw = m_garrisonPointData[ i ].effect;
- DrawableID drawableID = draw ? draw->getID() : INVALID_DRAWABLE_ID;
- xfer->xferDrawableID( &drawableID );
- } // end if, save
- else
- {
- // objectID
- ObjectID objectID;
- xfer->xferObjectID( &objectID );
- // target
- ObjectID targetID;
- xfer->xferObjectID( &targetID );
-
- // placement frame
- UnsignedInt placeFrame;
- xfer->xferUnsignedInt( &placeFrame );
- // last effect frame
- UnsignedInt lastEffectFrame;
- xfer->xferUnsignedInt( &lastEffectFrame );
- // effect drawable id
- DrawableID drawableID;
- xfer->xferDrawableID( &drawableID );
- // store
- if( i < MAX_GARRISON_POINTS )
- {
- m_garrisonPointData[ i ].objectID = objectID;
- m_garrisonPointData[ i ].targetID = targetID;
- m_garrisonPointData[ i ].placeFrame = placeFrame;
- m_garrisonPointData[ i ].lastEffectFrame = lastEffectFrame;
- m_garrisonPointData[ i ].effectID = drawableID;
- } // end if
- } // end else, load
- } // end for i
- // garrison points in use
- xfer->xferInt( &m_garrisonPointsInUse );
- // garrison points
- xfer->xferUser( m_garrisonPoint, sizeof( Coord3D ) * MAX_GARRISON_POINT_CONDITIONS * MAX_GARRISON_POINTS );
- // garrison points initialized
- xfer->xferBool( &m_garrisonPointsInitialized );
- // rally valid
- xfer->xferBool( &m_rallyValid );
- // exit rally point
- xfer->xferCoord3D( &m_exitRallyPoint );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void GarrisonContain::loadPostProcess( void )
- {
- // extend base class
- OpenContain::loadPostProcess();
- // connect up pointers needed
- for( Int i = 0; i < MAX_GARRISON_POINTS; i++ )
- {
- // object pointer
- if( m_garrisonPointData[ i ].objectID != INVALID_ID )
- {
- m_garrisonPointData[ i ].object = TheGameLogic->findObjectByID( m_garrisonPointData[ i ].objectID );
- if( m_garrisonPointData[ i ].object == NULL )
- {
- DEBUG_CRASH(( "GarrisonContain::loadPostProcess - Unable to find object for point data\n" ));
- throw SC_INVALID_DATA;
- } // end if
- } // end if
- else
- m_garrisonPointData[ i ].object = NULL;
- // drawable effect pointer
- if( m_garrisonPointData[ i ].effectID != INVALID_ID )
- {
- m_garrisonPointData[ i ].effect = TheGameClient->findDrawableByID( m_garrisonPointData[ i ].effectID );
- if( m_garrisonPointData[ i ].effect == NULL )
- {
- DEBUG_CRASH(( "GarrisonContain::loadPostProcess - Unable to find effect for point data\n" ));
- throw SC_INVALID_DATA;
- } // end if
- } // end if
- else
- m_garrisonPointData[ i ].effect = NULL;
- } // end for i
- } // end loadPostProcess
|