| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485 |
- /*
- ** 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: WorkerAIUpdate.cpp ///////////////////////////////////////////////////////////////////////
- // Author: Graham Smallwood, June 2002
- // Desc: A Worker is a unit that is both a Dozer and a Supply Truck.
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // 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/GameState.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/GlobalData.h"
- #include "Common/ResourceGatheringManager.h"
- #include "Common/Upgrade.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/GameText.h"
- #include "GameClient/InGameUI.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/BridgeBehavior.h"
- #include "GameLogic/Module/BridgeTowerBehavior.h"
- #include "GameLogic/Module/CreateModule.h"
- #include "GameLogic/Module/SupplyTruckAIUpdate.h"
- #include "GameLogic/Module/SupplyCenterDockUpdate.h"
- #include "GameLogic/Module/SupplyWarehouseDockUpdate.h"
- #include "GameLogic/Module/WorkerAIUpdate.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- // FORWARD DECLARATIONS ///////////////////////////////////////////////////////////////////////////
- enum
- {
- AS_DOZER, ///< When not actively Gathering, or when actively building, I am a dozer
- AS_SUPPLY_TRUCK ///< When told explicitly by player or other object, I become a supply truck
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- WorkerAIUpdate::WorkerAIUpdate( Thing *thing, const ModuleData* moduleData ) :
- AIUpdateInterface( thing, moduleData )
-
- {
- //
- // 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
- //
- //Added By Sadullah Nader
- //Initialization(s) inserted
- m_isRebuild = FALSE;
- //
- m_dozerMachine = NULL;
- for( Int i = 0; i < DOZER_NUM_TASKS; i++ )
- {
- m_task[ i ].m_targetObjectID = INVALID_ID;
- m_task[ i ].m_taskOrderFrame = 0;
- for( Int j = 0; j < DOZER_NUM_DOCK_POINTS; j++ )
- {
- m_dockPoint[ i ][ j ].valid = FALSE;
- m_dockPoint[ i ][ j ].location.zero();
- }
- }
- m_currentTask = DOZER_TASK_INVALID;
- m_buildSubTask = DOZER_SELECT_BUILD_DOCK_LOCATION; // irrelavant, but I want non-garbage value
-
- m_supplyTruckStateMachine = NULL;
- m_numberBoxes = 0;
- m_forcePending = FALSE;
- m_forcedBusyPending = FALSE;
- m_workerMachine = NULL;
- m_suppliesDepletedVoice = getWorkerAIUpdateModuleData()->m_suppliesDepletedVoice;
-
- createMachines();
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- WorkerAIUpdate::~WorkerAIUpdate( void )
- {
- // delete our behavior state machine
- if( m_dozerMachine )
- m_dozerMachine->deleteInstance();
- if( m_supplyTruckStateMachine )
- m_supplyTruckStateMachine->deleteInstance();
- if( m_workerMachine )
- m_workerMachine->deleteInstance();
- }
- //-------------------------------------------------------------------------------------------------
- Bool WorkerAIUpdate::isCurrentlyFerryingSupplies() const
- {
- if (m_supplyTruckStateMachine)
- {
- switch (m_supplyTruckStateMachine->getCurrentStateID())
- {
- case ST_IDLE:
- case ST_BUSY:
- case ST_REGROUPING:
- return false;
- case ST_WANTING:
- case ST_DOCKING:
- return true;
- }
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- Bool WorkerAIUpdate::isAvailableForSupplying() const
- {
- return true;
- }
- // ------------------------------------------------------------------------------------------------
- Real WorkerAIUpdate::getRepairHealthPerSecond( void ) const
- {
- return getWorkerAIUpdateModuleData()->m_repairHealthPercentPerSecond;
- }
- // ------------------------------------------------------------------------------------------------
- Real WorkerAIUpdate::getBoredTime( void ) const
- {
- return getWorkerAIUpdateModuleData()->m_boredTime;
- }
- // ------------------------------------------------------------------------------------------------
- Real WorkerAIUpdate::getBoredRange( void ) const
- {
- return getWorkerAIUpdateModuleData()->m_boredRange;
- }
- // ------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::createMachines( void )
- {
- if( m_workerMachine == NULL )
- {
- m_workerMachine = newInstance(WorkerStateMachine)( getObject() );
- if( m_dozerMachine == NULL )
- {
- m_dozerMachine = newInstance(DozerPrimaryStateMachine)( getObject() );
- m_dozerMachine->initDefaultState();
- }
- if( m_supplyTruckStateMachine == NULL )
- {
- m_supplyTruckStateMachine = newInstance(SupplyTruckStateMachine)( getObject() );
- m_supplyTruckStateMachine->initDefaultState();
- }
- m_workerMachine->initDefaultState();// this has to wait until all three are in place since
- // an immediate transition check will ask questions of the machines.
- //#ifdef _DEBUG
- // m_workerMachine->setDebugOutput(TRUE);
- // m_dozerMachine->setDebugOutput(TRUE);
- // m_supplyTruckStateMachine->setDebugOutput(TRUE);
- //#endif
- }
- }
- //-------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------
- UnsignedInt WorkerAIUpdate::getActionDelayForDock( Object *dock )
- {
- // Decide whether to use my Center or Warehouse delay time
- static const NameKeyType key_warehouseUpdate = NAMEKEY("SupplyWarehouseDockUpdate");
- SupplyWarehouseDockUpdate *warehouseModule = (SupplyWarehouseDockUpdate*) dock->findUpdateModule( key_warehouseUpdate );
- if (warehouseModule) {
- return getWorkerAIUpdateModuleData()->m_warehouseDelay;
- }
- static const NameKeyType key_centerUpdate = NAMEKEY("SupplyCenterDockUpdate");
- SupplyCenterDockUpdate *centerModule = (SupplyCenterDockUpdate*) dock->findUpdateModule( key_centerUpdate );
- if (centerModule) {
- return getWorkerAIUpdateModuleData()->m_centerDelay;
- }
- return 0;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Real WorkerAIUpdate::getWarehouseScanDistance() const
- {
- // Ai players get larger scan range. jba.
- if (getObject()->getControllingPlayer()->getPlayerType() == PLAYER_COMPUTER) {
- return 2 * getWorkerAIUpdateModuleData()->m_warehouseScanDistance;
- }
- return getWorkerAIUpdateModuleData()->m_warehouseScanDistance;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime WorkerAIUpdate::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
- //
- // create all the machines if they don't yet exist
- createMachines();
- // DO NOT set us as being to able to move with super precision off grid locations
- // Causes workers to get stuck. jba.
- //if( getCurLocomotor() )
- //getCurLocomotor()->setUltraAccurate( TRUE );
- // extend the normal AI system
- AIUpdateInterface::update();
- // do nothing if we're dead
- ///@todo shouldn't this be at a higher level?
- if( getObject()->isEffectivelyDead() )
- return UPDATE_SLEEP_NONE;
- // run our own state machine, and the appropriate sub machine
- m_workerMachine->updateStateMachine();
- if( m_workerMachine->getCurrentStateID() == AS_DOZER )
- {
- // 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
- // update dozer behavior
- m_dozerMachine->updateStateMachine();
- } // end if
- else
- {
- m_supplyTruckStateMachine->updateStateMachine();
- // If we are harvesting, we can be diverted to clear mines. jba.
- getObject()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
- }
- return UPDATE_SLEEP_NONE;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /** The entry point of a construct command to the Dozer */
- //-------------------------------------------------------------------------------------------------
- Object *WorkerAIUpdate::construct( const ThingTemplate *what,
- const Coord3D *pos,
- Real angle,
- Player *owningPlayer,
- Bool isRebuild )
- {
- // !!! NOTE: If you modify this you must modify the dozer too !!!
- // !!! Graham: Please please please have inspiration for how to *not* duplicate this code
- // GS - Construct needs to be an AI primitive. Inheriting off of AIUpdate means you are writing a
- // master brain that will call AI primitives on the object, not something that does stuff itself.
- // SupplyTruckAI decides who to call AIDock on. Worker should decide to AIDock or AIConstruct
- // or AIRepair. Dozer should just use the latter two. No construction logic should be in
- // the inherited AIUpdates at all.
- // 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 )
- {
- // validate the the position to build at is valid
- if( TheBuildAssistant->isLocationLegalToBuild( pos, what, angle,
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::NO_OBJECT_OVERLAP,
- getObject(), NULL ) != LBC_OK )
- return NULL;
-
- } // 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
- 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() );
- // leave the supply truck state and now behave like a dozer.
- exitingSupplyTruckState();
-
- // take the required money away from the player
- if( isRebuild == FALSE )
- {
- Money *money = owningPlayer->getMoney();
- money->withdraw( what->calcCostToBuild( owningPlayer ) );
- } // end if
-
- //
- // set a bit that this object is under construction, it is important to do this early
- // before the hooks add/subtract power from a player are executed
- //
- obj->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNDER_CONSTRUCTION ) );
- // 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;
-
- }
- // ------------------------------------------------------------------------------------------------
- /** We just exited from a supply truck task and are now idle, we should go back to Dozer idle
- mode */
- // ------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::exitingSupplyTruckState()
- {
- if( m_workerMachine->getCurrentStateID() == AS_SUPPLY_TRUCK )
- {
- // We've been given a Dozer specific order that the Supply Truck machine doesn't recognize
- // as BUSY (because this command also recognizes its own busy and is likewise waiting).
- // Explicitly slap it upside the head.
- if( getObject()->getAIUpdateInterface() )
- {
- getObject()->getAIUpdateInterface()->aiIdle(CMD_FROM_AI);
- }
- m_workerMachine->setState( AS_DOZER );
- // To clarify, I leave supply truck mode when I notice I am doing something not supply
- // truck related. When given a construct command, I wait to do anything until I notice
- // I'm not busy. Both states are being polite, so I must force the switch.
- }
- }
-
- // ------------------------------------------------------------------------------------------------
- /** Given our current task and repair target, can we accept this as a new repair target */
- // ------------------------------------------------------------------------------------------------
- Bool WorkerAIUpdate::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
- //----------------------------------------------------------------------------------------
- void WorkerAIUpdate::privateIdle(CommandSourceType cmdSource)
- {
- // Leaving this commented out to show that although the regular supply truck does this, the
- // worker's dozer brain will get completely screwed.
- // If the user gives a stop command, I have to turn off autopilot
- // if( cmdSource == CMD_FROM_PLAYER )
- // setForceBusyState(TRUE);
-
- AIUpdateInterface::privateIdle(cmdSource);
- }
- //----------------------------------------------------------------------------------------
- void WorkerAIUpdate::privateDock( Object *dock, CommandSourceType cmdSource )
- {
- AIUpdateInterface::privateDock( dock, cmdSource );
- // If this is a command from a player, I will remember this as my favorite dock to override
- // ResourceManager searches.
- if ((cmdSource == CMD_FROM_PLAYER) && dock)
- {
- // Please note, there is not a separate Warehouse and Center memory by Design. Because
- // we lack a UI way to click Warehouse and drag to center to set up a specific path, the
- // practical realization has been made that you do not want separate memory.
- m_preferredDock = dock->getID();
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::privateRepair( Object *obj, CommandSourceType cmdSource )
- {
- Object *dozer = getObject();
- // sanity, if we can't repair the object then get out of there
- if( TheActionManager->canRepairObject( dozer, obj, cmdSource ) == FALSE )
- return;
- // if we are already repairing this target do nothing
- if( canAcceptNewRepair( obj ) == FALSE )
- return;
- //
- // if this object has already been targeted for repair by an object we won't also try to
- // go repair it
- ObjectID currentRepairer = obj->getSoleHealingBenefactor();
- if( currentRepairer != INVALID_ID && currentRepairer != dozer->getID() )
- return;
- // start the new task
- newTask( DOZER_TASK_REPAIR, obj );
- } // end privateRepair
- // ------------------------------------------------------------------------------------------------
- /** Resume construction on a building */
- // ------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::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
- //-------------------------------------------------------------------------------------------------
- /** Issue and order to the dozer */
- //-------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::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;
- m_preferredDock = INVALID_ID; // If we are dozing, we don't want any supply truck stuff going on. jba.
- //
- // 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 = DozerAIUpdate::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;
- m_dockPoint[ task ][ DOZER_DOCK_POINT_END ].valid = TRUE;
- m_dockPoint[ task ][ DOZER_DOCK_POINT_END ].location = position;
- } // end if, build task
- // 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();
- // reset the workermachine, if we've been acting like a supply truck
- if( m_workerMachine->getCurrentStateID() == AS_SUPPLY_TRUCK )
- {
- // We've been given a Dozer specific order that the Supply Truck machine doesn't recognize
- // as BUSY (because this command also recognizes its own busy and is likewise waiting).
- // Explicitly slap it upside the head.
- if( getObject()->getAIUpdateInterface() )
- {
- getObject()->getAIUpdateInterface()->aiIdle(CMD_FROM_AI);
- }
- m_workerMachine->setState( AS_DOZER );
- // To clarify, I leave supply truck mode when I notice I am doing something not supply
- // truck related. When given a construct command, I wait to do anything until I notice
- // I'm not busy. Both states are being polite, so I must force the switch.
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** 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 WorkerAIUpdate::cancelTask( DozerTask task )
- {
- // clear the order
- internalCancelTask( task );
- // reset the machine to we can re-evaluate what we want to do
- m_dozerMachine->resetToDefaultState();
- }
- //-------------------------------------------------------------------------------------------------
- /** Is there a given task waiting to be done */
- //-------------------------------------------------------------------------------------------------
- Bool WorkerAIUpdate::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;
-
- }
- //-------------------------------------------------------------------------------------------------
- /** Is there any task pending */
- //-------------------------------------------------------------------------------------------------
- Bool WorkerAIUpdate::isAnyTaskPending( void )
- {
-
- for( Int i = 0; i < DOZER_NUM_TASKS; i++ )
- if( isTaskPending( (DozerTask)i ) )
- return TRUE;
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- /** Get the target object of a given task */
- //-------------------------------------------------------------------------------------------------
- ObjectID WorkerAIUpdate::getTaskTarget( DozerTask task )
- {
- // sanity
- DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
- return m_task[ task ].m_targetObjectID;
- }
- //-------------------------------------------------------------------------------------------------
- /** Set a task as successfully completed */
- //-------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::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;
- }
- //-------------------------------------------------------------------------------------------------
- /** Clear a task from the Dozer for consideration, we can use this when a goal object becomes
- * invalid/destroyed etc. */
- //-------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::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();
- if( !ai )
- {
- return;
- }
- /// @todo we really need a stop command instead of making it move to it's current location
- ai->aiMoveToPosition( getObject()->getPosition(), CMD_FROM_AI );
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::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:
- {
- Object *obj = NULL;
- // the builder is no longer actively repairing something
- getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
- // 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
- if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
- removeBridgeScaffolding( obj );
- } // 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 )
- }
- //-------------------------------------------------------------------------------------------------
- /** If we were building something, kill the active-construction flag on it */
- //-------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::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 WorkerAIUpdate::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;
- }
- }
- }
- return mostRecentTask;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- const Coord3D* WorkerAIUpdate::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
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::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;
-
- // create our machines if they don't yet exist
- createMachines();
- switch( parms->m_cmd )
- {
- // --------------------------------------------------------------------------------------------
- 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
- if (isClearingMines() && m_numberBoxes > 0 )
- {
- // if clearing mines, we drop any boxes we were carrying
- m_numberBoxes = 0;
- Drawable *draw = getObject()->getDrawable();
- if( draw )
- {
- draw->updateDrawableSupplyStatus( getWorkerAIUpdateModuleData()->m_maxBoxesData, m_numberBoxes );
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // SupplyTruck stuff
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool WorkerAIUpdate::loseOneBox()
- {
- if( m_numberBoxes == 0 )
- return FALSE;
- m_numberBoxes--;
- Drawable *draw = getObject()->getDrawable();
- if( draw )
- {
- draw->updateDrawableSupplyStatus( getWorkerAIUpdateModuleData()->m_maxBoxesData, m_numberBoxes );
- }
- return TRUE;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool WorkerAIUpdate::gainOneBox( Int remainingStock )
- {
- if( getWorkerAIUpdateModuleData() && m_numberBoxes >= getWorkerAIUpdateModuleData()->m_maxBoxesData )
- return FALSE;
- ++m_numberBoxes;
- //if I just took the last box,
- //i will announce that this supply source is now empty
- if (remainingStock == 0)
- {
- Object* bestWarehouse = getObject()->getControllingPlayer()->getResourceGatheringManager()->findBestSupplyWarehouse( getObject() );
-
- Bool playDepleted = FALSE;
- if ( bestWarehouse )
- {
- //figure out whether the best one is considerably far from the previous one (current position)
- Coord3D delta = *getObject()->getPosition();
- delta.sub( bestWarehouse->getPosition() );
- if ( delta.length() > getWarehouseScanDistance()/4)
- playDepleted = TRUE;
- }
- else
- playDepleted = TRUE;
- if (playDepleted && m_suppliesDepletedVoice.getEventName().isEmpty() == false)
- {
- m_suppliesDepletedVoice.setObjectID(getObject()->getID());
- m_suppliesDepletedVoice.setPlayingHandle(TheAudio->addAudioEvent(&m_suppliesDepletedVoice));
- }
- }
- Drawable *draw = getObject()->getDrawable();
- if( draw )
- {
- draw->updateDrawableSupplyStatus( getWorkerAIUpdateModuleData()->m_maxBoxesData, m_numberBoxes );
- }
- return TRUE;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool WorkerAIUpdate::isSupplyTruckBrainActiveAndBusy()
- {
- return (m_workerMachine->getCurrentStateID() == AS_SUPPLY_TRUCK)
- && (m_supplyTruckStateMachine->getCurrentStateID() == ST_BUSY);
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::resetSupplyTruckBrain()
- {
- m_supplyTruckStateMachine->resetToDefaultState();
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::resetDozerBrain()
- {
- m_dozerMachine->resetToDefaultState();
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- // Worker's master state machine, that controls Dozerness or Supplytruckness
- class ActAsDozerState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ActAsDozerState, "ActAsDozerState")
- protected:
- // snapshot interface STUBBED.
- virtual void crc( Xfer *xfer ){};
- virtual void xfer( Xfer *xfer ){};
- virtual void loadPostProcess(){};
- public:
- ActAsDozerState( StateMachine *machine ) :State( machine, "ActAsDozerState" ){}
- virtual StateReturnType onEnter();
- virtual StateReturnType update();
- virtual StateReturnType onExit();
- };
- EMPTY_DTOR(ActAsDozerState)
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- class ActAsSupplyTruckState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ActAsSupplyTruckState, "ActAsSupplyTruckState")
- protected:
- // snapshot interface STUBBED.
- virtual void crc( Xfer *xfer ){};
- virtual void xfer( Xfer *xfer ){};
- virtual void loadPostProcess(){};
- public:
- ActAsSupplyTruckState( StateMachine *machine ) :State( machine, "ActAsSupplyTruckState" ){}
- virtual StateReturnType onEnter();
- virtual StateReturnType update();
- virtual StateReturnType onExit();
- };
- EMPTY_DTOR(ActAsSupplyTruckState)
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- WorkerStateMachine::WorkerStateMachine( Object *owner ) : StateMachine( owner, "WorkerStateMachine" )
- {
- static const StateConditionInfo asDozerConditions[] =
- {
- StateConditionInfo(supplyTruckSubMachineWantsToEnter, AS_SUPPLY_TRUCK, NULL),
- StateConditionInfo(NULL, NULL, NULL) // keep last
- };
- static const StateConditionInfo asTruckConditions[] =
- {
- StateConditionInfo(supplyTruckSubMachineReadyToLeave, AS_DOZER, NULL),
- StateConditionInfo(NULL, NULL, NULL) // keep last
- };
- // order matters: first state is the default state.
- defineState( AS_DOZER, newInstance(ActAsDozerState)( this ), INVALID_STATE_ID, INVALID_STATE_ID, asDozerConditions );
- defineState( AS_SUPPLY_TRUCK, newInstance(ActAsSupplyTruckState)( this ), INVALID_STATE_ID, INVALID_STATE_ID, asTruckConditions );
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- WorkerStateMachine::~WorkerStateMachine()
- {
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void WorkerStateMachine::crc( Xfer *xfer )
- {
- StateMachine::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void WorkerStateMachine::xfer( Xfer *xfer )
- {
- XferVersion cv = 1;
- XferVersion v = cv;
- xfer->xferVersion( &v, cv );
- StateMachine::xfer(xfer);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void WorkerStateMachine::loadPostProcess( void )
- {
- StateMachine::loadPostProcess();
- } // end loadPostProcess
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool WorkerStateMachine::supplyTruckSubMachineWantsToEnter( State *thisState, void* userData )
- {
- Object *owner = thisState->getMachineOwner();
- WorkerAIUpdate *update = (WorkerAIUpdate*)owner->getAIUpdateInterface();
- if( !update )
- {
- return false;
- }
- AIStateType masterState = update->getAIStateType();
- //If I detect a Supply force message, or if I have been put straight in dock,
- //then the worker master part of me wants to switch to the Supply sub-brain
- return update->isForcedIntoWantingState() || (masterState == AI_DOCK);
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool WorkerStateMachine::supplyTruckSubMachineReadyToLeave( State *thisState, void* userData )
- {
- Object *owner = thisState->getMachineOwner();
- WorkerAIUpdate *update = (WorkerAIUpdate*)owner->getAIUpdateInterface();
- if( !update )
- {
- return false;
- }
- // AIStateType masterState = update->getAIStateType();
- // It isn't ready to leave if it is on its way in. The first clause
- // allow a latch for a moment
- // so there is no transition out on the way in. Active and Busy means it isn't doing
- // anything Supply related.
- return !supplyTruckSubMachineWantsToEnter( thisState, NULL )
- && update->isSupplyTruckBrainActiveAndBusy();
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- StateReturnType ActAsDozerState::onEnter()
- {
- return STATE_CONTINUE;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- StateReturnType ActAsDozerState::update()
- {
- return STATE_CONTINUE;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- StateReturnType ActAsDozerState::onExit()
- {
- Object *owner = getMachineOwner();
- WorkerAIUpdate *update = (WorkerAIUpdate*)owner->getAIUpdateInterface();
- if( !update )
- {
- return STATE_FAILURE;
- }
- update->resetDozerBrain();
- return STATE_CONTINUE;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- StateReturnType ActAsSupplyTruckState::onEnter()
- {
- return STATE_CONTINUE;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- StateReturnType ActAsSupplyTruckState::update()
- {
- return STATE_CONTINUE;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- StateReturnType ActAsSupplyTruckState::onExit()
- {
- Object *owner = getMachineOwner();
- WorkerAIUpdate *update = (WorkerAIUpdate*)owner->getAIUpdateInterface();
- if( !update )
- {
- return STATE_FAILURE;
- }
- update->resetSupplyTruckBrain();
- return STATE_CONTINUE;
- }
- // ------------------------------------------------------------------------------------------------
- /** Create the bridge scaffolding if necessary for the bridge that is attached to this tower */
- // ------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::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 WorkerAIUpdate::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
- //------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::startBuildingSound( const AudioEventRTS *sound, ObjectID constructionSiteID )
- {
- m_buildingSound = *sound;
- m_buildingSound.setObjectID( constructionSiteID );
- m_buildingSound.setPlayingHandle( TheAudio->addAudioEvent( &m_buildingSound ) );
- }
- //------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::finishBuildingSound()
- {
- TheAudio->removeAudioEvent( m_buildingSound.getPlayingHandle() );
- }
- //------------------------------------------------------------------------------------------------
- Int WorkerAIUpdate::getUpgradedSupplyBoost() const
- {
- Player *player = getObject()->getControllingPlayer();
- static const UpgradeTemplate *workerShoeTemplate = TheUpgradeCenter->findUpgrade( "Upgrade_GLAWorkerShoes" );
-
- if (player && workerShoeTemplate && player->hasUpgradeComplete(workerShoeTemplate))
- return getWorkerAIUpdateModuleData()->m_upgradedSupplyBoost;
- else
- return 0;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::crc( Xfer *xfer )
- {
- // extend base class
- AIUpdateInterface::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::xfer( Xfer *xfer )
- {
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
-
- // extend base class
- AIUpdateInterface::xfer(xfer);
- //------------------------- xfer Dozer info
- 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));
- //------------------------- xfer Supply Truck info
- xfer->xferSnapshot(m_supplyTruckStateMachine);
- xfer->xferObjectID(&m_preferredDock);
- xfer->xferInt(&m_numberBoxes);
- xfer->xferBool(&m_forcePending);
- //-------------------------- xfer Worker info
- xfer->xferSnapshot(m_workerMachine);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void WorkerAIUpdate::loadPostProcess( void )
- {
- // extend base class
- AIUpdateInterface::loadPostProcess();
- } // end loadPostProcess
|