WorkerAIUpdate.cpp 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: WorkerAIUpdate.cpp ///////////////////////////////////////////////////////////////////////
  24. // Author: Graham Smallwood, June 2002
  25. // Desc: A Worker is a unit that is both a Dozer and a Supply Truck.
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/ActionManager.h"
  30. #include "Common/Team.h"
  31. #include "Common/StateMachine.h"
  32. #include "Common/BuildAssistant.h"
  33. #include "Common/GameState.h"
  34. #include "Common/ThingTemplate.h"
  35. #include "Common/ThingFactory.h"
  36. #include "Common/Player.h"
  37. #include "Common/Money.h"
  38. #include "Common/Radar.h"
  39. #include "Common/RandomValue.h"
  40. #include "Common/GlobalData.h"
  41. #include "Common/ResourceGatheringManager.h"
  42. #include "Common/Upgrade.h"
  43. #include "GameClient/Drawable.h"
  44. #include "GameClient/GameText.h"
  45. #include "GameClient/InGameUI.h"
  46. #include "GameLogic/AIPathfind.h"
  47. #include "GameLogic/Locomotor.h"
  48. #include "GameLogic/PartitionManager.h"
  49. #include "GameLogic/Module/BodyModule.h"
  50. #include "GameLogic/Module/BridgeBehavior.h"
  51. #include "GameLogic/Module/BridgeTowerBehavior.h"
  52. #include "GameLogic/Module/CreateModule.h"
  53. #include "GameLogic/Module/SupplyTruckAIUpdate.h"
  54. #include "GameLogic/Module/SupplyCenterDockUpdate.h"
  55. #include "GameLogic/Module/SupplyWarehouseDockUpdate.h"
  56. #include "GameLogic/Module/WorkerAIUpdate.h"
  57. #ifdef _INTERNAL
  58. // for occasional debugging...
  59. //#pragma optimize("", off)
  60. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  61. #endif
  62. // FORWARD DECLARATIONS ///////////////////////////////////////////////////////////////////////////
  63. enum
  64. {
  65. AS_DOZER, ///< When not actively Gathering, or when actively building, I am a dozer
  66. AS_SUPPLY_TRUCK ///< When told explicitly by player or other object, I become a supply truck
  67. };
  68. ///////////////////////////////////////////////////////////////////////////////////////////////////
  69. ///////////////////////////////////////////////////////////////////////////////////////////////////
  70. ///////////////////////////////////////////////////////////////////////////////////////////////////
  71. //-------------------------------------------------------------------------------------------------
  72. //-------------------------------------------------------------------------------------------------
  73. WorkerAIUpdate::WorkerAIUpdate( Thing *thing, const ModuleData* moduleData ) :
  74. AIUpdateInterface( thing, moduleData )
  75. {
  76. //
  77. // initialize the dozer machine to NULL, we want to do this and create it during the update
  78. // implementation because at this point we don't have the object all setup
  79. //
  80. //Added By Sadullah Nader
  81. //Initialization(s) inserted
  82. m_isRebuild = FALSE;
  83. //
  84. m_dozerMachine = NULL;
  85. for( Int i = 0; i < DOZER_NUM_TASKS; i++ )
  86. {
  87. m_task[ i ].m_targetObjectID = INVALID_ID;
  88. m_task[ i ].m_taskOrderFrame = 0;
  89. for( Int j = 0; j < DOZER_NUM_DOCK_POINTS; j++ )
  90. {
  91. m_dockPoint[ i ][ j ].valid = FALSE;
  92. m_dockPoint[ i ][ j ].location.zero();
  93. }
  94. }
  95. m_currentTask = DOZER_TASK_INVALID;
  96. m_buildSubTask = DOZER_SELECT_BUILD_DOCK_LOCATION; // irrelavant, but I want non-garbage value
  97. m_supplyTruckStateMachine = NULL;
  98. m_numberBoxes = 0;
  99. m_forcePending = FALSE;
  100. m_forcedBusyPending = FALSE;
  101. m_workerMachine = NULL;
  102. m_suppliesDepletedVoice = getWorkerAIUpdateModuleData()->m_suppliesDepletedVoice;
  103. createMachines();
  104. }
  105. //-------------------------------------------------------------------------------------------------
  106. //-------------------------------------------------------------------------------------------------
  107. WorkerAIUpdate::~WorkerAIUpdate( void )
  108. {
  109. // delete our behavior state machine
  110. if( m_dozerMachine )
  111. m_dozerMachine->deleteInstance();
  112. if( m_supplyTruckStateMachine )
  113. m_supplyTruckStateMachine->deleteInstance();
  114. if( m_workerMachine )
  115. m_workerMachine->deleteInstance();
  116. }
  117. //-------------------------------------------------------------------------------------------------
  118. Bool WorkerAIUpdate::isCurrentlyFerryingSupplies() const
  119. {
  120. if (m_supplyTruckStateMachine)
  121. {
  122. switch (m_supplyTruckStateMachine->getCurrentStateID())
  123. {
  124. case ST_IDLE:
  125. case ST_BUSY:
  126. case ST_REGROUPING:
  127. return false;
  128. case ST_WANTING:
  129. case ST_DOCKING:
  130. return true;
  131. }
  132. }
  133. return false;
  134. }
  135. //-------------------------------------------------------------------------------------------------
  136. Bool WorkerAIUpdate::isAvailableForSupplying() const
  137. {
  138. return true;
  139. }
  140. // ------------------------------------------------------------------------------------------------
  141. Real WorkerAIUpdate::getRepairHealthPerSecond( void ) const
  142. {
  143. return getWorkerAIUpdateModuleData()->m_repairHealthPercentPerSecond;
  144. }
  145. // ------------------------------------------------------------------------------------------------
  146. Real WorkerAIUpdate::getBoredTime( void ) const
  147. {
  148. return getWorkerAIUpdateModuleData()->m_boredTime;
  149. }
  150. // ------------------------------------------------------------------------------------------------
  151. Real WorkerAIUpdate::getBoredRange( void ) const
  152. {
  153. return getWorkerAIUpdateModuleData()->m_boredRange;
  154. }
  155. // ------------------------------------------------------------------------------------------------
  156. void WorkerAIUpdate::createMachines( void )
  157. {
  158. if( m_workerMachine == NULL )
  159. {
  160. m_workerMachine = newInstance(WorkerStateMachine)( getObject() );
  161. if( m_dozerMachine == NULL )
  162. {
  163. m_dozerMachine = newInstance(DozerPrimaryStateMachine)( getObject() );
  164. m_dozerMachine->initDefaultState();
  165. }
  166. if( m_supplyTruckStateMachine == NULL )
  167. {
  168. m_supplyTruckStateMachine = newInstance(SupplyTruckStateMachine)( getObject() );
  169. m_supplyTruckStateMachine->initDefaultState();
  170. }
  171. m_workerMachine->initDefaultState();// this has to wait until all three are in place since
  172. // an immediate transition check will ask questions of the machines.
  173. //#ifdef _DEBUG
  174. // m_workerMachine->setDebugOutput(TRUE);
  175. // m_dozerMachine->setDebugOutput(TRUE);
  176. // m_supplyTruckStateMachine->setDebugOutput(TRUE);
  177. //#endif
  178. }
  179. }
  180. //-------------------------------------------------------------------------------------------------
  181. //----------------------------------------------------------------------------------------
  182. UnsignedInt WorkerAIUpdate::getActionDelayForDock( Object *dock )
  183. {
  184. // Decide whether to use my Center or Warehouse delay time
  185. static const NameKeyType key_warehouseUpdate = NAMEKEY("SupplyWarehouseDockUpdate");
  186. SupplyWarehouseDockUpdate *warehouseModule = (SupplyWarehouseDockUpdate*) dock->findUpdateModule( key_warehouseUpdate );
  187. if (warehouseModule) {
  188. return getWorkerAIUpdateModuleData()->m_warehouseDelay;
  189. }
  190. static const NameKeyType key_centerUpdate = NAMEKEY("SupplyCenterDockUpdate");
  191. SupplyCenterDockUpdate *centerModule = (SupplyCenterDockUpdate*) dock->findUpdateModule( key_centerUpdate );
  192. if (centerModule) {
  193. return getWorkerAIUpdateModuleData()->m_centerDelay;
  194. }
  195. return 0;
  196. }
  197. //-------------------------------------------------------------------------------------------------
  198. //-------------------------------------------------------------------------------------------------
  199. Real WorkerAIUpdate::getWarehouseScanDistance() const
  200. {
  201. // Ai players get larger scan range. jba.
  202. if (getObject()->getControllingPlayer()->getPlayerType() == PLAYER_COMPUTER) {
  203. return 2 * getWorkerAIUpdateModuleData()->m_warehouseScanDistance;
  204. }
  205. return getWorkerAIUpdateModuleData()->m_warehouseScanDistance;
  206. }
  207. //-------------------------------------------------------------------------------------------------
  208. //-------------------------------------------------------------------------------------------------
  209. UpdateSleepTime WorkerAIUpdate::update( void )
  210. {
  211. //
  212. // NOTE: Any changes to DozerAIUpdate::* you probably want to reflect and copy into
  213. // WorkerAIUPdate:* as well ... sigh
  214. //
  215. //
  216. // now that we're really executing we have all the necessary object modules in place to
  217. // correctly create a state machine and set the default state
  218. //
  219. // create all the machines if they don't yet exist
  220. createMachines();
  221. // DO NOT set us as being to able to move with super precision off grid locations
  222. // Causes workers to get stuck. jba.
  223. //if( getCurLocomotor() )
  224. //getCurLocomotor()->setUltraAccurate( TRUE );
  225. // extend the normal AI system
  226. AIUpdateInterface::update();
  227. // do nothing if we're dead
  228. ///@todo shouldn't this be at a higher level?
  229. if( getObject()->isEffectivelyDead() )
  230. return UPDATE_SLEEP_NONE;
  231. // run our own state machine, and the appropriate sub machine
  232. m_workerMachine->updateStateMachine();
  233. if( m_workerMachine->getCurrentStateID() == AS_DOZER )
  234. {
  235. // get and validate our current task
  236. DozerTask currentTask = getCurrentTask();
  237. if( currentTask != DOZER_TASK_INVALID )
  238. {
  239. ObjectID taskTarget = getTaskTarget( currentTask );
  240. Object *targetObject = TheGameLogic->findObjectByID( taskTarget );
  241. Bool invalidTask = FALSE;
  242. // validate the task and the target
  243. if( currentTask == DOZER_TASK_REPAIR &&
  244. TheActionManager->canRepairObject( getObject(), targetObject, getLastCommandSource() ) == FALSE )
  245. invalidTask = TRUE;
  246. // cancel the task if it's now invalid
  247. if( invalidTask == TRUE )
  248. cancelTask( currentTask );
  249. } // end if
  250. // update dozer behavior
  251. m_dozerMachine->updateStateMachine();
  252. } // end if
  253. else
  254. {
  255. m_supplyTruckStateMachine->updateStateMachine();
  256. // If we are harvesting, we can be diverted to clear mines. jba.
  257. getObject()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
  258. }
  259. return UPDATE_SLEEP_NONE;
  260. }
  261. // ------------------------------------------------------------------------------------------------
  262. // ------------------------------------------------------------------------------------------------
  263. // ------------------------------------------------------------------------------------------------
  264. // ------------------------------------------------------------------------------------------------
  265. // ------------------------------------------------------------------------------------------------
  266. // ------------------------------------------------------------------------------------------------
  267. //-------------------------------------------------------------------------------------------------
  268. /** The entry point of a construct command to the Dozer */
  269. //-------------------------------------------------------------------------------------------------
  270. Object *WorkerAIUpdate::construct( const ThingTemplate *what,
  271. const Coord3D *pos,
  272. Real angle,
  273. Player *owningPlayer,
  274. Bool isRebuild )
  275. {
  276. // !!! NOTE: If you modify this you must modify the dozer too !!!
  277. // !!! Graham: Please please please have inspiration for how to *not* duplicate this code
  278. // GS - Construct needs to be an AI primitive. Inheriting off of AIUpdate means you are writing a
  279. // master brain that will call AI primitives on the object, not something that does stuff itself.
  280. // SupplyTruckAI decides who to call AIDock on. Worker should decide to AIDock or AIConstruct
  281. // or AIRepair. Dozer should just use the latter two. No construction logic should be in
  282. // the inherited AIUpdates at all.
  283. // create our machines if they don't yet exist
  284. ///@todo make 'construct' a real AI command and you won't need a special case
  285. m_isRebuild = isRebuild;
  286. createMachines();
  287. // sanity
  288. if( what == NULL || pos == NULL || owningPlayer == NULL )
  289. return NULL;
  290. // sanity
  291. DEBUG_ASSERTCRASH( getObject()->getControllingPlayer() == owningPlayer,
  292. ("Dozer::Construct - The controlling player of the Dozer is not the owning player passed in\n") );
  293. // if we're not rebuilding, we have a few checks to pass first for sanity
  294. if( isRebuild == FALSE )
  295. {
  296. // AI has weaker restriction on building
  297. Bool dozerIsAI = owningPlayer->getPlayerType() == PLAYER_COMPUTER;
  298. if( dozerIsAI )
  299. {
  300. // validate the the position to build at is valid
  301. if( TheBuildAssistant->isLocationLegalToBuild( pos, what, angle,
  302. BuildAssistant::CLEAR_PATH |
  303. BuildAssistant::NO_OBJECT_OVERLAP,
  304. getObject(), NULL ) != LBC_OK )
  305. return NULL;
  306. } // end if
  307. else
  308. {
  309. // make sure the player is capable of building this
  310. if( TheBuildAssistant->canMakeUnit( getObject(), what ) != CANMAKE_OK )
  311. return NULL;
  312. // validate the the position to build at is valid
  313. if( TheBuildAssistant->isLocationLegalToBuild( pos, what, angle,
  314. BuildAssistant::TERRAIN_RESTRICTIONS |
  315. BuildAssistant::CLEAR_PATH |
  316. BuildAssistant::NO_OBJECT_OVERLAP |
  317. BuildAssistant::SHROUD_REVEALED,
  318. getObject(), NULL ) != LBC_OK )
  319. return NULL;
  320. } // end else
  321. } // end if
  322. // what will our initial status bits
  323. ObjectStatusMaskType statusBits = MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNDER_CONSTRUCTION );
  324. if( isRebuild )
  325. statusBits.set( OBJECT_STATUS_RECONSTRUCTING );
  326. // create an object at the destination location
  327. Object *obj = TheThingFactory->newObject( what, owningPlayer->getDefaultTeam(), statusBits );
  328. // even though we haven't actually built anything yet, this keeps things tidy
  329. obj->setProducer( getObject() );
  330. obj->setBuilder( getObject() );
  331. // leave the supply truck state and now behave like a dozer.
  332. exitingSupplyTruckState();
  333. // take the required money away from the player
  334. if( isRebuild == FALSE )
  335. {
  336. Money *money = owningPlayer->getMoney();
  337. money->withdraw( what->calcCostToBuild( owningPlayer ) );
  338. } // end if
  339. //
  340. // set a bit that this object is under construction, it is important to do this early
  341. // before the hooks add/subtract power from a player are executed
  342. //
  343. obj->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNDER_CONSTRUCTION ) );
  344. // initialize object
  345. obj->setPosition( pos );
  346. obj->setOrientation( angle );
  347. // Flatten the terrain underneath the object, then adjust to the flattened height. jba.
  348. TheTerrainLogic->flattenTerrain(obj);
  349. Coord3D adjustedPos = *pos;
  350. adjustedPos.z = TheTerrainLogic->getGroundHeight(pos->x, pos->y);
  351. obj->setPosition(&adjustedPos);
  352. // Note - very important that we add to map AFTER we flatten terrain. jba.
  353. TheAI->pathfinder()->addObjectToPathfindMap( obj );
  354. // "callback" event for structure created (note that it's not yet "complete")
  355. owningPlayer->onStructureCreated( getObject(), obj );
  356. // set a construction percent for the new object to zero and a status for under construction
  357. obj->setConstructionPercent( 0.0 );
  358. // newly constructed objects start at one hit point
  359. BodyModuleInterface *body = obj->getBodyModule();
  360. body->internalChangeHealth( -body->getHealth() + 1.0f );
  361. // set the model action state to awaiting construction
  362. obj->clearAndSetModelConditionFlags(
  363. MAKE_MODELCONDITION_MASK2(MODELCONDITION_PARTIALLY_CONSTRUCTED, MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED),
  364. MAKE_MODELCONDITION_MASK(MODELCONDITION_AWAITING_CONSTRUCTION)
  365. );
  366. // we have a construction pending
  367. newTask( DOZER_TASK_BUILD, obj );
  368. return obj;
  369. }
  370. // ------------------------------------------------------------------------------------------------
  371. /** We just exited from a supply truck task and are now idle, we should go back to Dozer idle
  372. mode */
  373. // ------------------------------------------------------------------------------------------------
  374. void WorkerAIUpdate::exitingSupplyTruckState()
  375. {
  376. if( m_workerMachine->getCurrentStateID() == AS_SUPPLY_TRUCK )
  377. {
  378. // We've been given a Dozer specific order that the Supply Truck machine doesn't recognize
  379. // as BUSY (because this command also recognizes its own busy and is likewise waiting).
  380. // Explicitly slap it upside the head.
  381. if( getObject()->getAIUpdateInterface() )
  382. {
  383. getObject()->getAIUpdateInterface()->aiIdle(CMD_FROM_AI);
  384. }
  385. m_workerMachine->setState( AS_DOZER );
  386. // To clarify, I leave supply truck mode when I notice I am doing something not supply
  387. // truck related. When given a construct command, I wait to do anything until I notice
  388. // I'm not busy. Both states are being polite, so I must force the switch.
  389. }
  390. }
  391. // ------------------------------------------------------------------------------------------------
  392. /** Given our current task and repair target, can we accept this as a new repair target */
  393. // ------------------------------------------------------------------------------------------------
  394. Bool WorkerAIUpdate::canAcceptNewRepair( Object *obj )
  395. {
  396. // sanity
  397. if( obj == NULL )
  398. return FALSE;
  399. // if we're not repairing right now, we don't have any accept restrictions
  400. if( getCurrentTask() != DOZER_TASK_REPAIR )
  401. return TRUE;
  402. // get current repair target
  403. Object *currentRepair = TheGameLogic->findObjectByID( m_task[ DOZER_TASK_REPAIR ].m_targetObjectID );
  404. if( currentRepair )
  405. {
  406. // check for same object
  407. if( currentRepair == obj )
  408. return FALSE;
  409. // check for repairing any tower on the same bridge
  410. if( currentRepair->isKindOf( KINDOF_BRIDGE_TOWER ) &&
  411. obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
  412. {
  413. BridgeTowerBehaviorInterface *currentTowerInterface = NULL;
  414. BridgeTowerBehaviorInterface *newTowerInterface = NULL;
  415. currentTowerInterface = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( currentRepair );
  416. newTowerInterface = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
  417. // sanity
  418. if( currentTowerInterface == NULL || newTowerInterface == NULL )
  419. {
  420. DEBUG_CRASH(( "Unable to find bridge tower interface on object\n" ));
  421. return FALSE;
  422. } // end if
  423. // if they are part of the same bridge, ignore this repair command
  424. if( currentTowerInterface->getBridgeID() == newTowerInterface->getBridgeID() )
  425. return FALSE;
  426. } // end if
  427. } // end if, currentRepair object exists
  428. // all is well
  429. return TRUE;
  430. } // end canAcceptNewRepair
  431. //----------------------------------------------------------------------------------------
  432. void WorkerAIUpdate::privateIdle(CommandSourceType cmdSource)
  433. {
  434. // Leaving this commented out to show that although the regular supply truck does this, the
  435. // worker's dozer brain will get completely screwed.
  436. // If the user gives a stop command, I have to turn off autopilot
  437. // if( cmdSource == CMD_FROM_PLAYER )
  438. // setForceBusyState(TRUE);
  439. AIUpdateInterface::privateIdle(cmdSource);
  440. }
  441. //----------------------------------------------------------------------------------------
  442. void WorkerAIUpdate::privateDock( Object *dock, CommandSourceType cmdSource )
  443. {
  444. AIUpdateInterface::privateDock( dock, cmdSource );
  445. // If this is a command from a player, I will remember this as my favorite dock to override
  446. // ResourceManager searches.
  447. if ((cmdSource == CMD_FROM_PLAYER) && dock)
  448. {
  449. // Please note, there is not a separate Warehouse and Center memory by Design. Because
  450. // we lack a UI way to click Warehouse and drag to center to set up a specific path, the
  451. // practical realization has been made that you do not want separate memory.
  452. m_preferredDock = dock->getID();
  453. }
  454. }
  455. // ------------------------------------------------------------------------------------------------
  456. // ------------------------------------------------------------------------------------------------
  457. void WorkerAIUpdate::privateRepair( Object *obj, CommandSourceType cmdSource )
  458. {
  459. Object *dozer = getObject();
  460. // sanity, if we can't repair the object then get out of there
  461. if( TheActionManager->canRepairObject( dozer, obj, cmdSource ) == FALSE )
  462. return;
  463. // if we are already repairing this target do nothing
  464. if( canAcceptNewRepair( obj ) == FALSE )
  465. return;
  466. //
  467. // if this object has already been targeted for repair by an object we won't also try to
  468. // go repair it
  469. ObjectID currentRepairer = obj->getSoleHealingBenefactor();
  470. if( currentRepairer != INVALID_ID && currentRepairer != dozer->getID() )
  471. return;
  472. // start the new task
  473. newTask( DOZER_TASK_REPAIR, obj );
  474. } // end privateRepair
  475. // ------------------------------------------------------------------------------------------------
  476. /** Resume construction on a building */
  477. // ------------------------------------------------------------------------------------------------
  478. void WorkerAIUpdate::privateResumeConstruction( Object *obj, CommandSourceType cmdSource )
  479. {
  480. // sanity
  481. if( obj == NULL )
  482. return;
  483. // make sure we can resume construction on this
  484. if( TheActionManager->canResumeConstructionOf( getObject(), obj, cmdSource ) == FALSE )
  485. return;
  486. // start the new task for construction
  487. newTask( DOZER_TASK_BUILD, obj );
  488. } // end privateResumeConstruction
  489. //-------------------------------------------------------------------------------------------------
  490. /** Issue and order to the dozer */
  491. //-------------------------------------------------------------------------------------------------
  492. void WorkerAIUpdate::newTask( DozerTask task, Object* target )
  493. {
  494. // sanity
  495. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  496. // sanity
  497. if( target == NULL )
  498. return;
  499. m_preferredDock = INVALID_ID; // If we are dozing, we don't want any supply truck stuff going on. jba.
  500. //
  501. // special check for the build task, we should never be given more than one of them ...
  502. // for the other tasks we just forget what we were doing and the new target takes
  503. // precedence for the task
  504. //
  505. if( task == DOZER_TASK_BUILD || task == DOZER_TASK_REPAIR )
  506. {
  507. // handle getting two tasks
  508. if( isTaskPending( task ) == TRUE )
  509. cancelTask( task );
  510. // get our object
  511. Object *me = getObject();
  512. Coord3D position;
  513. target = DozerAIUpdate::findGoodBuildOrRepairPositionAndTarget(me, target, position);
  514. if (target == NULL)
  515. return; // could happen for some bridges
  516. //
  517. // for building, we say that even "thinking" about building or rebuilding an object
  518. // sets us as the current builder of that object. this allows any dozers that are
  519. // ordered later to resume construction on something to see that somebody is already taking
  520. // care of it and then they won't be even try to resume a build since we don't allow
  521. // multiple dozers/workers to double up on construction efforts
  522. //
  523. if( task == DOZER_TASK_BUILD )
  524. target->setBuilder( me );
  525. m_dockPoint[ task ][ DOZER_DOCK_POINT_START ].valid = TRUE;
  526. m_dockPoint[ task ][ DOZER_DOCK_POINT_START ].location = position;
  527. m_dockPoint[ task ][ DOZER_DOCK_POINT_ACTION ].valid = TRUE;
  528. m_dockPoint[ task ][ DOZER_DOCK_POINT_ACTION ].location = position;
  529. m_dockPoint[ task ][ DOZER_DOCK_POINT_END ].valid = TRUE;
  530. m_dockPoint[ task ][ DOZER_DOCK_POINT_END ].location = position;
  531. } // end if, build task
  532. // set the new task target and the frame in which we got this order
  533. m_task[ task ].m_targetObjectID = target->getID();
  534. m_task[ task ].m_taskOrderFrame = TheGameLogic->getFrame();
  535. // reset the dozer behavior so that it can re-evluate which task to continue working on
  536. m_dozerMachine->resetToDefaultState();
  537. // reset the workermachine, if we've been acting like a supply truck
  538. if( m_workerMachine->getCurrentStateID() == AS_SUPPLY_TRUCK )
  539. {
  540. // We've been given a Dozer specific order that the Supply Truck machine doesn't recognize
  541. // as BUSY (because this command also recognizes its own busy and is likewise waiting).
  542. // Explicitly slap it upside the head.
  543. if( getObject()->getAIUpdateInterface() )
  544. {
  545. getObject()->getAIUpdateInterface()->aiIdle(CMD_FROM_AI);
  546. }
  547. m_workerMachine->setState( AS_DOZER );
  548. // To clarify, I leave supply truck mode when I notice I am doing something not supply
  549. // truck related. When given a construct command, I wait to do anything until I notice
  550. // I'm not busy. Both states are being polite, so I must force the switch.
  551. }
  552. }
  553. //-------------------------------------------------------------------------------------------------
  554. /** Cancel a task and reset the dozer behavior state machine so that it can
  555. * re-evaluate what it wants to do if it was working on the task being
  556. * cancelled */
  557. //-------------------------------------------------------------------------------------------------
  558. void WorkerAIUpdate::cancelTask( DozerTask task )
  559. {
  560. // clear the order
  561. internalCancelTask( task );
  562. // reset the machine to we can re-evaluate what we want to do
  563. m_dozerMachine->resetToDefaultState();
  564. }
  565. //-------------------------------------------------------------------------------------------------
  566. /** Is there a given task waiting to be done */
  567. //-------------------------------------------------------------------------------------------------
  568. Bool WorkerAIUpdate::isTaskPending( DozerTask task )
  569. {
  570. // sanity
  571. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  572. return m_task[ task ].m_targetObjectID != 0 ? TRUE : FALSE;
  573. }
  574. //-------------------------------------------------------------------------------------------------
  575. /** Is there any task pending */
  576. //-------------------------------------------------------------------------------------------------
  577. Bool WorkerAIUpdate::isAnyTaskPending( void )
  578. {
  579. for( Int i = 0; i < DOZER_NUM_TASKS; i++ )
  580. if( isTaskPending( (DozerTask)i ) )
  581. return TRUE;
  582. return FALSE;
  583. }
  584. //-------------------------------------------------------------------------------------------------
  585. /** Get the target object of a given task */
  586. //-------------------------------------------------------------------------------------------------
  587. ObjectID WorkerAIUpdate::getTaskTarget( DozerTask task )
  588. {
  589. // sanity
  590. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  591. return m_task[ task ].m_targetObjectID;
  592. }
  593. //-------------------------------------------------------------------------------------------------
  594. /** Set a task as successfully completed */
  595. //-------------------------------------------------------------------------------------------------
  596. void WorkerAIUpdate::internalTaskComplete( DozerTask task )
  597. {
  598. // sanity
  599. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  600. // call the single method that gets called for completing and canceling tasks
  601. internalTaskCompleteOrCancelled( task );
  602. // remove the info for this task
  603. m_task[ task ].m_targetObjectID = INVALID_ID;
  604. m_task[ task ].m_taskOrderFrame = 0;
  605. // remove dock point info for this task
  606. for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ )
  607. m_dockPoint[ task ][ i ].valid = FALSE;
  608. }
  609. //-------------------------------------------------------------------------------------------------
  610. /** Clear a task from the Dozer for consideration, we can use this when a goal object becomes
  611. * invalid/destroyed etc. */
  612. //-------------------------------------------------------------------------------------------------
  613. void WorkerAIUpdate::internalCancelTask( DozerTask task )
  614. {
  615. // sanity
  616. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  617. if(task < 0 || task >= DOZER_NUM_TASKS)
  618. return; //DAMNIT! You CANNOT assert and then not handle the damn error! The. Code. Must. Not. Crash.
  619. // call the single method that gets called for completing and canceling tasks
  620. internalTaskCompleteOrCancelled( task );
  621. // remove the info for this task
  622. m_task[ task ].m_targetObjectID = INVALID_ID;
  623. m_task[ task ].m_taskOrderFrame = 0;
  624. // remove dock point info for this task
  625. for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ )
  626. m_dockPoint[ task ][ i ].valid = FALSE;
  627. // stop the dozer from moving
  628. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  629. if( !ai )
  630. {
  631. return;
  632. }
  633. /// @todo we really need a stop command instead of making it move to it's current location
  634. ai->aiMoveToPosition( getObject()->getPosition(), CMD_FROM_AI );
  635. }
  636. // ------------------------------------------------------------------------------------------------
  637. // ------------------------------------------------------------------------------------------------
  638. void WorkerAIUpdate::internalTaskCompleteOrCancelled( DozerTask task )
  639. {
  640. switch( task )
  641. {
  642. // --------------------------------------------------------------------------------------------
  643. case DOZER_TASK_INVALID:
  644. {
  645. break; // do nothing, this is really no task
  646. } // end invalid
  647. // --------------------------------------------------------------------------------------------
  648. case DOZER_TASK_BUILD:
  649. {
  650. // the builder is no longer actively building something
  651. getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  652. // And the thing we were working on is no longer being actively built
  653. ///@todo This would be correct except that we don't have idle crane animations and it is December.
  654. // Object* goalObject = TheGameLogic->findObjectByID(m_task[task].m_targetObjectID);
  655. // if (goalObject != NULL)
  656. // {
  657. // goalObject->clearModelConditionState(MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED);
  658. // }
  659. break;
  660. } // end build
  661. // --------------------------------------------------------------------------------------------
  662. case DOZER_TASK_REPAIR:
  663. {
  664. Object *obj = NULL;
  665. // the builder is no longer actively repairing something
  666. getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  667. // get object to reapir (if present)
  668. obj = TheGameLogic->findObjectByID( m_task[ task ].m_targetObjectID );
  669. if( obj )
  670. {
  671. // when we're done repairing bridges, tell the scaffolding to go away
  672. if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
  673. removeBridgeScaffolding( obj );
  674. } // end if
  675. break;
  676. } // end repair
  677. // --------------------------------------------------------------------------------------------
  678. case DOZER_TASK_FORTIFY:
  679. {
  680. break;
  681. } // end fortify
  682. // --------------------------------------------------------------------------------------------
  683. default:
  684. {
  685. DEBUG_CRASH(( "internalTaskCompleteOrCancelled: Unknown Dozer task '%d'\n", task ));
  686. break;
  687. } // end default
  688. } // end switch( task )
  689. }
  690. //-------------------------------------------------------------------------------------------------
  691. /** If we were building something, kill the active-construction flag on it */
  692. //-------------------------------------------------------------------------------------------------
  693. void WorkerAIUpdate::onDelete( void )
  694. {
  695. Int i;
  696. // cancel any of the tasks we had queued up
  697. for( i = DOZER_TASK_FIRST; i < DOZER_NUM_TASKS; ++i )
  698. {
  699. if( isTaskPending( (DozerTask)i ) )
  700. cancelTask( (DozerTask)i );
  701. } // end for i
  702. for( i = 0; i < DOZER_NUM_TASKS; i++ )
  703. {
  704. Object* goalObject = TheGameLogic->findObjectByID(m_task[i].m_targetObjectID);
  705. if (goalObject != NULL)
  706. {
  707. goalObject->clearModelConditionState(MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED);
  708. }
  709. }
  710. }
  711. //-------------------------------------------------------------------------------------------------
  712. /** Get the most recently issued task */
  713. //-------------------------------------------------------------------------------------------------
  714. DozerTask WorkerAIUpdate::getMostRecentCommand( void )
  715. {
  716. Int i;
  717. DozerTask mostRecentTask = DOZER_TASK_INVALID;
  718. UnsignedInt mostRecentFrame = 0;
  719. for( i = 0; i < DOZER_NUM_TASKS; i++ )
  720. {
  721. if( isTaskPending( (DozerTask)i ) )
  722. {
  723. if( m_task[ i ].m_taskOrderFrame > mostRecentFrame )
  724. {
  725. mostRecentTask = (DozerTask)i;
  726. mostRecentFrame = m_task[ i ].m_taskOrderFrame;
  727. }
  728. }
  729. }
  730. return mostRecentTask;
  731. }
  732. // ------------------------------------------------------------------------------------------------
  733. // ------------------------------------------------------------------------------------------------
  734. const Coord3D* WorkerAIUpdate::getDockPoint( DozerTask task, DozerDockPoint point )
  735. {
  736. // sanity
  737. if( task < 0 || task >= DOZER_NUM_TASKS )
  738. return NULL;
  739. // sanity
  740. if( point < 0 || point >= DOZER_NUM_DOCK_POINTS )
  741. return NULL;
  742. // if the point has been set (is valid) then return it
  743. if( m_dockPoint[ task ][ point ].valid )
  744. return &m_dockPoint[ task ][ point ].location;
  745. // no valid point has been set for this dock point on this task
  746. return NULL;
  747. } // end getDockPoint
  748. // ------------------------------------------------------------------------------------------------
  749. // ------------------------------------------------------------------------------------------------
  750. // ------------------------------------------------------------------------------------------------
  751. // ------------------------------------------------------------------------------------------------
  752. // ------------------------------------------------------------------------------------------------
  753. // ------------------------------------------------------------------------------------------------
  754. //-------------------------------------------------------------------------------------------------
  755. void WorkerAIUpdate::aiDoCommand(const AICommandParms* parms)
  756. {
  757. //
  758. // anytime we get a command, just remove any model condition that has us actively building
  759. // if we need to show that, that bit will be set anyway again during the build process
  760. //
  761. getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  762. if (!isAllowedToRespondToAiCommands(parms))
  763. return;
  764. // create our machines if they don't yet exist
  765. createMachines();
  766. switch( parms->m_cmd )
  767. {
  768. // --------------------------------------------------------------------------------------------
  769. case AICMD_REPAIR:
  770. {
  771. // if we have no task right now, go idle so we can immediately respond to this
  772. if( getCurrentTask() == DOZER_TASK_INVALID )
  773. aiIdle( CMD_FROM_AI );
  774. // do the repair
  775. privateRepair(parms->m_obj, parms->m_cmdSource);
  776. break;
  777. } // end repair
  778. // --------------------------------------------------------------------------------------------
  779. case AICMD_RESUME_CONSTRUCTION:
  780. {
  781. // if we have no task right now, go idle so we can immediately respond to this
  782. if( getCurrentTask() == DOZER_TASK_INVALID )
  783. aiIdle( CMD_FROM_AI );
  784. // do the command
  785. privateResumeConstruction( parms->m_obj, parms->m_cmdSource );
  786. break;
  787. } // end resume construction
  788. // --------------------------------------------------------------------------------------------
  789. default:
  790. {
  791. // if this is from the player, cancel our current task
  792. if( parms->m_cmdSource == CMD_FROM_PLAYER && getCurrentTask() != DOZER_TASK_INVALID )
  793. cancelTask( getCurrentTask() );
  794. // issue the command
  795. AIUpdateInterface::aiDoCommand(parms);
  796. // when a player issues commands, this will cause the dozer to re-evaluate what it's doing
  797. if( parms->m_cmdSource == CMD_FROM_PLAYER )
  798. m_dozerMachine->resetToDefaultState();
  799. break;
  800. } // end default
  801. } // end switch
  802. if (isClearingMines() && m_numberBoxes > 0 )
  803. {
  804. // if clearing mines, we drop any boxes we were carrying
  805. m_numberBoxes = 0;
  806. Drawable *draw = getObject()->getDrawable();
  807. if( draw )
  808. {
  809. draw->updateDrawableSupplyStatus( getWorkerAIUpdateModuleData()->m_maxBoxesData, m_numberBoxes );
  810. }
  811. }
  812. }
  813. // ------------------------------------------------------------------------------------------------
  814. // ------------------------------------------------------------------------------------------------
  815. // ------------------------------------------------------------------------------------------------
  816. // ------------------------------------------------------------------------------------------------
  817. // ------------------------------------------------------------------------------------------------
  818. // ------------------------------------------------------------------------------------------------
  819. // SupplyTruck stuff
  820. // ------------------------------------------------------------------------------------------------
  821. // ------------------------------------------------------------------------------------------------
  822. Bool WorkerAIUpdate::loseOneBox()
  823. {
  824. if( m_numberBoxes == 0 )
  825. return FALSE;
  826. m_numberBoxes--;
  827. Drawable *draw = getObject()->getDrawable();
  828. if( draw )
  829. {
  830. draw->updateDrawableSupplyStatus( getWorkerAIUpdateModuleData()->m_maxBoxesData, m_numberBoxes );
  831. }
  832. return TRUE;
  833. }
  834. // ------------------------------------------------------------------------------------------------
  835. // ------------------------------------------------------------------------------------------------
  836. Bool WorkerAIUpdate::gainOneBox( Int remainingStock )
  837. {
  838. if( getWorkerAIUpdateModuleData() && m_numberBoxes >= getWorkerAIUpdateModuleData()->m_maxBoxesData )
  839. return FALSE;
  840. ++m_numberBoxes;
  841. //if I just took the last box,
  842. //i will announce that this supply source is now empty
  843. if (remainingStock == 0)
  844. {
  845. Object* bestWarehouse = getObject()->getControllingPlayer()->getResourceGatheringManager()->findBestSupplyWarehouse( getObject() );
  846. Bool playDepleted = FALSE;
  847. if ( bestWarehouse )
  848. {
  849. //figure out whether the best one is considerably far from the previous one (current position)
  850. Coord3D delta = *getObject()->getPosition();
  851. delta.sub( bestWarehouse->getPosition() );
  852. if ( delta.length() > getWarehouseScanDistance()/4)
  853. playDepleted = TRUE;
  854. }
  855. else
  856. playDepleted = TRUE;
  857. if (playDepleted && m_suppliesDepletedVoice.getEventName().isEmpty() == false)
  858. {
  859. m_suppliesDepletedVoice.setObjectID(getObject()->getID());
  860. m_suppliesDepletedVoice.setPlayingHandle(TheAudio->addAudioEvent(&m_suppliesDepletedVoice));
  861. }
  862. }
  863. Drawable *draw = getObject()->getDrawable();
  864. if( draw )
  865. {
  866. draw->updateDrawableSupplyStatus( getWorkerAIUpdateModuleData()->m_maxBoxesData, m_numberBoxes );
  867. }
  868. return TRUE;
  869. }
  870. // ------------------------------------------------------------------------------------------------
  871. // ------------------------------------------------------------------------------------------------
  872. Bool WorkerAIUpdate::isSupplyTruckBrainActiveAndBusy()
  873. {
  874. return (m_workerMachine->getCurrentStateID() == AS_SUPPLY_TRUCK)
  875. && (m_supplyTruckStateMachine->getCurrentStateID() == ST_BUSY);
  876. }
  877. // ------------------------------------------------------------------------------------------------
  878. // ------------------------------------------------------------------------------------------------
  879. void WorkerAIUpdate::resetSupplyTruckBrain()
  880. {
  881. m_supplyTruckStateMachine->resetToDefaultState();
  882. }
  883. // ------------------------------------------------------------------------------------------------
  884. // ------------------------------------------------------------------------------------------------
  885. void WorkerAIUpdate::resetDozerBrain()
  886. {
  887. m_dozerMachine->resetToDefaultState();
  888. }
  889. // ------------------------------------------------------------------------------------------------
  890. // ------------------------------------------------------------------------------------------------
  891. // ------------------------------------------------------------------------------------------------
  892. // ------------------------------------------------------------------------------------------------
  893. // ------------------------------------------------------------------------------------------------
  894. // ------------------------------------------------------------------------------------------------
  895. // Worker's master state machine, that controls Dozerness or Supplytruckness
  896. class ActAsDozerState : public State
  897. {
  898. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ActAsDozerState, "ActAsDozerState")
  899. protected:
  900. // snapshot interface STUBBED.
  901. virtual void crc( Xfer *xfer ){};
  902. virtual void xfer( Xfer *xfer ){};
  903. virtual void loadPostProcess(){};
  904. public:
  905. ActAsDozerState( StateMachine *machine ) :State( machine, "ActAsDozerState" ){}
  906. virtual StateReturnType onEnter();
  907. virtual StateReturnType update();
  908. virtual StateReturnType onExit();
  909. };
  910. EMPTY_DTOR(ActAsDozerState)
  911. // ------------------------------------------------------------------------------------------------
  912. // ------------------------------------------------------------------------------------------------
  913. class ActAsSupplyTruckState : public State
  914. {
  915. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ActAsSupplyTruckState, "ActAsSupplyTruckState")
  916. protected:
  917. // snapshot interface STUBBED.
  918. virtual void crc( Xfer *xfer ){};
  919. virtual void xfer( Xfer *xfer ){};
  920. virtual void loadPostProcess(){};
  921. public:
  922. ActAsSupplyTruckState( StateMachine *machine ) :State( machine, "ActAsSupplyTruckState" ){}
  923. virtual StateReturnType onEnter();
  924. virtual StateReturnType update();
  925. virtual StateReturnType onExit();
  926. };
  927. EMPTY_DTOR(ActAsSupplyTruckState)
  928. // ------------------------------------------------------------------------------------------------
  929. // ------------------------------------------------------------------------------------------------
  930. WorkerStateMachine::WorkerStateMachine( Object *owner ) : StateMachine( owner, "WorkerStateMachine" )
  931. {
  932. static const StateConditionInfo asDozerConditions[] =
  933. {
  934. StateConditionInfo(supplyTruckSubMachineWantsToEnter, AS_SUPPLY_TRUCK, NULL),
  935. StateConditionInfo(NULL, NULL, NULL) // keep last
  936. };
  937. static const StateConditionInfo asTruckConditions[] =
  938. {
  939. StateConditionInfo(supplyTruckSubMachineReadyToLeave, AS_DOZER, NULL),
  940. StateConditionInfo(NULL, NULL, NULL) // keep last
  941. };
  942. // order matters: first state is the default state.
  943. defineState( AS_DOZER, newInstance(ActAsDozerState)( this ), INVALID_STATE_ID, INVALID_STATE_ID, asDozerConditions );
  944. defineState( AS_SUPPLY_TRUCK, newInstance(ActAsSupplyTruckState)( this ), INVALID_STATE_ID, INVALID_STATE_ID, asTruckConditions );
  945. }
  946. // ------------------------------------------------------------------------------------------------
  947. // ------------------------------------------------------------------------------------------------
  948. WorkerStateMachine::~WorkerStateMachine()
  949. {
  950. }
  951. // ------------------------------------------------------------------------------------------------
  952. /** CRC */
  953. // ------------------------------------------------------------------------------------------------
  954. void WorkerStateMachine::crc( Xfer *xfer )
  955. {
  956. StateMachine::crc(xfer);
  957. } // end crc
  958. // ------------------------------------------------------------------------------------------------
  959. /** Xfer Method */
  960. // ------------------------------------------------------------------------------------------------
  961. void WorkerStateMachine::xfer( Xfer *xfer )
  962. {
  963. XferVersion cv = 1;
  964. XferVersion v = cv;
  965. xfer->xferVersion( &v, cv );
  966. StateMachine::xfer(xfer);
  967. } // end xfer
  968. // ------------------------------------------------------------------------------------------------
  969. /** Load post process */
  970. // ------------------------------------------------------------------------------------------------
  971. void WorkerStateMachine::loadPostProcess( void )
  972. {
  973. StateMachine::loadPostProcess();
  974. } // end loadPostProcess
  975. // ------------------------------------------------------------------------------------------------
  976. // ------------------------------------------------------------------------------------------------
  977. Bool WorkerStateMachine::supplyTruckSubMachineWantsToEnter( State *thisState, void* userData )
  978. {
  979. Object *owner = thisState->getMachineOwner();
  980. WorkerAIUpdate *update = (WorkerAIUpdate*)owner->getAIUpdateInterface();
  981. if( !update )
  982. {
  983. return false;
  984. }
  985. AIStateType masterState = update->getAIStateType();
  986. //If I detect a Supply force message, or if I have been put straight in dock,
  987. //then the worker master part of me wants to switch to the Supply sub-brain
  988. return update->isForcedIntoWantingState() || (masterState == AI_DOCK);
  989. }
  990. // ------------------------------------------------------------------------------------------------
  991. // ------------------------------------------------------------------------------------------------
  992. Bool WorkerStateMachine::supplyTruckSubMachineReadyToLeave( State *thisState, void* userData )
  993. {
  994. Object *owner = thisState->getMachineOwner();
  995. WorkerAIUpdate *update = (WorkerAIUpdate*)owner->getAIUpdateInterface();
  996. if( !update )
  997. {
  998. return false;
  999. }
  1000. // AIStateType masterState = update->getAIStateType();
  1001. // It isn't ready to leave if it is on its way in. The first clause
  1002. // allow a latch for a moment
  1003. // so there is no transition out on the way in. Active and Busy means it isn't doing
  1004. // anything Supply related.
  1005. return !supplyTruckSubMachineWantsToEnter( thisState, NULL )
  1006. && update->isSupplyTruckBrainActiveAndBusy();
  1007. }
  1008. // ------------------------------------------------------------------------------------------------
  1009. // ------------------------------------------------------------------------------------------------
  1010. StateReturnType ActAsDozerState::onEnter()
  1011. {
  1012. return STATE_CONTINUE;
  1013. }
  1014. // ------------------------------------------------------------------------------------------------
  1015. // ------------------------------------------------------------------------------------------------
  1016. StateReturnType ActAsDozerState::update()
  1017. {
  1018. return STATE_CONTINUE;
  1019. }
  1020. // ------------------------------------------------------------------------------------------------
  1021. // ------------------------------------------------------------------------------------------------
  1022. StateReturnType ActAsDozerState::onExit()
  1023. {
  1024. Object *owner = getMachineOwner();
  1025. WorkerAIUpdate *update = (WorkerAIUpdate*)owner->getAIUpdateInterface();
  1026. if( !update )
  1027. {
  1028. return STATE_FAILURE;
  1029. }
  1030. update->resetDozerBrain();
  1031. return STATE_CONTINUE;
  1032. }
  1033. // ------------------------------------------------------------------------------------------------
  1034. // ------------------------------------------------------------------------------------------------
  1035. StateReturnType ActAsSupplyTruckState::onEnter()
  1036. {
  1037. return STATE_CONTINUE;
  1038. }
  1039. // ------------------------------------------------------------------------------------------------
  1040. // ------------------------------------------------------------------------------------------------
  1041. StateReturnType ActAsSupplyTruckState::update()
  1042. {
  1043. return STATE_CONTINUE;
  1044. }
  1045. // ------------------------------------------------------------------------------------------------
  1046. // ------------------------------------------------------------------------------------------------
  1047. StateReturnType ActAsSupplyTruckState::onExit()
  1048. {
  1049. Object *owner = getMachineOwner();
  1050. WorkerAIUpdate *update = (WorkerAIUpdate*)owner->getAIUpdateInterface();
  1051. if( !update )
  1052. {
  1053. return STATE_FAILURE;
  1054. }
  1055. update->resetSupplyTruckBrain();
  1056. return STATE_CONTINUE;
  1057. }
  1058. // ------------------------------------------------------------------------------------------------
  1059. /** Create the bridge scaffolding if necessary for the bridge that is attached to this tower */
  1060. // ------------------------------------------------------------------------------------------------
  1061. void WorkerAIUpdate::createBridgeScaffolding( Object *bridgeTower )
  1062. {
  1063. // sanity
  1064. if( bridgeTower == NULL )
  1065. return;
  1066. // get the bridge behavior interface from the bridge object that this tower is a part of
  1067. BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( bridgeTower );
  1068. if( btbi == NULL )
  1069. return;
  1070. Object *bridgeObject = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1071. if( bridgeObject == NULL )
  1072. return;
  1073. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridgeObject );
  1074. if( bbi == NULL )
  1075. return;
  1076. // tell the bridge to create scaffolding if necessary
  1077. bbi->createScaffolding();
  1078. } // end createBridgeScaffolding
  1079. // ------------------------------------------------------------------------------------------------
  1080. /** Remove the bridge scaffolding from the bridge object that is attached to this tower */
  1081. // ------------------------------------------------------------------------------------------------
  1082. void WorkerAIUpdate::removeBridgeScaffolding( Object *bridgeTower )
  1083. {
  1084. // sanity
  1085. if( bridgeTower == NULL )
  1086. return;
  1087. // get the bridge behavior interface from the bridge object that this tower is a part of
  1088. BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( bridgeTower );
  1089. if( btbi == NULL )
  1090. return;
  1091. Object *bridgeObject = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1092. if( bridgeObject == NULL )
  1093. return;
  1094. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridgeObject );
  1095. if( bbi == NULL )
  1096. return;
  1097. // tell the bridge to end any scaffolding from repairing
  1098. bbi->removeScaffolding();
  1099. } // end removeBridgeScaffolding
  1100. //------------------------------------------------------------------------------------------------
  1101. void WorkerAIUpdate::startBuildingSound( const AudioEventRTS *sound, ObjectID constructionSiteID )
  1102. {
  1103. m_buildingSound = *sound;
  1104. m_buildingSound.setObjectID( constructionSiteID );
  1105. m_buildingSound.setPlayingHandle( TheAudio->addAudioEvent( &m_buildingSound ) );
  1106. }
  1107. //------------------------------------------------------------------------------------------------
  1108. void WorkerAIUpdate::finishBuildingSound()
  1109. {
  1110. TheAudio->removeAudioEvent( m_buildingSound.getPlayingHandle() );
  1111. }
  1112. //------------------------------------------------------------------------------------------------
  1113. Int WorkerAIUpdate::getUpgradedSupplyBoost() const
  1114. {
  1115. Player *player = getObject()->getControllingPlayer();
  1116. static const UpgradeTemplate *workerShoeTemplate = TheUpgradeCenter->findUpgrade( "Upgrade_GLAWorkerShoes" );
  1117. if (player && workerShoeTemplate && player->hasUpgradeComplete(workerShoeTemplate))
  1118. return getWorkerAIUpdateModuleData()->m_upgradedSupplyBoost;
  1119. else
  1120. return 0;
  1121. }
  1122. // ------------------------------------------------------------------------------------------------
  1123. /** CRC */
  1124. // ------------------------------------------------------------------------------------------------
  1125. void WorkerAIUpdate::crc( Xfer *xfer )
  1126. {
  1127. // extend base class
  1128. AIUpdateInterface::crc(xfer);
  1129. } // end crc
  1130. // ------------------------------------------------------------------------------------------------
  1131. /** Xfer method
  1132. * Version Info:
  1133. * 1: Initial version */
  1134. // ------------------------------------------------------------------------------------------------
  1135. void WorkerAIUpdate::xfer( Xfer *xfer )
  1136. {
  1137. XferVersion currentVersion = 1;
  1138. XferVersion version = currentVersion;
  1139. xfer->xferVersion( &version, currentVersion );
  1140. // extend base class
  1141. AIUpdateInterface::xfer(xfer);
  1142. //------------------------- xfer Dozer info
  1143. Int numTasks = DOZER_NUM_TASKS;
  1144. xfer->xferInt(&numTasks);
  1145. if (numTasks != DOZER_NUM_TASKS) {
  1146. DEBUG_CRASH(("DOZER_NUM_TASKS changed unexpectedly."));
  1147. throw SC_INVALID_DATA;
  1148. }
  1149. Int i, j;
  1150. for (i=0; i<DOZER_NUM_TASKS; i++) {
  1151. xfer->xferObjectID(&m_task[i].m_targetObjectID);
  1152. xfer->xferUnsignedInt(&m_task[i].m_taskOrderFrame);
  1153. }
  1154. xfer->xferSnapshot(m_dozerMachine);
  1155. xfer->xferUser(&m_currentTask, sizeof(m_currentTask));
  1156. Int dockPoints = DOZER_NUM_DOCK_POINTS;
  1157. xfer->xferInt(&dockPoints);
  1158. if (dockPoints!=DOZER_NUM_DOCK_POINTS) {
  1159. DEBUG_CRASH(("DOZER_NUM_DOCK_POINTS changed unexpectedly."));
  1160. throw SC_INVALID_DATA;
  1161. }
  1162. for (i=0; i<DOZER_NUM_TASKS; i++) {
  1163. for (j=0; j<DOZER_NUM_DOCK_POINTS; j++) {
  1164. xfer->xferBool(&m_dockPoint[i][j].valid);
  1165. xfer->xferCoord3D(&m_dockPoint[i][j].location);
  1166. }
  1167. }
  1168. xfer->xferUser(&m_buildSubTask, sizeof(m_buildSubTask));
  1169. //------------------------- xfer Supply Truck info
  1170. xfer->xferSnapshot(m_supplyTruckStateMachine);
  1171. xfer->xferObjectID(&m_preferredDock);
  1172. xfer->xferInt(&m_numberBoxes);
  1173. xfer->xferBool(&m_forcePending);
  1174. //-------------------------- xfer Worker info
  1175. xfer->xferSnapshot(m_workerMachine);
  1176. } // end xfer
  1177. // ------------------------------------------------------------------------------------------------
  1178. /** Load post process */
  1179. // ------------------------------------------------------------------------------------------------
  1180. void WorkerAIUpdate::loadPostProcess( void )
  1181. {
  1182. // extend base class
  1183. AIUpdateInterface::loadPostProcess();
  1184. } // end loadPostProcess