| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505 |
- /*
- ** 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: DozerAIUpdate.cpp ////////////////////////////////////////////////////////////////////////
- // Author: Colin Day, February 2002
- // Desc: Dozer AI behavior
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/ActionManager.h"
- #include "Common/Team.h"
- #include "Common/StateMachine.h"
- #include "Common/BuildAssistant.h"
- #include "Common/ThingTemplate.h"
- #include "Common/ThingFactory.h"
- #include "Common/Player.h"
- #include "Common/Money.h"
- #include "Common/Radar.h"
- #include "Common/RandomValue.h"
- #include "Common/GameState.h"
- #include "Common/GlobalData.h"
- #include "Common/Xfer.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/GameText.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/BridgeBehavior.h"
- #include "GameLogic/Module/BridgeTowerBehavior.h"
- #include "GameLogic/Module/CreateModule.h"
- #include "GameLogic/Module/DozerAIUpdate.h"
- #include "GameClient/InGameUI.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- // FORWARD DECLARATIONS ///////////////////////////////////////////////////////////////////////////
- class DozerPrimaryStateMachine;
- class DozerActionStateMachine;
- static const Real MIN_ACTION_TOLERANCE = 70.0f;
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- /** Available Dozer actions */
- //-------------------------------------------------------------------------------------------------
- enum DozerActionType
- {
- DOZER_ACTION_PICK_ACTION_POS, ///< pick a location "around" the target to do our action
- DOZER_ACTION_MOVE_TO_ACTION_POS,///< move to our action pos we've picked
- DOZER_ACTION_DO_ACTION, ///< do our action at the position, build/repair/etc ...
- };
- //-------------------------------------------------------------------------------------------------
- /** Dozer picks a position around the target to do the action */
- //-------------------------------------------------------------------------------------------------
- class DozerActionPickActionPosState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerActionPickActionPosState, "DozerActionPickActionPosState")
- public:
- DozerActionPickActionPosState( StateMachine *machine, DozerTask task );
- virtual StateReturnType update( void );
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer );
- virtual void xfer( Xfer *xfer );
- virtual void loadPostProcess();
- protected:
-
- DozerTask m_task; ///< our task
- Int m_failedAttempts; /**< counter for successive unsuccessfull attempts to pick
- and move to an action position */
- };
- EMPTY_DTOR(DozerActionPickActionPosState)
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- DozerActionPickActionPosState::DozerActionPickActionPosState( StateMachine *machine,
- DozerTask task ) :
- State( machine, "DozerActionPickActionPosState" )
- {
- m_task = task;
- m_failedAttempts = 0;
- } // end DozerActionPickActionPosState
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void DozerActionPickActionPosState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void DozerActionPickActionPosState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferUser(&m_task, sizeof(m_task));
- xfer->xferInt(&m_failedAttempts);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void DozerActionPickActionPosState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- /** Pick a position around the target */
- //-------------------------------------------------------------------------------------------------
- StateReturnType DozerActionPickActionPosState::update( void )
- {
- StateMachine *machine = getMachine();
- Object *dozer = machine->getOwner();
- // get dozer ai update interface
- AIUpdateInterface *ai = dozer->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- DozerAIInterface *dozerAI = ai->getDozerAIInterface();
- if( !dozerAI )
- {
- return STATE_FAILURE;
- }
- // get the object we're concerned with for our task
- Object *goalObject = TheGameLogic->findObjectByID( dozerAI->getTaskTarget( m_task ) );
- // if there is no goal, get out of this machine with a failure code (success is done in the action state )
- if( goalObject == NULL )
- {
- // to be clean get rid of the goal object we set
- getMachine()->setGoalObject( NULL );
- // cancel our task
- dozerAI->cancelTask( m_task );
- // exit with failure
- return STATE_FAILURE;
- } // end if
- // pick a location to move to
- Coord3D goalPos;
- const Coord3D *pos = dozerAI->getDockPoint( m_task, DOZER_DOCK_POINT_START );
- if( pos )
- goalPos = *pos;
- else
- {
- // pick a spot to use
- //
- // find the vector from goal object to us ... we will start our search on this angle so
- // that we "approach" a closer point rather than a point on a random side
- //
- Coord2D v;
- v.x = dozer->getPosition()->x - goalObject->getPosition()->x;
- v.y = dozer->getPosition()->y - goalObject->getPosition()->y;
-
- Real radius = goalObject->getGeometryInfo().getBoundingSphereRadius();
- FindPositionOptions fpOptions;
- fpOptions.minRadius = radius;
- fpOptions.maxRadius = radius;
- fpOptions.startAngle = v.toAngle();
- if( ThePartitionManager->findPositionAround( goalObject->getPosition(),
- &fpOptions,
- &goalPos ) == FALSE )
- {
- // return STATE_FAILURE; no, we don't ever want dozers to fail, particularly
- // if ai.
- goalPos = *goalObject->getPosition();
- } // end if
- //
- // we only ignore the goal object when we did the point selection in this more
- // "random" method cause we could pick a place inside the object
- //
- ai->ignoreObstacle( goalObject );
- } // end if
- // set goal position and object
- machine->setGoalObject( goalObject );
- machine->setGoalPosition( &goalPos );
- ai->ignoreObstacle(goalObject);
- ai->aiMoveToPosition( &goalPos, CMD_FROM_AI );
- return STATE_SUCCESS;
- } // end update
- //-------------------------------------------------------------------------------------------------
- /** Dozer moves to the action position */
- //-------------------------------------------------------------------------------------------------
- class DozerActionMoveToActionPosState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerActionMoveToActionPosState, "DozerActionMoveToActionPosState")
- public:
- DozerActionMoveToActionPosState( StateMachine *machine, DozerTask task ) : State( machine, "DozerActionMoveToActionPosState" ) { m_task = task; }
- virtual StateReturnType update( void );
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer );
- virtual void xfer( Xfer *xfer );
- virtual void loadPostProcess();
- protected:
- DozerTask m_task; ///< our task
- };
- EMPTY_DTOR(DozerActionMoveToActionPosState)
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void DozerActionMoveToActionPosState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void DozerActionMoveToActionPosState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferUser(&m_task, sizeof(m_task));
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void DozerActionMoveToActionPosState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- /** We are supposed to be on route to our action position now, see when we get there or
- * detect that we have encountered a problem that's going to cause it to give up */
- //-------------------------------------------------------------------------------------------------
- StateReturnType DozerActionMoveToActionPosState::update( void )
- {
- Object *goalObject = getMachineGoalObject();
- Object *dozer = getMachineOwner();
- // sanity
- if( goalObject == NULL || dozer == NULL )
- return STATE_FAILURE;
- AIUpdateInterface *ai = dozer->getAIUpdateInterface();
- ObjectID currentRepairer = goalObject->getSoleHealingBenefactor();
- if( m_task==DOZER_TASK_REPAIR && currentRepairer != INVALID_ID && currentRepairer != dozer->getID() )//oops I guess someone beat me to it!
- {
- if ( ai )
- {
- DozerAIInterface *dozerAI = ai->getDozerAIInterface();
- if ( dozerAI )
- dozerAI->internalTaskComplete( m_task );
- }
- getMachine()->setGoalObject( NULL );
- return STATE_FAILURE;
- }
- // Double oops, someone else has taken over the build responsibility for this building. We can't double up.
- // This can happen because a guy who is moving to build who is told to build will idle for a frame after registering.
- // Two workers, two started buildings.
- // Grab both workers, resume on one building
- // First will register, second will see first as active and say no
- // Grab both, click on other building
- // First will register, and idle for a frame, second will see first as not active and say yes
- // Next frame, first will start the build task, without reasking validity
- // Infinite number of workers can be told to build something with just two in progress buildings
- if( (m_task == DOZER_TASK_BUILD) && goalObject && (goalObject->getBuilderID() != dozer->getID()) )
- {
- // Geebus. Returning failure is ignored, so you have to explicitly make the ai stop trying to restart the machine
- if ( ai )
- {
- DozerAIInterface *dozerAI = ai->getDozerAIInterface();
- if ( dozerAI )
- dozerAI->internalTaskComplete( m_task );
- }
- getMachine()->setGoalObject( NULL );
- return STATE_FAILURE;
- }
- // if distance between us and our goal position is close enough
- const Coord3D *goalPos = getMachine()->getGoalPosition();
- Real distSqr = ThePartitionManager->getDistanceSquared( dozer, goalPos, FROM_BOUNDINGSPHERE_2D );
- const Real SLOP = 15.0f;
- Real allowableDistanceSqr = sqr(max( MIN_ACTION_TOLERANCE, dozer->getGeometryInfo().getBoundingSphereRadius() + SLOP ));
- if( distSqr <= allowableDistanceSqr )
- {
- if( m_task == DOZER_TASK_BUILD )
- {
- //
- // the object is now no longer awaiting construction, it is being constructed ... note
- // that we might possibly be here multiple times for a single object being built
- // but the setting of the same model condition doesn't have any adverse effects
- //
- goalObject->clearAndSetModelConditionFlags(
- MAKE_MODELCONDITION_MASK(MODELCONDITION_AWAITING_CONSTRUCTION),
- MAKE_MODELCONDITION_MASK2(MODELCONDITION_PARTIALLY_CONSTRUCTED, MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED));
- } // end if
- return STATE_SUCCESS;
- } // end if
- // if we're in the idle state fail our move
- // Failure transition is back to DOZER_ACTION_PICK_ACTION_POS, so
- // it is ok to fail. jba.
- if( ai && ai->isIdle() )
- return STATE_FAILURE;
- return STATE_CONTINUE;
- } // end update
- //-------------------------------------------------------------------------------------------------
- /** Dozer does the "action" */
- //-------------------------------------------------------------------------------------------------
- class DozerActionDoActionState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerActionDoActionState, "DozerActionDoActionState")
- public:
- DozerActionDoActionState( StateMachine *machine, DozerTask task ) : State( machine, "DozerActionDoActionState" )
- {
- m_task = task;
- //Added By Sadullah Nader
- // Initializations missing and needed
- m_enterFrame = 0;
- }
- virtual StateReturnType update( void );
- virtual StateReturnType onEnter( void );
- virtual void onExit( StateExitType status ) { }
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer );
- virtual void xfer( Xfer *xfer );
- virtual void loadPostProcess();
- protected:
-
- DozerTask m_task; ///< our task
- UnsignedInt m_enterFrame; ///< frame we entered this state on
- };
- EMPTY_DTOR(DozerActionDoActionState)
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void DozerActionDoActionState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void DozerActionDoActionState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferUser(&m_task, sizeof(m_task));
- xfer->xferUnsignedInt(&m_enterFrame);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void DozerActionDoActionState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- /** Entering the do action state */
- //-------------------------------------------------------------------------------------------------
- StateReturnType DozerActionDoActionState::onEnter( void )
- {
- Object *dozer = getMachineOwner();
- DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
- if( !dozerAI )
- {
- return STATE_FAILURE;
- }
- // DozerAIUpdate *dozerAI = static_cast<DozerAIUpdate *>(dozer->getAIUpdateInterface());
-
- // record the frame we came in on
- m_enterFrame = TheGameLogic->getFrame();
- // when building, we have additional movement that we will for docking
- if( m_task == DOZER_TASK_BUILD )
- dozerAI->setBuildSubTask( DOZER_SELECT_BUILD_DOCK_LOCATION );
- return STATE_CONTINUE;
- } // end onEnter
- //-------------------------------------------------------------------------------------------------
- /** Do the action */
- //-------------------------------------------------------------------------------------------------
- StateReturnType DozerActionDoActionState::update( void )
- {
- Object *goalObject = getMachineGoalObject();
- Object *dozer = getMachineOwner();
- AIUpdateInterface *ai = dozer->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- DozerAIInterface *dozerAI = ai->getDozerAIInterface();
- if( !dozerAI )
- {
- return STATE_FAILURE;
- }
- // DozerAIUpdate *dozerAI = static_cast<DozerAIUpdate *>(dozer->getAIUpdateInterface());
- // const UnsignedInt ACTION_TIME = LOGICFRAMES_PER_SECOND * 4 ; // frames to spend here in this state doing the action
- // check for object gone
- if( goalObject == NULL )
- return STATE_FAILURE;
- if ( dozer->isDisabledByType( DISABLED_UNMANNED ) )// Yipes, I've been sniped!
- return STATE_FAILURE;
- // do the task
- Bool complete = FALSE;
- switch( m_task )
- {
- //---------------------------------------------------------------------------------------------
- case DOZER_TASK_BUILD:
- {
- //GS Moved this inside Build, since you are allowed to Repair things that are not your player (canRepairObject handles it)
- if (dozer->getControllingPlayer() != goalObject->getControllingPlayer())//Yipes, SOmehow I have changed sides in mid build!
- return STATE_FAILURE;
- // if we need to select the dock location and move there do so
- if( dozerAI->getBuildSubTask() == DOZER_SELECT_BUILD_DOCK_LOCATION )
- {
- const Coord3D *dockLocation = dozerAI->getDockPoint( m_task, DOZER_DOCK_POINT_ACTION );
- if( dockLocation )
- ai->aiMoveToPosition( dockLocation, CMD_FROM_AI );
- // we're now moving to the dock location
- dozerAI->setBuildSubTask( DOZER_MOVING_TO_BUILD_DOCK_LOCATION );
- } // end if
-
- // if we're moving to the build dock location, when we become idle we are there
- if( dozerAI->getBuildSubTask() == DOZER_MOVING_TO_BUILD_DOCK_LOCATION )
- {
- if( ai->isIdle() )
- {
- dozerAI->setBuildSubTask( DOZER_DO_BUILD_AT_DOCK );
- // Get the audio sound and start playing the construction sound (get the sound
- // from the building itself)
- dozerAI->startBuildingSound( goalObject->getTemplate()->getPerUnitSound( "UnderConstruction" ), goalObject->getID() );
- }
- } // end if
- // only do the build if we've moved into the dock position
- if( dozerAI->getBuildSubTask() == DOZER_DO_BUILD_AT_DOCK )
- {
- // the builder is now actively constructing something
- dozer->setModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
-
- // increase the construction percent of the goal object
- Int framesToBuild = goalObject->getTemplate()->calcTimeToBuild( dozer->getControllingPlayer() );
- Real percentProgressThisFrame = 100.0f / framesToBuild;
- goalObject->setConstructionPercent( goalObject->getConstructionPercent() +
- percentProgressThisFrame );
- //
- // every time we construct a piece of the goal object, the goal object gets a little
- // bit o health, note that we're bypassing the regular healing/damage methods
- // here and going straight for the change of the health
- //
- BodyModuleInterface *body = goalObject->getBodyModule();
- body->internalChangeHealth( body->getMaxHealth() / INT_TO_REAL( framesToBuild ) );
- //
- // since we've just actually contributed a "piece" to this building, we'll say that
- // we are now the producer and the builder
- //
- goalObject->setProducer( dozer );
- goalObject->setBuilder( dozer );
- // check for construction complete
- if( goalObject->getConstructionPercent() >= 100.0f )
- {
- // clear the under construction status
- goalObject->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNDER_CONSTRUCTION ) );
- goalObject->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_RECONSTRUCTING ) );
- // stop playing the construction sound!
- dozerAI->finishBuildingSound();
- // object will now be idle instead of in one of the construction actions
- goalObject->clearModelConditionFlags(
- MAKE_MODELCONDITION_MASK3(MODELCONDITION_AWAITING_CONSTRUCTION, MODELCONDITION_PARTIALLY_CONSTRUCTED, MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED));
- // set the construction at the 100% enum value
- goalObject->setConstructionPercent( CONSTRUCTION_COMPLETE );
- //
- // now that we're done with construction we evaluate the visual condition of
- // the object since while under construction visual changes due to damage state
- // do not occur. Note that is was important to call this after we cleared the
- // model conditions involving construction because part of this evaluation
- // is to auto populate the model with particle systems when special named
- // bones for the current given state are provided
- //
- body->evaluateVisualCondition();
- // this object now has energy influence in the player
- Player *player = goalObject->getControllingPlayer();
- if( player )
- {
- // notification for build completeion
- player->onStructureConstructionComplete( dozer, goalObject, dozerAI->getIsRebuild() );
- player->getAcademyStats()->recordProduction( goalObject, dozer );
- //
- // Now onCreates were called at construction start. Now at finish is when we
- // want the Game side of OnCreate
- //
- for (BehaviorModule** m = goalObject->getBehaviorModules(); *m; ++m)
- {
- CreateModuleInterface* create = (*m)->getCreate();
- if (!create)
- continue;
- create->onBuildComplete();
- }
- } // end if
- // Creation is another valid and essential time to call this. This building now Looks.
- goalObject->handlePartitionCellMaintenance();
-
- // UnderConstruction just cleared, so update our upgrades
- goalObject->updateUpgradeModules();
- // this object how has influence in the controlling players' tech tree
- /// @todo need to write this
- // do some UI stuff for the constrolling player
- if( dozer->isLocallyControlled() )
- {
- // message the the building player
- UnicodeString format = TheGameText->fetch( "DOZER:ConstructionComplete" );
- UnicodeString objectName = goalObject->getTemplate()->getDisplayName();
- if( objectName.isEmpty() )
- {
- UnicodeString format = TheGameText->fetch( "INI:MissingDisplayName" );
- objectName.format( format, goalObject->getTemplate()->getName().str() );
- } // end if
- UnicodeString msg;
- msg.format( format.str(), objectName.str() );
- TheInGameUI->message( msg );
- AudioEventRTS audio = *dozer->getTemplate()->getVoiceTaskComplete();
- audio.setObjectID(dozer->getID());
- TheAudio->addAudioEvent(&audio);
- /// make radar neat-o attention grabber event at build location
- TheRadar->createEvent( goalObject->getPosition(), RADAR_EVENT_CONSTRUCTION );
- } // end if
- // this will allow us to exit the state machine with success
- complete = TRUE;
-
- // move off to the end dock position if present
- const Coord3D *endPos = dozerAI->getDockPoint( m_task, DOZER_DOCK_POINT_END );
- Coord3D pos = *dozer->getPosition();
- if( endPos ) {
- pos = *endPos;
- }
- // Our goal may be inside a building, if we are packing them in tight, so try adjusting. jba.
- TheAI->pathfinder()->adjustToPossibleDestination(dozer, ai->getLocomotorSet(), &pos);
- ai->aiMoveToPosition( &pos, CMD_FROM_AI );
- } // end if
- } // end if
- break;
- } // end build
- //---------------------------------------------------------------------------------------------
- case DOZER_TASK_REPAIR:
- {
- BodyModuleInterface *body = goalObject->getBodyModule();
- // check for fully "repaired"
- if( body->getHealth() == body->getMaxHealth() )
- {
- // issue repair complete message, we might want to remove this later cause it could be annoying
- TheInGameUI->message( "DOZER:RepairComplete" );
- // we're now complete
- complete = TRUE;
- } // end if
- else
- {
- Bool canHeal = TRUE;
- // if we are repairing a bridge, create scaffolding over the bridge if we need to
- if( goalObject->isKindOf( KINDOF_BRIDGE_TOWER ) )
- dozerAI->createBridgeScaffolding( goalObject );
- // the builder is now actively repairing something, we'll borrow the constructing animation
- dozer->setModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
- //
- // when repairing bridges, we cannot actually do any repairing until the
- // scaffolding is extended and all the way complete
- //
- if( goalObject->isKindOf( KINDOF_BRIDGE_TOWER ) )
- {
- BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( goalObject );
- DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower interface\n") );
- Object *bridgeObject = TheGameLogic->findObjectByID( btbi->getBridgeID() );
- DEBUG_ASSERTCRASH( bridgeObject, ("Unable to find bridge center object\n") );
- BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridgeObject );
- DEBUG_ASSERTCRASH( bbi, ("Unable to find bridge interface from tower goal object during repair\n") );
- if( bbi->isScaffoldInMotion() == TRUE )
- canHeal = FALSE;
- } // end if
- // do healing
- if( canHeal )
- {
- // figure out how much health we will restore this frame
- Real health = body->getMaxHealth() * dozerAI->getRepairHealthPerSecond() /
- LOGICFRAMES_PER_SECOND;
- // try to give it a little bit-o-health
- if ( ! goalObject->attemptHealingFromSoleBenefactor(health, dozer, 2) )//this frame and the next
- {
- // goalObject->setStatus( OBJECT_STATUS_UNDERGOING_REPAIR );
- // This bit used to be set way back in DozerAIUpdate::privateRepair(), but it has been outmoded
- // so that several dozers/workers can target the same sick building for repair, but the first one to begin
- // this is done by attemptHealingFromSoleBenefactor() which lets the beneficiary of the healing
- // remember who has been healing it, and will return false to everybody else
- //or if the goalObject is already receiving healing, I must stop, since my healing is getting rejected here
- dozerAI->internalTaskComplete( m_task );
- getMachine()->setGoalObject( NULL );
- return STATE_FAILURE;
- }
- } // end if
- } // end else
- // play a repairing sound
- break;
- } // end repair
- //---------------------------------------------------------------------------------------------
- case DOZER_TASK_FORTIFY:
- {
- /// @todo write me
- break;
- } // end fortify
- //---------------------------------------------------------------------------------------------
- default:
- {
- DEBUG_CRASH(( "Unknown task for the dozer action do action state\n" ));
- return STATE_FAILURE;
- } // end default
- } // end switch
- // if we're complete with the task we exit success
- if( complete == TRUE )
- {
- // this task is now complete, remove it from dozer consideration
- dozerAI->internalTaskComplete( m_task );
- // to be clean get rid of the goal object we set
- getMachine()->setGoalObject( NULL );
-
- getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
- // we're done
- return STATE_SUCCESS;
- } // end if
- // just continue sitting here for a while
- getMachineOwner()->clearWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);// no mine clearing fun while I'm on the job
- return STATE_CONTINUE;
- } // end update
- //-------------------------------------------------------------------------------------------------
- /** The Dozer action state machine */
- //-------------------------------------------------------------------------------------------------
- class DozerActionStateMachine : public StateMachine
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( DozerActionStateMachine, "DozerActionStateMachine" );
- public:
- DozerActionStateMachine( Object *owner, DozerTask task );
- // virtual destructor prototypes provided by memory pool object
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer );
- virtual void xfer( Xfer *xfer );
- virtual void loadPostProcess();
- protected:
- DozerTask m_task; ///< the task of this action state machine
- };
- EMPTY_DTOR(DozerActionStateMachine)
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- DozerActionStateMachine::DozerActionStateMachine( Object *owner, DozerTask task ) :
- StateMachine( owner, "DozerActionStateMachine" )
- {
- // initialize our task
- m_task = task;
- // order matters: first state is the default state.
- defineState( DOZER_ACTION_PICK_ACTION_POS, newInstance(DozerActionPickActionPosState)( this, task ), DOZER_ACTION_MOVE_TO_ACTION_POS, EXIT_MACHINE_WITH_FAILURE );
- defineState( DOZER_ACTION_MOVE_TO_ACTION_POS, newInstance(DozerActionMoveToActionPosState)( this, task ), DOZER_ACTION_DO_ACTION, DOZER_ACTION_PICK_ACTION_POS );
- defineState( DOZER_ACTION_DO_ACTION, newInstance(DozerActionDoActionState)( this, task ), EXIT_MACHINE_WITH_SUCCESS, EXIT_MACHINE_WITH_FAILURE );
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void DozerActionStateMachine::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void DozerActionStateMachine::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferUser(&m_task, sizeof(m_task));
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void DozerActionStateMachine::loadPostProcess( void )
- {
- } // end loadPostProcess
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- static Object *findObjectToRepair( Object *dozer )
- {
- // sanity
- if( dozer == NULL )
- return NULL;
- if( !dozer->getAIUpdateInterface() )
- {
- return NULL;
- }
- const DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
- PartitionFilterSamePlayer filter1( dozer->getControllingPlayer() );
- PartitionFilterAcceptByKindOf filter2( MAKE_KINDOF_MASK( KINDOF_STRUCTURE ),
- KINDOFMASK_NONE );
- PartitionFilterSameMapStatus filterMapStatus(dozer);
- PartitionFilter *filters[] = { &filter1, &filter2, &filterMapStatus, NULL };
- ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( dozer->getPosition(),
- dozerAI->getBoredRange(),
- FROM_CENTER_2D,
- filters );
- MemoryPoolObjectHolder hold( iter );
- Object *obj;
- Object *closestRepairTarget = NULL;
- Real closestRepairTargetDistSqr = 0.0f;
- for( obj = iter->first(); obj; obj = iter->next() )
- {
- // ignore objects we cant repair
- if( TheActionManager->canRepairObject( dozer, obj, CMD_FROM_AI ) == FALSE )
- continue;
- // target the closest valid repair target
- if( closestRepairTarget == NULL )
- {
- closestRepairTarget = obj;
- closestRepairTargetDistSqr = ThePartitionManager->getDistanceSquared( dozer, obj, FROM_CENTER_2D );
- } // end if
- else
- {
- // only use this command center if it's closer than the last one we found
- Real distSqr = ThePartitionManager->getDistanceSquared( dozer, obj, FROM_CENTER_2D );
- if( distSqr < closestRepairTargetDistSqr )
- {
-
- closestRepairTarget = obj;
- closestRepairTargetDistSqr = distSqr;
- } // end if
- } // end else
- } // end for obj
- return closestRepairTarget;
- } // end findObjectToRepair
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- static Object *findMine( Object *dozer )
- {
- // sanity
- if( dozer == NULL )
- return NULL;
- if( !dozer->getAIUpdateInterface() )
- {
- return NULL;
- }
- const DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
- // srj sez: only clear enemy or neutal mines. clearing allied mines (ie, OURS) is really dumb.
- // (and no, PossibleToAttack won't necessarily filter these out.)
- PartitionFilterRelationship filterTeam(dozer, PartitionFilterRelationship::ALLOW_ENEMIES | PartitionFilterRelationship::ALLOW_NEUTRAL);
- PartitionFilterPossibleToAttack filterAttack(ATTACK_NEW_TARGET, dozer, CMD_FROM_DOZER);
- PartitionFilterSameMapStatus filterMapStatus(dozer);
- PartitionFilter *filters[] = { &filterTeam, &filterAttack, &filterMapStatus, NULL };
- Object* mine = ThePartitionManager->getClosestObject(dozer, dozerAI->getBoredRange(), FROM_CENTER_2D, filters);
- return mine;
- } // end findMine
- //-------------------------------------------------------------------------------------------------
- /** Available primary Dozer states */
- //-------------------------------------------------------------------------------------------------
- enum
- {
- DOZER_PRIMARY_IDLE, ///< dozer is idle
- DOZER_PRIMARY_BUILD, ///< dozer is building a structure for the player
- DOZER_PRIMARY_REPAIR, ///< dozer is repairing something
- DOZER_PRIMARY_FORTIFY, ///< dozer is fortifying a civilian building, making it stronger
- DOZER_PRIMARY_GO_HOME, ///< dozer has nothing else to do so it will return a command center
- };
- //-------------------------------------------------------------------------------------------------
- /** Dozer primary idle state */
- //-------------------------------------------------------------------------------------------------
- class DozerPrimaryIdleState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerPrimaryIdleState, "DozerPrimaryIdleState")
- public:
- DozerPrimaryIdleState( StateMachine *machine ) : State( machine, "DozerPrimaryIdleState" )
- {
- m_idleTooLongTimestamp = 0;
- m_idlePlayerNumber = 0;
- m_isMarkedAsIdle = FALSE;
- }
- virtual StateReturnType update( void );
- virtual StateReturnType onEnter( void );
- virtual void onExit( StateExitType status );
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer );
- virtual void xfer( Xfer *xfer );
- virtual void loadPostProcess();
- protected:
- UnsignedInt m_idleTooLongTimestamp; ///< when this is more than our idle too long time we try to do something about it
- Int m_idlePlayerNumber; ///< Remeber what list we were added to.
- Bool m_isMarkedAsIdle;
- };
- EMPTY_DTOR(DozerPrimaryIdleState)
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void DozerPrimaryIdleState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void DozerPrimaryIdleState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferUnsignedInt(&m_idleTooLongTimestamp);
- xfer->xferInt(&m_idlePlayerNumber);
- xfer->xferBool(&m_isMarkedAsIdle);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void DozerPrimaryIdleState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- /** Upon entering the dozer primary idle state */
- //-------------------------------------------------------------------------------------------------
- StateReturnType DozerPrimaryIdleState::onEnter( void )
- {
- //
- // upon entering this state we begin tracking how long we're idle for to try to do something
- // about it every once in a while
- //
- m_idleTooLongTimestamp = TheGameLogic->getFrame();
- //if(TheGameLogic->getFrame() > m_idleNotifyTimestamp + NOTIFY_AFTER_TIME)
- m_isMarkedAsIdle = FALSE;
- getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
- return STATE_CONTINUE;
- } // end onEnter
- //-------------------------------------------------------------------------------------------------
- /** Upon exiting the dozer primary idle state */
- //-------------------------------------------------------------------------------------------------
- void DozerPrimaryIdleState::onExit( StateExitType status )
- {
- if(m_isMarkedAsIdle)
- {
- TheInGameUI->removeIdleWorker(getMachineOwner(), m_idlePlayerNumber);
- m_idlePlayerNumber = -1;
- m_isMarkedAsIdle = FALSE;
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Dozer idle behavior */
- //-------------------------------------------------------------------------------------------------
- StateReturnType DozerPrimaryIdleState::update( void )
- {
- Object *dozer = getMachineOwner();
- AIUpdateInterface *ai = dozer->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- DozerAIInterface *dozerAI = ai->getDozerAIInterface();
- if( !dozerAI )
- {
- return STATE_FAILURE;
- }
- //
- // These are to add into the IngameUI idle worker button thingy
- // we don't want to add in if we're already in the list or if
- // we're "Effectivly dead"
- //
- if( ai->isIdle() && !m_isMarkedAsIdle && !dozer->isEffectivelyDead())
- {
- m_idlePlayerNumber = dozer->getControllingPlayer()->getPlayerIndex();
- TheInGameUI->addIdleWorker(getMachineOwner());
- m_isMarkedAsIdle = TRUE;
- getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
- }
- if( m_isMarkedAsIdle && (!ai->isIdle() || dozer->isEffectivelyDead()))
- {
- TheInGameUI->removeIdleWorker(getMachineOwner(), m_idlePlayerNumber);
- m_idlePlayerNumber = -1;
- m_isMarkedAsIdle = FALSE;
- }
- //
- // if our actual real AI state machine is not idle we're doing something so let's reset
- // our idle too long timestamp
- //
- if( !ai->isIdle() )
- m_idleTooLongTimestamp = TheGameLogic->getFrame();
-
- //
- // if we're just sitting here in idle, and there is no task pending ... after so long we'll
- // try to go to the nearest command center
- //
- if( TheGameLogic->getFrame() - m_idleTooLongTimestamp > dozerAI->getBoredTime() &&
- dozerAI->isAnyTaskPending() == FALSE )
- {
- //
- // to prevent us from doing this potentially expensive logic to find objects every frame
- // we'll only try it every once in a while so set our idle too long timestamp to the
- // current frame
- //
- m_idleTooLongTimestamp = TheGameLogic->getFrame();
- // try to find something around us that we can repair
- Object *repairTarget = findObjectToRepair( dozer );
- if( repairTarget )
- {
- // issue the command
- ai->aiRepair( repairTarget, CMD_FROM_AI );
- //
- // in theory we would need to "interrupt" whatever it is the Dozer is doing now, but
- // we know we're in the idle state doing nothing so we don't really need to
- //
- } else {
- getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
- Object *mine = findMine(dozer);
- if (mine!=NULL) {
- ai->aiAttackObject( mine, 1, CMD_FROM_DOZER);
- }
- }
-
- } // end if
- return STATE_CONTINUE;
- } // end update
- //-------------------------------------------------------------------------------------------------
- /** Dozer action state */
- //-------------------------------------------------------------------------------------------------
- class DozerActionState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerActionState, "DozerActionState")
- public:
- DozerActionState( StateMachine *machine, DozerTask task ) : State( machine, "DozerActionState" )
- {
- m_task = task;
- m_actionMachine = newInstance(DozerActionStateMachine)( getMachineOwner(), task );
- m_actionMachine->initDefaultState();
- }
- /*
- Note that we DON'T use CONVERT_SLEEP_TO_CONTINUE; since we're not doing anything else
- interesting in update, we can sleep when this machine sleeps
- */
- virtual StateReturnType update( void ) { return m_actionMachine->updateStateMachine(); }
- virtual StateReturnType onEnter( void );
- virtual void onExit( StateExitType status );
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer );
- virtual void xfer( Xfer *xfer );
- virtual void loadPostProcess();
- protected:
- DozerTask m_task; // task for this state (and therefore sub-machine too)
- StateMachine *m_actionMachine;
- };
- inline DozerActionState::~DozerActionState( void ) { if (m_actionMachine) m_actionMachine->deleteInstance(); }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void DozerActionState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void DozerActionState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferUser(&m_task, sizeof(m_task));
- xfer->xferSnapshot(m_actionMachine);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void DozerActionState::loadPostProcess( void )
- {
- } // end loadPostProcess
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- StateReturnType DozerActionState::onEnter( void )
- {
- // save this as the current action of the dozer
- Object *dozer = getMachineOwner();
- if( !dozer->getAIUpdateInterface() )
- {
- return STATE_FAILURE;
- }
- DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
- dozerAI->setCurrentTask( m_task );
- //
- // we have a machine within this state all it's own ...
- // note that during an onEnter, since the action machine is persistent, anytime we transition
- // into doing an action, we need to reset our state machine to do that action as it may
- // have been left in any state from the previous time we ran it
- //
- m_actionMachine->resetToDefaultState();
- return STATE_CONTINUE;
- } // end onEnter
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void DozerActionState::onExit( StateExitType status )
- {
- // save the current action of the dozer as none
- Object *dozer = getMachineOwner();
- if( !dozer->getAIUpdateInterface() )
- {
- return;
- }
- DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
- dozerAI->setCurrentTask( DOZER_TASK_INVALID );
- } // end onExit
- //-------------------------------------------------------------------------------------------------
- /** Dozer primary going home state */
- //-------------------------------------------------------------------------------------------------
- class DozerPrimaryGoingHomeState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerPrimaryGoingHomeState, "DozerPrimaryGoingHomeState")
- protected:
- // snapshot interface STUBBED no member vars
- virtual void crc( Xfer *xfer ){};
- virtual void xfer( Xfer *xfer ){};
- virtual void loadPostProcess(){};
- public:
- DozerPrimaryGoingHomeState( StateMachine *machine ) : State( machine, "DozerPrimaryGoingHomeState" ) { }
- virtual StateReturnType update( void ) { return STATE_FAILURE; }
- };
- EMPTY_DTOR(DozerPrimaryGoingHomeState)
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- DozerPrimaryStateMachine::DozerPrimaryStateMachine( Object *owner ) : StateMachine( owner, "DozerPrimaryStateMachine" )
- {
- static const StateConditionInfo idleConditions[] =
- {
- StateConditionInfo(isBuildMostImportant, DOZER_PRIMARY_BUILD, NULL),
- StateConditionInfo(isRepairMostImportant, DOZER_PRIMARY_REPAIR, NULL),
- StateConditionInfo(isFortifyMostImportant, DOZER_PRIMARY_FORTIFY, NULL),
- StateConditionInfo(NULL, NULL, NULL) // keep last
- };
- // order matters: first state is the default state.
- defineState( DOZER_PRIMARY_IDLE, newInstance(DozerPrimaryIdleState)( this ), INVALID_STATE_ID, INVALID_STATE_ID, idleConditions );
- defineState( DOZER_PRIMARY_BUILD, newInstance(DozerActionState)( this, DOZER_TASK_BUILD ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
- defineState( DOZER_PRIMARY_REPAIR, newInstance(DozerActionState)( this, DOZER_TASK_REPAIR ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
- defineState( DOZER_PRIMARY_FORTIFY, newInstance(DozerActionState)( this, DOZER_TASK_FORTIFY ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
- defineState( DOZER_PRIMARY_GO_HOME, newInstance(DozerPrimaryGoingHomeState)( this ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
- } // end DozerPrimaryStateMachine
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- DozerPrimaryStateMachine::~DozerPrimaryStateMachine( void )
- {
- } // end ~DozerPrimaryStateMachine
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void DozerPrimaryStateMachine::crc( Xfer *xfer )
- {
- StateMachine::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void DozerPrimaryStateMachine::xfer( Xfer *xfer )
- {
- XferVersion cv = 1;
- XferVersion v = cv;
- xfer->xferVersion( &v, cv );
- StateMachine::xfer(xfer);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void DozerPrimaryStateMachine::loadPostProcess( void )
- {
- StateMachine::loadPostProcess();
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Bool DozerPrimaryStateMachine::isBuildMostImportant( State *thisState, void* userData )
- {
- Object *dozer = thisState->getMachineOwner();
- AIUpdateInterface *ai = dozer->getAIUpdateInterface();
- if( !ai )
- {
- return FALSE;
- }
- DozerAIInterface *dozerAI = ai->getDozerAIInterface();
- if( !dozerAI )
- {
- return FALSE;
- }
- if( !ai->isIdle() )
- return FALSE; // busy doing something else
- // if the most important task is us then return true
- DozerTask task = dozerAI->getMostRecentCommand();
- return task == DOZER_TASK_BUILD;
- } // end isBuildMostImportant
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Bool DozerPrimaryStateMachine::isRepairMostImportant( State *thisState, void* userData )
- {
- Object *dozer = thisState->getMachineOwner();
- AIUpdateInterface *ai = dozer->getAIUpdateInterface();
- if( !ai )
- {
- return false;
- }
- DozerAIInterface *dozerAI = ai->getDozerAIInterface();
- if( !dozerAI )
- {
- return false;
- }
- if( !ai->isIdle() )
- return FALSE; // busy doing something else
- // if the most important task is us then return true
- DozerTask task = dozerAI->getMostRecentCommand();
- return task == DOZER_TASK_REPAIR;
- } // end isRepairMostImportant
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Bool DozerPrimaryStateMachine::isFortifyMostImportant( State *thisState, void* userData )
- {
- Object *dozer = thisState->getMachineOwner();
- AIUpdateInterface *ai = dozer->getAIUpdateInterface();
- if( !ai )
- {
- return false;
- }
- DozerAIInterface *dozerAI = ai->getDozerAIInterface();
- if( !dozerAI )
- {
- return false;
- }
- if( !ai->isIdle() )
- return FALSE; // busy doing something else
- // if the most important task is us then return true
- DozerTask task = dozerAI->getMostRecentCommand();
- return task == DOZER_TASK_FORTIFY;
- } // end isFortifyMostImportat
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- DozerAIUpdateModuleData::DozerAIUpdateModuleData( void )
- {
- m_repairHealthPercentPerSecond = 0.0f;
- m_boredTime = 0.0f;
- m_boredRange = 0.0f;
- } // end DozerAIUpdateModuleData
- // ------------------------------------------------------------------------------------------------
- void DozerAIUpdateModuleData::buildFieldParse( MultiIniFieldParse& p)
- {
- AIUpdateModuleData::buildFieldParse( p );
- static const FieldParse dataFieldParse[] =
- {
- { "RepairHealthPercentPerSecond", INI::parsePercentToReal, NULL, offsetof( DozerAIUpdateModuleData, m_repairHealthPercentPerSecond ) },
- { "BoredTime", INI::parseDurationReal, NULL, offsetof( DozerAIUpdateModuleData, m_boredTime ) },
- { "BoredRange", INI::parseReal, NULL, offsetof( DozerAIUpdateModuleData, m_boredRange ) },
- { 0, 0, 0, 0 }
- };
- p.add( dataFieldParse );
- } // end buildFieldParse
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- DozerAIUpdate::DozerAIUpdate( Thing *thing, const ModuleData* moduleData ) :
- AIUpdateInterface( thing, moduleData )
-
- {
- Int i, j;
- for( i = 0; i < DOZER_NUM_TASKS; i++ )
- {
- m_task[ i ].m_targetObjectID = INVALID_ID;
- m_task[ i ].m_taskOrderFrame = 0;
- for( j = 0; j < DOZER_NUM_DOCK_POINTS; j++ )
- {
- m_dockPoint[ i ][ j ].valid = FALSE;
- m_dockPoint[ i ][ j ].location.zero();
- } // end for j
- } // end for i
- m_currentTask = DOZER_TASK_INVALID;
- m_buildSubTask = DOZER_SELECT_BUILD_DOCK_LOCATION; // irrelavant, but I want non-garbage value
- //
- // initialize the dozer machine to NULL, we want to do this and create it during the update
- // implementation because at this point we don't have the object all setup
- //
- m_dozerMachine = NULL;
- createMachines();
- } // end DozerAIUpdate
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- DozerAIUpdate::~DozerAIUpdate( void )
- {
- // delete our behavior state machine
- if( m_dozerMachine )
- m_dozerMachine->deleteInstance();
- // no orders
- for( Int i = 0; i < DOZER_NUM_TASKS; i++ )
- {
- m_task[ i ].m_targetObjectID = INVALID_ID;
- m_task[ i ].m_taskOrderFrame = 0;
- } // end for i
- } // end ~DozerAIUpdate
- // ------------------------------------------------------------------------------------------------
- /** Create any sub machines we need to do all our behavior */
- // ------------------------------------------------------------------------------------------------
- void DozerAIUpdate::createMachines( void )
- {
- if( m_dozerMachine == NULL )
- {
- m_dozerMachine = newInstance(DozerPrimaryStateMachine)( getObject() );
- m_dozerMachine->initDefaultState();
- } // end if
- } // end createMachines
- // ------------------------------------------------------------------------------------------------
- /** Create the bridge scaffolding if necessary for the bridge that is attached to this tower */
- // ------------------------------------------------------------------------------------------------
- void DozerAIUpdate::createBridgeScaffolding( Object *bridgeTower )
- {
- // sanity
- if( bridgeTower == NULL )
- return;
- // get the bridge behavior interface from the bridge object that this tower is a part of
- BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( bridgeTower );
- if( btbi == NULL )
- return;
- Object *bridgeObject = TheGameLogic->findObjectByID( btbi->getBridgeID() );
- if( bridgeObject == NULL )
- return;
- BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridgeObject );
- if( bbi == NULL )
- return;
- // tell the bridge to create scaffolding if necessary
- bbi->createScaffolding();
- } // end createBridgeScaffolding
- // ------------------------------------------------------------------------------------------------
- /** Remove the bridge scaffolding from the bridge object that is attached to this tower */
- // ------------------------------------------------------------------------------------------------
- void DozerAIUpdate::removeBridgeScaffolding( Object *bridgeTower )
- {
- // sanity
- if( bridgeTower == NULL )
- return;
- // get the bridge behavior interface from the bridge object that this tower is a part of
- BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( bridgeTower );
- if( btbi == NULL )
- return;
- Object *bridgeObject = TheGameLogic->findObjectByID( btbi->getBridgeID() );
- if( bridgeObject == NULL )
- return;
- BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridgeObject );
- if( bbi == NULL )
- return;
- // tell the bridge to end any scaffolding from repairing
- bbi->removeScaffolding();
- } // end removeBridgeScaffolding
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime DozerAIUpdate::update( void )
- {
- //
- // NOTE: Any changes to DozerAIUpdate::* you probably want to reflect and copy into
- // WorkerAIUPdate:* as well ... sigh
- //
- //
- // now that we're really executing we have all the necessary object modules in place to
- // correctly create a state machine and set the default state
- //
- createMachines();
- // set us as being to able to move with super precision off grid locations
- /*
- if( getCurLocomotor() ) {
- getCurLocomotor()->setUltraAccurate( TRUE );
- getCurLocomotor()->setAllowInvalidPosition(TRUE);
- }*/
-
- // extend the normal AI system
- UpdateSleepTime result;
- result = AIUpdateInterface::update();
- // do nothing if we're dead
- ///@todo shouldn't this be at a higher level?
- if( getObject()->isEffectivelyDead() )
- return UPDATE_SLEEP_NONE;
- // get and validate our current task
- DozerTask currentTask = getCurrentTask();
- if( currentTask != DOZER_TASK_INVALID )
- {
-
- ObjectID taskTarget = getTaskTarget( currentTask );
- Object *targetObject = TheGameLogic->findObjectByID( taskTarget );
- Bool invalidTask = FALSE;
- // validate the task and the target
- if( currentTask == DOZER_TASK_REPAIR &&
- TheActionManager->canRepairObject( getObject(), targetObject, getLastCommandSource() ) == FALSE )
- invalidTask = TRUE;
-
- // cancel the task if it's now invalid
- if( invalidTask == TRUE )
- cancelTask( currentTask );
- } // end if
- else
- getObject()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
- // run our own state machine
- m_dozerMachine->updateStateMachine();
- return UPDATE_SLEEP_NONE;
-
- } // end update
- //-------------------------------------------------------------------------------------------------
- /** The entry point of a construct command to the Dozer */
- //-------------------------------------------------------------------------------------------------
- Object *DozerAIUpdate::construct( const ThingTemplate *what,
- const Coord3D *pos,
- Real angle,
- Player *owningPlayer,
- Bool isRebuild )
- {
- // !!! NOTE: If you modify this you must modify the worker too !!!
- // !!! Graham: Please please please have inspiration for how to *not* duplicate this code
- // create our machines if they don't yet exist
- ///@todo make 'construct' a real AI command and you won't need a special case
- m_isRebuild = isRebuild;
- createMachines();
- // sanity
- if( what == NULL || pos == NULL || owningPlayer == NULL )
- return NULL;
- // sanity
- DEBUG_ASSERTCRASH( getObject()->getControllingPlayer() == owningPlayer,
- ("Dozer::Construct - The controlling player of the Dozer is not the owning player passed in\n") );
-
- // if we're not rebuilding, we have a few checks to pass first for sanity
- if( isRebuild == FALSE )
- {
- // AI has weaker restriction on building
- Bool dozerIsAI = owningPlayer->getPlayerType() == PLAYER_COMPUTER;
- if( dozerIsAI )
- {
- // Just build it. The ai will validate, or cheat. jba.
-
- } // end if
- else
- {
- // make sure the player is capable of building this
- if( TheBuildAssistant->canMakeUnit( getObject(), what ) != CANMAKE_OK)
- return NULL;
- // validate the the position to build at is valid
- if( TheBuildAssistant->isLocationLegalToBuild( pos, what, angle,
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::NO_OBJECT_OVERLAP |
- BuildAssistant::SHROUD_REVEALED,
- getObject(), NULL ) != LBC_OK )
- return NULL;
- } // end else
- } // end if
- //
- // what will our initial status bits be, it is important to do this early
- // before the hooks add/subtract power from a player are executed
- //
- ObjectStatusMaskType statusBits = MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNDER_CONSTRUCTION );
- if( isRebuild )
- statusBits.set( OBJECT_STATUS_RECONSTRUCTING );
- // create an object at the destination location
- Object *obj = TheThingFactory->newObject( what, owningPlayer->getDefaultTeam(), statusBits );
- // even though we haven't actually built anything yet, this keeps things tidy
- obj->setProducer( getObject() );
- obj->setBuilder( getObject() );
- // take the required money away from the player
- if( isRebuild == FALSE )
- {
- Money *money = owningPlayer->getMoney();
- money->withdraw( what->calcCostToBuild( owningPlayer ) );
- } // end if
- // initialize object
- obj->setPosition( pos );
- obj->setOrientation( angle );
- // Flatten the terrain underneath the object, then adjust to the flattened height. jba.
- TheTerrainLogic->flattenTerrain(obj);
- Coord3D adjustedPos = *pos;
- adjustedPos.z = TheTerrainLogic->getGroundHeight(pos->x, pos->y);
- obj->setPosition(&adjustedPos);
-
- // Note - very important that we add to map AFTER we flatten terrain. jba.
- TheAI->pathfinder()->addObjectToPathfindMap( obj );
- // "callback" event for structure created (note that it's not yet "complete")
- owningPlayer->onStructureCreated( getObject(), obj );
- // set a construction percent for the new object to zero and a status for under construction
- obj->setConstructionPercent( 0.0 );
- // newly constructed objects start at one hit point
- BodyModuleInterface *body = obj->getBodyModule();
- body->internalChangeHealth( -body->getHealth() + 1.0f );
- // set the model action state to awaiting construction
- obj->clearAndSetModelConditionFlags(
- MAKE_MODELCONDITION_MASK2(MODELCONDITION_PARTIALLY_CONSTRUCTED, MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED),
- MAKE_MODELCONDITION_MASK(MODELCONDITION_AWAITING_CONSTRUCTION)
- );
- // we have a construction pending
- newTask( DOZER_TASK_BUILD, obj );
- return obj;
-
- } // end construct
- // ------------------------------------------------------------------------------------------------
- /** Given our current task and repair target, can we accept this as a new repair target */
- // ------------------------------------------------------------------------------------------------
- Bool DozerAIUpdate::canAcceptNewRepair( Object *obj )
- {
- // sanity
- if( obj == NULL )
- return FALSE;
- // if we're not repairing right now, we don't have any accept restrictions
- if( getCurrentTask() != DOZER_TASK_REPAIR )
- return TRUE;
- // get current repair target
- Object *currentRepair = TheGameLogic->findObjectByID( m_task[ DOZER_TASK_REPAIR ].m_targetObjectID );
- if( currentRepair )
- {
- // check for same object
- if( currentRepair == obj )
- return FALSE;
- // check for repairing any tower on the same bridge
- if( currentRepair->isKindOf( KINDOF_BRIDGE_TOWER ) &&
- obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
- {
- BridgeTowerBehaviorInterface *currentTowerInterface = NULL;
- BridgeTowerBehaviorInterface *newTowerInterface = NULL;
- currentTowerInterface = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( currentRepair );
- newTowerInterface = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
- // sanity
- if( currentTowerInterface == NULL || newTowerInterface == NULL )
- {
- DEBUG_CRASH(( "Unable to find bridge tower interface on object\n" ));
- return FALSE;
- } // end if
- // if they are part of the same bridge, ignore this repair command
- if( currentTowerInterface->getBridgeID() == newTowerInterface->getBridgeID() )
- return FALSE;
- } // end if
- } // end if, currentRepair object exists
- // all is well
- return TRUE;
- } // end canAcceptNewRepair
- // ------------------------------------------------------------------------------------------------
- /** Issue an order for the Dozer to go repair the target 'obj' */
- // ------------------------------------------------------------------------------------------------
- void DozerAIUpdate::privateRepair( Object *obj, CommandSourceType cmdSource )
- {
- Object *dozer = getObject();
- // if we are already repairing this target do nothing
- if( canAcceptNewRepair( obj ) == FALSE )
- return;
- // sanity, if we can't repair the object then get out of there
- if( TheActionManager->canRepairObject( dozer, obj, cmdSource ) == FALSE )
- return;
- // if this object is already actively being repaired by another dozertype we won't also try to go repair it
- ObjectID currentRepairer = obj->getSoleHealingBenefactor();
- if( currentRepairer != INVALID_ID && currentRepairer != dozer->getID() )
- return;
- // Bridges have been made indestructible, so these checks were in vain. -- ML
- // if this object is a bridge tower, we need to check the status of the bridge in order
- // to check for a 'duplicate repair'
- //
- //if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
- //{
- // BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
- // DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower behavior interface\n") );
- //
- // Object *bridge = TheGameLogic->findObjectByID( btbi->getBridgeID() );
- // DEBUG_ASSERTCRASH( bridge, ("Unable to find bridge object\n") );
- // if( BitTest( bridge->getStatusBits(), OBJECT_STATUS_UNDERGOING_REPAIR ) == TRUE )
- // return;
- //
- //} // end if
-
-
- // for bridges, set the status for the bridge object
- //if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
- //{
- // BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
- // DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower behavior interface\n") );
- //
- // Object *bridge = TheGameLogic->findObjectByID( btbi->getBridgeID() );
- // DEBUG_ASSERTCRASH( bridge, ("Unable to find bridge object\n") );
- // bridge->setStatus( OBJECT_STATUS_UNDERGOING_REPAIR );
- //
- //} // end if
- // start the new task
- newTask( DOZER_TASK_REPAIR, obj );
- } // end repair
- // ------------------------------------------------------------------------------------------------
- /** Resume construction on a building */
- // ------------------------------------------------------------------------------------------------
- void DozerAIUpdate::privateResumeConstruction( Object *obj, CommandSourceType cmdSource )
- {
- // sanity
- if( obj == NULL )
- return;
- // make sure we can resume construction on this
- if( TheActionManager->canResumeConstructionOf( getObject(), obj, cmdSource ) == FALSE )
- return;
- // start the new task for construction
- newTask( DOZER_TASK_BUILD, obj );
- } // end privateResumeConstruction
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /*static*/ Bool DozerAIUpdate::findGoodBuildOrRepairPosition(const Object* me, const Object* target, Coord3D& positionOut)
- {
- // The place we go to build or repair is the closest spot from us to them
- Coord3D ourPosition = *me->getPosition();
- Coord3D theirPosition = *target->getPosition();
-
- Coord3D bestPosition = theirPosition;// This answer is the best, as it includes findPositionAround
- Coord3D workingPosition = theirPosition;// But if findPositionAround fails, we need to say something.
-
- Vector3 offset( ourPosition.x - theirPosition.x,
- ourPosition.y - theirPosition.y,
- ourPosition.z - theirPosition.z );
- offset.Normalize();
- // This scaler makes FindPositionAround bias towards our side
- offset = offset * (target->getGeometryInfo().getMajorRadius() / 2);
- workingPosition.x += offset.X;
- workingPosition.y += offset.Y;
- workingPosition.z += offset.Z;
- // this is a little cheesy... the idea is that we can only choose a location that is pretty close
- // in z to the desired one. this prevents us from choosing a space at the bottom of a cliff when
- // the space we want is at the top of the cliff. ideally we should do a funky terrain-zone compare
- // but that isn't well-exposed... (srj)
- const Real MAX_Z_DELTA = 10.0f;
- FindPositionOptions fpOptions;
- fpOptions.minRadius = 0.0f;
- fpOptions.maxRadius = 100.0f;
- fpOptions.sourceToPathToDest = me;// This makes it find a place forWhom can get to.
- if (!me->isUsingAirborneLocomotor())
- fpOptions.maxZDelta = MAX_Z_DELTA;
- if( me->isUsingAirborneLocomotor() )
- fpOptions.ignoreObject = target;// Flyers can ignore stuff, so they can approach right over the target if they want.
- Bool spotFound = ThePartitionManager->findPositionAround( &workingPosition, &fpOptions, &bestPosition );
- positionOut = spotFound ? bestPosition : workingPosition;
- return spotFound;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /*static*/ Object* DozerAIUpdate::findGoodBuildOrRepairPositionAndTarget(Object* me, Object* target, Coord3D& positionOut)
- {
- if (target->isKindOf(KINDOF_BRIDGE))
- {
- BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject(target);
- if (bbi)
- {
- AIUpdateInterface* ai = me->getAI();
- // have to repair at a tower.
- Real bestDistSqr = 1e10f;
- Object* bestTower = NULL;
- for (Int i = 0; i < BRIDGE_MAX_TOWERS; ++i)
- {
- Object* tower = TheGameLogic->findObjectByID(bbi->getTowerID((BridgeTowerType)i));
- if( tower )
- {
- Coord3D tmp;
- Bool found = findGoodBuildOrRepairPosition(me, tower, tmp);
- // do isPathAvail against the result of this, NOT the tower pos,
- // since towers are often in cliff cells.
- if (found && ai->isPathAvailable(&tmp))
- {
- Real thisDistSqr = sqr(me->getPosition()->x - tmp.x) + sqr(me->getPosition()->y - tmp.y);
- if (thisDistSqr < bestDistSqr)
- {
- positionOut = tmp;
- bestDistSqr = thisDistSqr;
- bestTower = tower;
- }
- }
- }
- }
- if (bestTower)
- return bestTower;
- DEBUG_CRASH(("should not happen, no reachable tower found"));
- return NULL;
- }
- }
- findGoodBuildOrRepairPosition(me, target, positionOut);
- return target;
- }
- //-------------------------------------------------------------------------------------------------
- /** Issue and order to the dozer */
- //-------------------------------------------------------------------------------------------------
- void DozerAIUpdate::newTask( DozerTask task, Object *target )
- {
- // sanity
- DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
- // sanity
- if( target == NULL )
- return;
- //
- // special check for the build task, we should never be given more than one of them ...
- // for the other tasks we just forget what we were doing and the new target takes
- // precedence for the task
- //
- if( task == DOZER_TASK_BUILD || task == DOZER_TASK_REPAIR )
- {
- // handle getting two tasks
- if( isTaskPending( task ) == TRUE )
- cancelTask( task );
- // get our object
- Object *me = getObject();
- Coord3D position;
- target = findGoodBuildOrRepairPositionAndTarget(me, target, position);
- if (target == NULL)
- return; // could happen for some bridges
- //
- // for building, we say that even "thinking" about building or rebuilding an object
- // sets us as the current builder of that object. this allows any dozers that are
- // ordered later to resume construction on something to see that somebody is already taking
- // care of it and then they won't be even try to resume a build since we don't allow
- // multiple dozers/workers to double up on construction efforts
- //
- if( task == DOZER_TASK_BUILD )
- target->setBuilder( me );
- m_dockPoint[ task ][ DOZER_DOCK_POINT_START ].valid = TRUE;
- m_dockPoint[ task ][ DOZER_DOCK_POINT_START ].location = position;
- m_dockPoint[ task ][ DOZER_DOCK_POINT_ACTION ].valid = TRUE;
- m_dockPoint[ task ][ DOZER_DOCK_POINT_ACTION ].location = position;
- Coord3D offset;
- offset.set(position.x-target->getPosition()->x, position.y-target->getPosition()->y, 0);
- offset.normalize();
- offset.scale(5*PATHFIND_CELL_SIZE_F);
- position.add(&offset); // move away from the dock point at the end of build.
- m_dockPoint[ task ][ DOZER_DOCK_POINT_END ].valid = TRUE;
- m_dockPoint[ task ][ DOZER_DOCK_POINT_END ].location = position;
- } // end if
- // set the new task target and the frame in which we got this order
- m_task[ task ].m_targetObjectID = target->getID();
- m_task[ task ].m_taskOrderFrame = TheGameLogic->getFrame();
- // reset the dozer behavior so that it can re-evluate which task to continue working on
- m_dozerMachine->resetToDefaultState();
- } // end newTask
- //-------------------------------------------------------------------------------------------------
- /** Cancel a task and reset the dozer behavior state machine so that it can
- * re-evaluate what it wants to do if it was working on the task being
- * cancelled */
- //-------------------------------------------------------------------------------------------------
- void DozerAIUpdate::cancelTask( DozerTask task )
- {
- // clear the order
- internalCancelTask( task );
- // reset the machine to we can re-evaluate what we want to do
- m_dozerMachine->resetToDefaultState();
- } // end cancelTask
- //-------------------------------------------------------------------------------------------------
- /** Is there a given task waiting to be done */
- //-------------------------------------------------------------------------------------------------
- Bool DozerAIUpdate::isTaskPending( DozerTask task )
- {
- // sanity
- DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
- return m_task[ task ].m_targetObjectID != 0 ? TRUE : FALSE;
-
- } // end isTaskPending
- //-------------------------------------------------------------------------------------------------
- /** Is there any task pending */
- //-------------------------------------------------------------------------------------------------
- Bool DozerAIUpdate::isAnyTaskPending( void )
- {
-
- for( Int i = 0; i < DOZER_NUM_TASKS; i++ )
- if( isTaskPending( (DozerTask)i ) )
- return TRUE;
- return FALSE;
- } // end isAnyTaskPending
- //-------------------------------------------------------------------------------------------------
- /** Get the target object of a given task */
- //-------------------------------------------------------------------------------------------------
- ObjectID DozerAIUpdate::getTaskTarget( DozerTask task )
- {
- // sanity
- DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
- return m_task[ task ].m_targetObjectID;
- } // end getTaskTarget
- //-------------------------------------------------------------------------------------------------
- /** Set a task as successfully completed */
- //-------------------------------------------------------------------------------------------------
- void DozerAIUpdate::internalTaskComplete( DozerTask task )
- {
- // sanity
- DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
- // call the single method that gets called for completing and canceling tasks
- internalTaskCompleteOrCancelled( task );
- // remove the info for this task
- m_task[ task ].m_targetObjectID = INVALID_ID;
- m_task[ task ].m_taskOrderFrame = 0;
- // remove dock point info for this task
- for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ )
- m_dockPoint[ task ][ i ].valid = FALSE;
- } // end internalTaskComplete
- //-------------------------------------------------------------------------------------------------
- /** Clear a task from the Dozer for consideration, we can use this when a goal object becomes
- * invalid/destroyed etc. */
- //-------------------------------------------------------------------------------------------------
- void DozerAIUpdate::internalCancelTask( DozerTask task )
- {
- // sanity
- DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
-
- if(task < 0 || task >= DOZER_NUM_TASKS)
- return; //DAMNIT! You CANNOT assert and then not handle the damn error! The. Code. Must. Not. Crash.
- // call the single method that gets called for completing and canceling tasks
- internalTaskCompleteOrCancelled( task );
- // remove the info for this task
- m_task[ task ].m_targetObjectID = INVALID_ID;
- m_task[ task ].m_taskOrderFrame = 0;
-
- // remove dock point info for this task
- for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ )
- m_dockPoint[ task ][ i ].valid = FALSE;
-
- // stop the dozer from moving
- AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
- ai->aiIdle( CMD_FROM_AI );
- } // end internalCancelTask
- // ------------------------------------------------------------------------------------------------
- /** This method is called whenever a task is completed *OR* cancelled */
- // ------------------------------------------------------------------------------------------------
- void DozerAIUpdate::internalTaskCompleteOrCancelled( DozerTask task )
- {
- switch( task )
- {
- // --------------------------------------------------------------------------------------------
- case DOZER_TASK_INVALID:
- {
- break; // do nothing, this is really no task
- } // end invalid
-
- // --------------------------------------------------------------------------------------------
- case DOZER_TASK_BUILD:
- {
- // the builder is no longer actively building something
- getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
- // And the thing we were working on is no longer being actively built
- ///@todo This would be correct except that we don't have idle crane animations and it is December.
- // Object* goalObject = TheGameLogic->findObjectByID(m_task[task].m_targetObjectID);
- // if (goalObject != NULL)
- // {
- // goalObject->clearModelConditionState(MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED);
- // }
- break;
-
- } // end build
- // --------------------------------------------------------------------------------------------
- case DOZER_TASK_REPAIR:
- {
- // the builder is no longer actively repairing something
- getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
- // Bridges have been made indestructible, so the code below was meaningless. -- ML
- // Object *obj = NULL;
- // get object to reapir (if present)
- //obj = TheGameLogic->findObjectByID( m_task[ task ].m_targetObjectID );
- //
- //if( obj )
- //{
- //
- //
- // when we're done repairing bridges, tell the scaffolding to go away and also remove
- // the undergoing repair status from the bridge object itself
- //
- // if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
- // {
- // // remove the bridge scaffold
- // removeBridgeScaffolding( obj );
- //
- // // clear the repair bit from the bridge
- // BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
- // DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower behavior interface\n") );
- // Object *bridge = TheGameLogic->findObjectByID( btbi->getBridgeID() );
- // DEBUG_ASSERTCRASH( bridge, ("Unable to find bridge object\n") );
- // bridge->clearStatus( OBJECT_STATUS_UNDERGOING_REPAIR );
- //
- // } // end if
- //
- //} // end if
- break;
- } // end repair
- // --------------------------------------------------------------------------------------------
- case DOZER_TASK_FORTIFY:
- {
- break;
- } // end fortify
- // --------------------------------------------------------------------------------------------
- default:
- {
- DEBUG_CRASH(( "internalTaskCompleteOrCancelled: Unknown Dozer task '%d'\n", task ));
- break;
- } // end default
- } // end switch( task )
- } // end internalTaskCompleteOrCancelled
- //-------------------------------------------------------------------------------------------------
- /** If we were building something, kill the active-construction flag on it */
- //-------------------------------------------------------------------------------------------------
- void DozerAIUpdate::onDelete( void )
- {
- Int i;
- // cancel any of the tasks we had queued up
- for( i = DOZER_TASK_FIRST; i < DOZER_NUM_TASKS; ++i )
- {
-
- if( isTaskPending( (DozerTask)i ) )
- cancelTask( (DozerTask)i );
-
- } // end for i
- for( i = 0; i < DOZER_NUM_TASKS; i++ )
- {
- Object* goalObject = TheGameLogic->findObjectByID(m_task[i].m_targetObjectID);
- if (goalObject != NULL)
- {
- goalObject->clearModelConditionState(MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED);
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Get the most recently issued task */
- //-------------------------------------------------------------------------------------------------
- DozerTask DozerAIUpdate::getMostRecentCommand( void )
- {
- Int i;
- DozerTask mostRecentTask = DOZER_TASK_INVALID;
- UnsignedInt mostRecentFrame = 0;
- for( i = 0; i < DOZER_NUM_TASKS; i++ )
- {
- if( isTaskPending( (DozerTask)i ) )
- {
- if( m_task[ i ].m_taskOrderFrame > mostRecentFrame )
- {
- mostRecentTask = (DozerTask)i;
- mostRecentFrame = m_task[ i ].m_taskOrderFrame;
- } // end if
- } // end if
- } // end for i
- return mostRecentTask;
-
- } // end getMostRecentCommand
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- const Coord3D* DozerAIUpdate::getDockPoint( DozerTask task, DozerDockPoint point )
- {
- // sanity
- if( task < 0 || task >= DOZER_NUM_TASKS )
- return NULL;
- // sanity
- if( point < 0 || point >= DOZER_NUM_DOCK_POINTS )
- return NULL;
- // if the point has been set (is valid) then return it
- if( m_dockPoint[ task ][ point ].valid )
- return &m_dockPoint[ task ][ point ].location;
- // no valid point has been set for this dock point on this task
- return NULL;
- } // end getDockPoint
- // ------------------------------------------------------------------------------------------------
- Real DozerAIUpdate::getRepairHealthPerSecond( void ) const
- {
- return getDozerAIUpdateModuleData()->m_repairHealthPercentPerSecond;
- }
- // ------------------------------------------------------------------------------------------------
- Real DozerAIUpdate::getBoredTime( void ) const
- {
- return getDozerAIUpdateModuleData()->m_boredTime;
- }
- // ------------------------------------------------------------------------------------------------
- Real DozerAIUpdate::getBoredRange( void ) const
- {
- if (getObject()->getControllingPlayer() &&
- getObject()->getControllingPlayer()->getPlayerType() == PLAYER_COMPUTER) {
- return TheAI->getAiData()->m_aiDozerBoredRadiusModifier*getDozerAIUpdateModuleData()->m_boredRange;
- }
- return getDozerAIUpdateModuleData()->m_boredRange;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- void DozerAIUpdate::aiDoCommand(const AICommandParms* parms)
- {
- //
- // anytime we get a command, just remove any model condition that has us actively building
- // if we need to show that, that bit will be set anyway again during the build process
- //
- getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
- if (!isAllowedToRespondToAiCommands(parms))
- return;
- // if we haven't made the dozer machine yet, do so now
- createMachines();
- switch( parms->m_cmd )
- {
- case AICMD_MOVE_AWAY_FROM_UNIT:
- {
- Object *otherObj = parms->m_obj;
- Bool otherIsDozer = false;
- if (otherObj) {
- otherIsDozer = otherObj->isKindOf(KINDOF_DOZER);
- }
- // We only want to do this if we aren't busy doing dozer things. jba.
- // Or if the other guy is a dozer too.
- if( !otherIsDozer && getCurrentTask() != DOZER_TASK_INVALID ) {
- return; // just ignore it. jba.
- }
- // issue the command
- AIUpdateInterface::aiDoCommand(parms);
- break;
- }
- // --------------------------------------------------------------------------------------------
- case AICMD_REPAIR:
- {
- // if we have no task right now, go idle so we can immediately respond to this
- if( getCurrentTask() == DOZER_TASK_INVALID )
- aiIdle( CMD_FROM_AI );
- // do the repair
- privateRepair(parms->m_obj, parms->m_cmdSource);
- break;
- } // end repair
- // --------------------------------------------------------------------------------------------
- case AICMD_RESUME_CONSTRUCTION:
- {
- // if we have no task right now, go idle so we can immediately respond to this
- if( getCurrentTask() == DOZER_TASK_INVALID )
- aiIdle( CMD_FROM_AI );
- // do the command
- privateResumeConstruction( parms->m_obj, parms->m_cmdSource );
- break;
- } // end resume construction
- // --------------------------------------------------------------------------------------------
- default:
- {
- // if this is from the player, cancel our current task
- if( parms->m_cmdSource == CMD_FROM_PLAYER && getCurrentTask() != DOZER_TASK_INVALID )
- cancelTask( getCurrentTask() );
- // issue the command
- AIUpdateInterface::aiDoCommand(parms);
- // when a player issues commands, this will cause the dozer to re-evaluate what it's doing
- if( parms->m_cmdSource == CMD_FROM_PLAYER )
- m_dozerMachine->resetToDefaultState();
- break;
- } // end default
- } // end switch
- } // end aiDoCommand
- //------------------------------------------------------------------------------------------------
- void DozerAIUpdate::startBuildingSound( const AudioEventRTS *sound, ObjectID constructionSiteID )
- {
- m_buildingSound = *sound;
- m_buildingSound.setObjectID( constructionSiteID );
- m_buildingSound.setPlayingHandle( TheAudio->addAudioEvent( &m_buildingSound ) );
- }
- //------------------------------------------------------------------------------------------------
- void DozerAIUpdate::finishBuildingSound()
- {
- TheAudio->removeAudioEvent( m_buildingSound.getPlayingHandle() );
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void DozerAIUpdate::crc( Xfer *xfer )
- {
- // extend base class
- AIUpdateInterface::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void DozerAIUpdate::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
-
- // extend base class
- AIUpdateInterface::xfer(xfer);
- Int numTasks = DOZER_NUM_TASKS;
- xfer->xferInt(&numTasks);
- if (numTasks != DOZER_NUM_TASKS) {
- DEBUG_CRASH(("DOZER_NUM_TASKS changed unexpectedly."));
- throw SC_INVALID_DATA;
- }
- Int i, j;
- for (i=0; i<DOZER_NUM_TASKS; i++) {
- xfer->xferObjectID(&m_task[i].m_targetObjectID);
- xfer->xferUnsignedInt(&m_task[i].m_taskOrderFrame);
- }
- xfer->xferSnapshot(m_dozerMachine);
- xfer->xferUser(&m_currentTask, sizeof(m_currentTask));
- Int dockPoints = DOZER_NUM_DOCK_POINTS;
- xfer->xferInt(&dockPoints);
- if (dockPoints!=DOZER_NUM_DOCK_POINTS) {
- DEBUG_CRASH(("DOZER_NUM_DOCK_POINTS changed unexpectedly."));
- throw SC_INVALID_DATA;
- }
- for (i=0; i<DOZER_NUM_TASKS; i++) {
- for (j=0; j<DOZER_NUM_DOCK_POINTS; j++) {
- xfer->xferBool(&m_dockPoint[i][j].valid);
- xfer->xferCoord3D(&m_dockPoint[i][j].location);
- }
- }
- xfer->xferUser(&m_buildSubTask, sizeof(m_buildSubTask));
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void DozerAIUpdate::loadPostProcess( void )
- {
- // extend base class
- AIUpdateInterface::loadPostProcess();
- } // end loadPostProcess
|