POWTruckAIUpdate.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. /*
  2. ** Command & Conquer Generals(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: POWTruckAIUpdate.cpp /////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day
  25. // Desc: AI for the POW 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/GlobalData.h"
  31. #include "Common/Money.h"
  32. #include "Common/Player.h"
  33. #include "Common/ThingTemplate.h"
  34. #include "GameClient/InGameUI.h"
  35. #include "GameLogic/AIPathfind.h"
  36. #include "GameLogic/Locomotor.h"
  37. #include "GameLogic/Object.h"
  38. #include "GameLogic/PartitionManager.h"
  39. #include "GameLogic/Module/AIUpdate.h"
  40. #include "GameLogic/Module/OpenContain.h"
  41. #include "GameLogic/Module/POWTruckAIUpdate.h"
  42. #ifdef ALLOW_SURRENDER
  43. ///////////////////////////////////////////////////////////////////////////////////////////////////
  44. // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
  45. ///////////////////////////////////////////////////////////////////////////////////////////////////
  46. // ------------------------------------------------------------------------------------------------
  47. // ------------------------------------------------------------------------------------------------
  48. POWTruckAIUpdateModuleData::POWTruckAIUpdateModuleData( void )
  49. {
  50. m_boredTimeInFrames = 0;
  51. m_hangAroundPrisonDistance = 0;
  52. } // end POWTruckAIUpdateModuleData
  53. // ------------------------------------------------------------------------------------------------
  54. void POWTruckAIUpdateModuleData::buildFieldParse( MultiIniFieldParse &p )
  55. {
  56. AIUpdateModuleData::buildFieldParse( p );
  57. static const FieldParse dataFieldParse[] =
  58. {
  59. { "BoredTime", INI::parseDurationUnsignedInt, NULL, offsetof( POWTruckAIUpdateModuleData, m_boredTimeInFrames ) },
  60. { "AtPrisonDistance", INI::parseReal, NULL, offsetof( POWTruckAIUpdateModuleData, m_hangAroundPrisonDistance ) },
  61. { 0, 0, 0, 0 }
  62. };
  63. p.add( dataFieldParse );
  64. } // end buildFieldParse
  65. ///////////////////////////////////////////////////////////////////////////////////////////////////
  66. // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
  67. ///////////////////////////////////////////////////////////////////////////////////////////////////
  68. //-------------------------------------------------------------------------------------------------
  69. //-------------------------------------------------------------------------------------------------
  70. POWTruckAIUpdate::POWTruckAIUpdate( Thing *thing, const ModuleData *moduleData )
  71. : AIUpdateInterface( thing, moduleData )
  72. {
  73. m_aiMode = AUTOMATIC;
  74. m_currentTask = POW_TRUCK_TASK_WAITING;
  75. m_targetID = INVALID_ID;
  76. m_prisonID = INVALID_ID;
  77. m_enteredWaitingFrame = 0;
  78. m_lastFindFrame = 0;
  79. } // end POWTruckAIUpdate
  80. //-------------------------------------------------------------------------------------------------
  81. //-------------------------------------------------------------------------------------------------
  82. POWTruckAIUpdate::~POWTruckAIUpdate( void )
  83. {
  84. } // end ~POWTruckAIUpdate
  85. //-------------------------------------------------------------------------------------------------
  86. //-------------------------------------------------------------------------------------------------
  87. void POWTruckAIUpdate::onDelete( void )
  88. {
  89. } // end onDelete
  90. // ------------------------------------------------------------------------------------------------
  91. //-------------------------------------------------------------------------------------------------
  92. void POWTruckAIUpdate::aiDoCommand( const AICommandParms *parms )
  93. {
  94. if (!isAllowedToRespondToAiCommands(parms))
  95. return;
  96. // if the command is from a player, stop whatever we might be doing and accept the new command
  97. if( parms->m_cmdSource == CMD_FROM_PLAYER )
  98. {
  99. // stop
  100. aiIdle( CMD_FROM_AI );
  101. // set our task to waiting
  102. setTask( POW_TRUCK_TASK_WAITING );
  103. } // end if
  104. switch( parms->m_cmd )
  105. {
  106. // --------------------------------------------------------------------------------------------
  107. case AICMD_PICK_UP_PRISONER:
  108. {
  109. privatePickUpPrisoner( parms->m_obj, parms->m_cmdSource );
  110. break;
  111. } // end pick up prisoner
  112. // --------------------------------------------------------------------------------------------
  113. case AICMD_RETURN_PRISONERS:
  114. {
  115. privateReturnPrisoners( parms->m_obj, parms->m_cmdSource );
  116. break;
  117. } // end return prisoners
  118. // --------------------------------------------------------------------------------------------
  119. default:
  120. {
  121. // call the default do command
  122. AIUpdateInterface::aiDoCommand( parms );
  123. break;
  124. } // end default
  125. } // end switch( parms->m_cmd )
  126. } // end aiDoCommand
  127. //-------------------------------------------------------------------------------------------------
  128. //-------------------------------------------------------------------------------------------------
  129. UpdateSleepTime POWTruckAIUpdate::update( void )
  130. {
  131. // we are ultra accurate
  132. if( getCurLocomotor() )
  133. getCurLocomotor()->setUltraAccurate( TRUE );
  134. // extend the regular ai update module
  135. UpdateSleepTime result;
  136. result = AIUpdateInterface::update();
  137. // being able to path through units makes the POW truck more effective
  138. // but we're getting rid of this functionality (CBD 10-10-2002)
  139. // setCanPathThroughUnits(true);
  140. // do our own logic
  141. switch( m_currentTask )
  142. {
  143. case POW_TRUCK_TASK_WAITING:
  144. updateWaiting();
  145. break;
  146. case POW_TRUCK_TASK_FIND_TARGET:
  147. updateFindTarget();
  148. break;
  149. case POW_TRUCK_TASK_COLLECTING_TARGET:
  150. updateCollectingTarget();
  151. break;
  152. case POW_TRUCK_TASK_RETURNING_PRISONERS:
  153. updateReturnPrisoners();
  154. break;
  155. default:
  156. DEBUG_CRASH(( "POWTruckAIUpdate::update - Unknown current task '%d'\n", m_currentTask ));
  157. break;
  158. } // end switch, current task
  159. return UPDATE_SLEEP_NONE;
  160. } // end update
  161. ///////////////////////////////////////////////////////////////////////////////////////////////////
  162. // PROTECTED //////////////////////////////////////////////////////////////////////////////////////
  163. ///////////////////////////////////////////////////////////////////////////////////////////////////
  164. // ------------------------------------------------------------------------------------------------
  165. /** Set our current task */
  166. // ------------------------------------------------------------------------------------------------
  167. void POWTruckAIUpdate::setTask( POWTruckTask task, Object *taskObject )
  168. {
  169. POWTruckTask oldTask = m_currentTask;
  170. // sanity, POW_TRUCK_TASK_COLLECTING_TARGET and POW_TRUCK_TASK_RETURNING_PRISONERS require taskObject parameters
  171. if( (task == POW_TRUCK_TASK_COLLECTING_TARGET || task == POW_TRUCK_TASK_RETURNING_PRISONERS) &&
  172. taskObject == NULL )
  173. {
  174. DEBUG_CRASH(( "POWTruckAIUpdate::setTask - Illegal arguments\n" ));
  175. setTask( POW_TRUCK_TASK_WAITING );
  176. return;
  177. } // end if
  178. // when leaving the collecting target state, we need to do some bookeeping
  179. if( oldTask == POW_TRUCK_TASK_COLLECTING_TARGET )
  180. {
  181. // target cleanup
  182. m_targetID = INVALID_ID;
  183. } // end if
  184. // to be clean, when leaving the POW_TRUCK_TASK_RETURNING_PRISONERS state, clear out the prison ID we were watching
  185. if( oldTask == POW_TRUCK_TASK_RETURNING_PRISONERS )
  186. m_prisonID = INVALID_ID;
  187. // we store IDs for some tasks
  188. if( task == POW_TRUCK_TASK_COLLECTING_TARGET )
  189. {
  190. // save ID of our target
  191. m_targetID = taskObject->getID();
  192. // mark this target as slated for pickup
  193. DEBUG_ASSERTCRASH( taskObject->getAIUpdateInterface(), ("POWTruckAIUpdate::setTask - '%s' has no ai module\n",
  194. taskObject->getTemplate()->getName().str()) );
  195. } // end if
  196. else if( task == POW_TRUCK_TASK_RETURNING_PRISONERS )
  197. {
  198. // save ID of the prison we're going to
  199. m_prisonID = taskObject->getID();
  200. } // end else
  201. else if( task == POW_TRUCK_TASK_WAITING )
  202. {
  203. // mark the frame and always go idle
  204. m_enteredWaitingFrame = TheGameLogic->getFrame();
  205. } // end else if
  206. // store the new current task
  207. m_currentTask = task;
  208. } // end task
  209. // ------------------------------------------------------------------------------------------------
  210. /** Set this unit into automatic or manual AI mode, when automatic the AI will look for
  211. * prisoners, collect them, and bring them back automatically. When in manual, the
  212. * player must direct the actions of the unit explicitly */
  213. // ------------------------------------------------------------------------------------------------
  214. void POWTruckAIUpdate::setAIMode( POWTruckAIMode mode )
  215. {
  216. // save the mode
  217. m_aiMode = mode;
  218. } // end setAIModel
  219. // ------------------------------------------------------------------------------------------------
  220. // ------------------------------------------------------------------------------------------------
  221. void POWTruckAIUpdate::privatePickUpPrisoner( Object *prisoner, CommandSourceType cmdSource )
  222. {
  223. // if 'prisoner' is not a valid target for picking up don't bother going forward
  224. if( validateTarget( prisoner ) == FALSE )
  225. return;
  226. //
  227. // we will now try to pick up this target ... if the command was from a player then
  228. // we are no longer in automatic mode and will not return to automatic mode until
  229. // the player issues the command for us to return back to prison
  230. //
  231. // CBD: Disabling this whole manual thing for now
  232. //
  233. if( FALSE && cmdSource == CMD_FROM_PLAYER )
  234. setAIMode( MANUAL );
  235. else
  236. setAIMode( AUTOMATIC );
  237. // set our task as picking up our target and save the target ID
  238. setTask( POW_TRUCK_TASK_COLLECTING_TARGET, prisoner );
  239. // move to the target
  240. ignoreObstacle( prisoner );
  241. if( getCurLocomotor() )
  242. getCurLocomotor()->setUltraAccurate( TRUE );
  243. aiMoveToObject( prisoner, CMD_FROM_AI );
  244. } // end privatePickUpPrisoner
  245. // ------------------------------------------------------------------------------------------------
  246. // ------------------------------------------------------------------------------------------------
  247. void POWTruckAIUpdate::privateReturnPrisoners( Object *prison, CommandSourceType cmdSource )
  248. {
  249. // when receiving this command from the player, we will always enter AUTOMATIC ai mode
  250. if( cmdSource == CMD_FROM_PLAYER )
  251. setAIMode( AUTOMATIC );
  252. // if no prison is provided, find one if possible
  253. if( prison == NULL )
  254. prison = findBestPrison();
  255. // still no prison, nothing to do
  256. if( prison == NULL )
  257. return;
  258. // set us into the return prisoners "state"
  259. setTask( POW_TRUCK_TASK_RETURNING_PRISONERS, prison );
  260. // dock with the prison
  261. if( getCurLocomotor() )
  262. getCurLocomotor()->setUltraAccurate( TRUE );
  263. aiDock( prison, cmdSource );
  264. } // end privateReturnPrisoners
  265. // ------------------------------------------------------------------------------------------------
  266. // ------------------------------------------------------------------------------------------------
  267. void POWTruckAIUpdate::updateWaiting( void )
  268. {
  269. // if we're manual control ... do nothing
  270. if( m_aiMode == MANUAL )
  271. return;
  272. // get our info
  273. Object *us = getObject();
  274. AIUpdateInterface *ai = us->getAIUpdateInterface();
  275. DEBUG_ASSERTCRASH( ai, ("POWTruckAIUpdate::updateWaiting - '%s' has no ai\n",
  276. us->getTemplate()->getName().str()) );
  277. //
  278. // if we're not idle we don't consider ourselves waiting ... we could be moving from
  279. // a players command, or just about anything ... so just keep updating the m_enteredWaitingFrame
  280. // to the current frame until we actually become idle and start to wait
  281. //
  282. if( ai->isIdle() == FALSE )
  283. m_enteredWaitingFrame = TheGameLogic->getFrame();
  284. // get module data
  285. const POWTruckAIUpdateModuleData *modData = getPOWTruckAIUpdateModuleData();
  286. // if enough time has passed we're bored of waiting, try to find another target
  287. if( TheGameLogic->getFrame() - m_enteredWaitingFrame > modData->m_boredTimeInFrames )
  288. setTask( POW_TRUCK_TASK_FIND_TARGET );
  289. } // updateWaiting
  290. static const UnsignedInt FIND_DELAY = LOGICFRAMES_PER_SECOND * 1;
  291. // ------------------------------------------------------------------------------------------------
  292. // ------------------------------------------------------------------------------------------------
  293. void POWTruckAIUpdate::updateFindTarget( void )
  294. {
  295. // we never find targets when in manual ai mode
  296. DEBUG_ASSERTCRASH( m_aiMode != MANUAL, ("POWTruckAIUpdate::updateFindTarget - We shouldn't be here with a manual ai mode\n") );
  297. if( m_aiMode == MANUAL )
  298. return;
  299. // don't do this too often
  300. if( TheGameLogic->getFrame() - m_lastFindFrame < FIND_DELAY )
  301. return;
  302. // we've now done a find
  303. m_lastFindFrame = TheGameLogic->getFrame();
  304. // get our info
  305. Object *us = getObject();
  306. AIUpdateInterface *ai = us->getAIUpdateInterface();
  307. DEBUG_ASSERTCRASH( ai, ("POWTruckAIUpdate::updateFindTarget - '%s' has no ai\n",
  308. us->getTemplate()->getName().str()) );
  309. // if we're full we should return to prison
  310. ContainModuleInterface *contain = us->getContain();
  311. if( contain && contain->getContainCount() == contain->getContainMax() )
  312. {
  313. doReturnPrisoners();
  314. return;
  315. } // end if
  316. // find closest target not already targeted for pickup by another POW truck
  317. Object *target = findBestTarget();
  318. // if target exists, go get it
  319. if( target )
  320. {
  321. // send the pickup command
  322. ai->aiPickUpPrisoner( target, CMD_FROM_AI );
  323. } // end if
  324. else
  325. {
  326. // no target exists, if we have prisoners return them
  327. if( contain->getContainCount() != 0 )
  328. doReturnPrisoners();
  329. else
  330. doReturnToPrison( NULL );
  331. } // end else
  332. } // end updateFindTarget
  333. // ------------------------------------------------------------------------------------------------
  334. // ------------------------------------------------------------------------------------------------
  335. void POWTruckAIUpdate::updateCollectingTarget( void )
  336. {
  337. // validate our target is still OK to collect
  338. Object *target = TheGameLogic->findObjectByID( m_targetID );
  339. if( validateTarget( target ) == FALSE )
  340. {
  341. // find more prisoners or wait for player intervention
  342. if( m_aiMode == AUTOMATIC )
  343. setTask( POW_TRUCK_TASK_FIND_TARGET );
  344. else
  345. setTask( POW_TRUCK_TASK_WAITING );
  346. return;
  347. } // end if
  348. // we should never become idle while collecting a target, if we do, find a target
  349. Object *us = getObject();
  350. AIUpdateInterface *ai = us->getAIUpdateInterface();
  351. if( ai->isIdle() )
  352. {
  353. if( m_aiMode == AUTOMATIC )
  354. setTask( POW_TRUCK_TASK_FIND_TARGET );
  355. else
  356. setTask( POW_TRUCK_TASK_WAITING );
  357. } // end if
  358. /*
  359. //
  360. // when we're close to the target, we tell the target to start walking toward
  361. // us ... if they're not idle we don't do this as they are already moving
  362. //
  363. Object *us = getObject();
  364. AIUpdateInterface *targetAI = target->getAIUpdateInterface();
  365. if( targetAI->isIdle() )
  366. {
  367. // are we close enought to tell them to start moving to us
  368. Real distSq = pow( us->getGeometryInfo().getBoundingSphereRadius() * 2.0f, 2 );
  369. if( ThePartitionManager->getDistanceSquared( us, target, FROM_CENTER_2D ) <= distSq )
  370. {
  371. // tell them to start moving to us
  372. targetAI->aiMoveToObject( us, CMD_FROM_AI );
  373. } // end if
  374. } // end if
  375. */
  376. } // end updateCollectingTarget
  377. // ------------------------------------------------------------------------------------------------
  378. // ------------------------------------------------------------------------------------------------
  379. struct PrisonerReturnData
  380. {
  381. Object *source; ///< objects will be removed from source and put into dest
  382. Object *dest; ///< objects will be removed from source and put into dest
  383. };
  384. // ------------------------------------------------------------------------------------------------
  385. // ------------------------------------------------------------------------------------------------
  386. static void putContainedInPrison( Object *obj, void *userData )
  387. {
  388. PrisonerReturnData *returnData = (PrisonerReturnData *)userData;
  389. // sanity
  390. DEBUG_ASSERTCRASH( returnData != NULL && returnData->source != NULL && returnData->dest != NULL,
  391. ("putContainedInPrison: Invalid arguments\n") );
  392. // take 'obj' out of the source
  393. ContainModuleInterface *sourceContain = returnData->source->getContain();
  394. if( sourceContain )
  395. {
  396. sourceContain->removeFromContain( obj );
  397. }
  398. // put 'obj' into dest
  399. ContainModuleInterface *destContain = returnData->dest->getContain();
  400. if( destContain )
  401. {
  402. destContain->addToContain( obj );
  403. }
  404. } // end putContainedInPrison
  405. // ------------------------------------------------------------------------------------------------
  406. // ------------------------------------------------------------------------------------------------
  407. void POWTruckAIUpdate::updateReturnPrisoners( void )
  408. {
  409. Object *us = getObject();
  410. AIUpdateInterface *ai = us->getAIUpdateInterface();
  411. DEBUG_ASSERTCRASH( ai, ("POWTruckAIUpdate::updateReturnPrisoners - '%s' has no AI\n",
  412. us->getTemplate()->getName().str()) );
  413. // get the prison we're returning to
  414. Object *prison = TheGameLogic->findObjectByID( m_prisonID );
  415. // prison has gone away, do this all over again
  416. if( prison == NULL )
  417. {
  418. doReturnPrisoners();
  419. return;
  420. } // end if
  421. // we should not go idle, we should collide with the prison to return the prisoners
  422. if( ai->isIdle() )
  423. doReturnPrisoners();
  424. } // end updateReturnPrisoners
  425. // ------------------------------------------------------------------------------------------------
  426. /** Is the object 'target' a valid target for collection */
  427. // ------------------------------------------------------------------------------------------------
  428. Bool POWTruckAIUpdate::validateTarget( const Object *target )
  429. {
  430. return TheActionManager->canPickUpPrisoner( getObject(), target, getLastCommandSource() );
  431. } // end validateTarget
  432. // ------------------------------------------------------------------------------------------------
  433. /** Initiate a return prisoners contained in us to prison */
  434. // ------------------------------------------------------------------------------------------------
  435. void POWTruckAIUpdate::doReturnPrisoners( void )
  436. {
  437. // find the closest prison
  438. Object *prison = findBestPrison();
  439. // if no prison is available, nothing to do
  440. if( prison == NULL )
  441. {
  442. setTask( POW_TRUCK_TASK_WAITING );
  443. return;
  444. } // end if
  445. // start the prisoner return process
  446. Object *us = getObject();
  447. AIUpdateInterface *ai = us->getAIUpdateInterface();
  448. DEBUG_ASSERTCRASH( ai, ("POWTruckAIUpdate::doReturnPrisoners - '%s' has no AI\n",
  449. us->getTemplate()->getName().str()) );
  450. ai->aiReturnPrisoners( prison, CMD_FROM_AI );
  451. } // end doReturnPrisoners
  452. // ------------------------------------------------------------------------------------------------
  453. /** Initate a return of our empty truck back near the closest prison */
  454. // ------------------------------------------------------------------------------------------------
  455. void POWTruckAIUpdate::doReturnToPrison( Object *prison )
  456. {
  457. //
  458. // the great thing about this logic is that there isn't a state to "return to prison", we
  459. // just go into waiting, find a prison if we can, and tell the AI to move there
  460. //
  461. setTask( POW_TRUCK_TASK_WAITING );
  462. // find the closest prison if one was not provided
  463. if( prison == NULL )
  464. prison = findBestPrison();
  465. // if no prison found forget it
  466. if( prison == NULL )
  467. return;
  468. // get our info
  469. Object *us = getObject();
  470. const POWTruckAIUpdateModuleData *modData = getPOWTruckAIUpdateModuleData();
  471. // if we're close enough to it then just stay here
  472. Real distSq = ThePartitionManager->getDistanceSquared( us, prison, FROM_CENTER_2D );
  473. if( distSq <= modData->m_hangAroundPrisonDistance * modData->m_hangAroundPrisonDistance )
  474. return;
  475. // dock us with the prison
  476. aiDock( prison, CMD_FROM_AI );
  477. } // end doReturnToPrison
  478. // ------------------------------------------------------------------------------------------------
  479. /** Find the best prison for us to use given our current position */
  480. // ------------------------------------------------------------------------------------------------
  481. Object *POWTruckAIUpdate::findBestPrison( void )
  482. {
  483. ObjectID prisonID = getObject()->getProducerID();
  484. if( prisonID == INVALID_ID )
  485. return NULL;
  486. // find prison object
  487. Object *prison = TheGameLogic->findObjectByID( prisonID );
  488. return prison;
  489. } // end findBestPrison
  490. // ------------------------------------------------------------------------------------------------
  491. /** Find the best prisoner for us to go pick up given our current situation */
  492. // ------------------------------------------------------------------------------------------------
  493. Object *POWTruckAIUpdate::findBestTarget( void )
  494. {
  495. const Object *us = getObject();
  496. Player *player = us->getControllingPlayer();
  497. // sanity
  498. if( player == NULL )
  499. return NULL;
  500. // get our info
  501. const AIUpdateInterface *ai = us->getAIUpdateInterface();
  502. DEBUG_ASSERTCRASH( ai, ("POWTruckAIUpdate::findBestTarget- '%s' has no AI\n",
  503. us->getTemplate()->getName().str()) );
  504. // scan all objects, there is no range
  505. Object *other;
  506. Real closestTargetDistSq = HUGE_DIST;
  507. Object *closestTarget = NULL;
  508. for( other = TheGameLogic->getFirstObject(); other; other = other->getNextObject() )
  509. {
  510. //
  511. // run this target through our validation scheme and don't pick it in the first
  512. // place if it's not a valid target for us
  513. //
  514. if( validateTarget( other ) == FALSE )
  515. continue;
  516. // ignore targets that we cannot pathfind to as we will never be able to pick them up
  517. if( isQuickPathAvailable( other->getPosition() ) == FALSE )
  518. continue;
  519. // is this target closer than the one we've found so far
  520. Real distSq = ThePartitionManager->getDistanceSquared( us, other, FROM_CENTER_2D );
  521. if( closestTarget == NULL || distSq < closestTargetDistSq )
  522. {
  523. // we must be able to pathfind to this target
  524. if( TheAI->pathfinder()->quickDoesPathExist( ai->getLocomotorSet(),
  525. us->getPosition(),
  526. other->getPosition() ) == TRUE )
  527. {
  528. // this is our new closest target
  529. closestTarget = other;
  530. closestTargetDistSq = distSq;
  531. } // end if
  532. } // end if
  533. } // end for other
  534. return closestTarget;
  535. } // end findBestTarget
  536. // ------------------------------------------------------------------------------------------------
  537. /** We are chosing to pass a structure through the iterate function to unload the
  538. * prisoners because it's more flexible in that, perhaps someday in the future, we
  539. * could fail to add an object to the prison (maybe it's full or something), and in
  540. * that case it's better to manually do actions each time a successful
  541. * add happens rather than figure it out from before counts and after counts of the truck */
  542. // ------------------------------------------------------------------------------------------------
  543. struct PrisonUnloadData
  544. {
  545. Object *sourceTruck;
  546. Object *destPrison;
  547. UnsignedInt bounty;
  548. };
  549. // ------------------------------------------------------------------------------------------------
  550. // ------------------------------------------------------------------------------------------------
  551. static void putPrisonersInPrison( Object *obj, void *userData )
  552. {
  553. PrisonUnloadData *prisonUnloadData = (PrisonUnloadData *)userData;
  554. Object *prison = prisonUnloadData->destPrison;
  555. // sanity
  556. DEBUG_ASSERTCRASH( prison, ("putPrisonersInPrison: NULL user data\n") );
  557. DEBUG_ASSERTCRASH( obj->getContainedBy() != NULL,
  558. ("putPrisonersInPrison: Prisoner '%s' is not contained by anything, it should be contained by a POW truck\n",
  559. obj->getTemplate()->getName().str()) );
  560. // extra super sanity, just so that we don't crash ... this is in the assert above
  561. if( obj->getContainedBy() == NULL )
  562. return;
  563. // take 'obj' out of the truck
  564. ContainModuleInterface *sourceContain = obj->getContainedBy()->getContain();
  565. if( sourceContain )
  566. {
  567. sourceContain->removeFromContain( obj );
  568. }
  569. // put 'obj' into 'prison'
  570. ContainModuleInterface *destContain = prison->getContain();
  571. if( destContain )
  572. {
  573. destContain->addToContain( obj );
  574. }
  575. // add up bounty for this prisoner
  576. Player *prisonerOwningPlayer = obj->getControllingPlayer();
  577. prisonUnloadData->bounty += TheGlobalData->m_prisonBountyMultiplier *
  578. obj->getTemplate()->calcCostToBuild( prisonerOwningPlayer );
  579. } // end putPrisonersInPrison
  580. // ------------------------------------------------------------------------------------------------
  581. // ------------------------------------------------------------------------------------------------
  582. void POWTruckAIUpdate::unloadPrisonersToPrison( Object *prison )
  583. {
  584. Object *us = getObject();
  585. // sanity
  586. if( prison == NULL )
  587. return;
  588. // get contain modules
  589. ContainModuleInterface *truckContain = us->getContain();
  590. // sanity
  591. DEBUG_ASSERTCRASH( prison->getContain(), ("POWTruckAIUpdate::unloadPrisonersToPrison - '%s' has no contain\n",
  592. prison->getTemplate()->getName().str()) );
  593. DEBUG_ASSERTCRASH( prison->getContain()->asOpenContain(),
  594. ("POWTruckAIUpdate::unloadPrisonersToPrison - '%s' has no OPEN contain\n",
  595. prison->getTemplate()->getName().str()) );
  596. DEBUG_ASSERTCRASH( truckContain, ("POWTruckAIUpdate::unloadPrisonersToPrison - '%s' has no contain\n",
  597. us->getTemplate()->getName().str()) );
  598. DEBUG_ASSERTCRASH( truckContain->asOpenContain(),
  599. ("POWTruckAIUpdate::unloadPrisonersToPrison - '%s' has no OPEN contain\n",
  600. us->getTemplate()->getName().str()) );
  601. // put the prisoners in the prison
  602. PrisonUnloadData prisonUnloadData;
  603. prisonUnloadData.sourceTruck = us;
  604. prisonUnloadData.destPrison = prison;
  605. prisonUnloadData.bounty = 0;
  606. truckContain->iterateContained( putPrisonersInPrison, &prisonUnloadData, FALSE );
  607. // if the destination prison collects a bounty, award that to the player
  608. if( prison->isKindOf( KINDOF_COLLECTS_PRISON_BOUNTY ) &&
  609. prisonUnloadData.bounty > 0 )
  610. {
  611. Player *player = us->getControllingPlayer();
  612. if( player )
  613. {
  614. Money *money = player->getMoney();
  615. if( money )
  616. {
  617. // deposit the money
  618. money->deposit( prisonUnloadData.bounty );
  619. player->getScoreKeeper()->addMoneyEarned( prisonUnloadData.bounty );
  620. // display text above the building
  621. Color moneyColor = TheGlobalData->m_prisonBountyTextColor;
  622. UnicodeString moneyString;
  623. Coord3D pos = *prison->getPosition();
  624. pos.z += prison->getGeometryInfo().getMaxHeightAbovePosition();
  625. moneyString.format( TheGameText->fetch( "GUI:AddCash" ), prisonUnloadData.bounty );
  626. TheInGameUI->addFloatingText( moneyString, &pos, moneyColor );
  627. } // end if
  628. } // end if
  629. } // end if
  630. // set our state to waiting and become idle
  631. setTask( POW_TRUCK_TASK_WAITING );
  632. } // end unloadPrisonersToPrison
  633. // ------------------------------------------------------------------------------------------------
  634. /** We have arrived at a prisoner to load into us ... do it */
  635. // ------------------------------------------------------------------------------------------------
  636. void POWTruckAIUpdate::loadPrisoner( Object *prisoner )
  637. {
  638. Object *us = getObject();
  639. // sanity
  640. if( prisoner == NULL )
  641. return;
  642. // validate that we can load this prisoner
  643. if( validateTarget( prisoner ) == FALSE )
  644. return;
  645. // sanity check ... make sure we have room
  646. ContainModuleInterface *contain = us->getContain();
  647. if( contain && contain->getContainCount() == contain->getContainMax() )
  648. {
  649. // we're full ... return to prison
  650. doReturnPrisoners();
  651. return;
  652. } // end if
  653. // add object to our containment
  654. contain->addToContain( prisoner );
  655. //
  656. // removed the surrendered status of these prisoners to false, that way if the truck
  657. // is killed, the prison blows up releasing all the prisoners, whatever, they will
  658. // behave as normal
  659. //
  660. AIUpdateInterface *prisonerAI = prisoner->getAIUpdateInterface();
  661. if( prisonerAI )
  662. prisonerAI->setSurrendered( NULL, FALSE );
  663. // done adding prisoner, for automatic AI find another target, for manual just wait
  664. if( m_aiMode == AUTOMATIC )
  665. setTask( POW_TRUCK_TASK_FIND_TARGET );
  666. else
  667. setTask( POW_TRUCK_TASK_WAITING );
  668. } // end loadPrisoner
  669. // ------------------------------------------------------------------------------------------------
  670. /** CRC */
  671. // ------------------------------------------------------------------------------------------------
  672. void POWTruckAIUpdate::crc( Xfer *xfer )
  673. {
  674. // extend base class
  675. AIUpdateInterface::crc(xfer);
  676. } // end crc
  677. // ------------------------------------------------------------------------------------------------
  678. /** Xfer method
  679. * Version Info:
  680. * 1: Initial version */
  681. // ------------------------------------------------------------------------------------------------
  682. void POWTruckAIUpdate::xfer( Xfer *xfer )
  683. {
  684. // version
  685. XferVersion currentVersion = 1;
  686. XferVersion version = currentVersion;
  687. xfer->xferVersion( &version, currentVersion );
  688. // extend base class
  689. AIUpdateInterface::xfer(xfer);
  690. xfer->xferUser(&m_aiMode, sizeof(m_aiMode));
  691. xfer->xferUser(&m_currentTask, sizeof(m_currentTask));
  692. xfer->xferObjectID(&m_targetID);
  693. xfer->xferObjectID(&m_prisonID);
  694. xfer->xferUnsignedInt(&m_enteredWaitingFrame);
  695. xfer->xferUnsignedInt(&m_lastFindFrame);
  696. } // end xfer
  697. // ------------------------------------------------------------------------------------------------
  698. /** Load post process */
  699. // ------------------------------------------------------------------------------------------------
  700. void POWTruckAIUpdate::loadPostProcess( void )
  701. {
  702. // extend base class
  703. AIUpdateInterface::loadPostProcess();
  704. } // end loadPostProcess
  705. #endif