DozerAIUpdate.cpp 86 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491
  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: DozerAIUpdate.cpp ////////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, February 2002
  25. // Desc: Dozer AI behavior
  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/ThingTemplate.h"
  34. #include "Common/ThingFactory.h"
  35. #include "Common/Player.h"
  36. #include "Common/Money.h"
  37. #include "Common/Radar.h"
  38. #include "Common/RandomValue.h"
  39. #include "Common/GameState.h"
  40. #include "Common/GlobalData.h"
  41. #include "Common/Xfer.h"
  42. #include "GameClient/Drawable.h"
  43. #include "GameClient/GameText.h"
  44. #include "GameLogic/AIPathfind.h"
  45. #include "GameLogic/PartitionManager.h"
  46. #include "GameLogic/Locomotor.h"
  47. #include "GameLogic/Module/BodyModule.h"
  48. #include "GameLogic/Module/BridgeBehavior.h"
  49. #include "GameLogic/Module/BridgeTowerBehavior.h"
  50. #include "GameLogic/Module/CreateModule.h"
  51. #include "GameLogic/Module/DozerAIUpdate.h"
  52. #include "GameClient/InGameUI.h"
  53. #ifdef _INTERNAL
  54. // for occasional debugging...
  55. //#pragma optimize("", off)
  56. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  57. #endif
  58. // FORWARD DECLARATIONS ///////////////////////////////////////////////////////////////////////////
  59. class DozerPrimaryStateMachine;
  60. class DozerActionStateMachine;
  61. static const Real MIN_ACTION_TOLERANCE = 70.0f;
  62. ///////////////////////////////////////////////////////////////////////////////////////////////////
  63. ///////////////////////////////////////////////////////////////////////////////////////////////////
  64. ///////////////////////////////////////////////////////////////////////////////////////////////////
  65. //-------------------------------------------------------------------------------------------------
  66. /** Available Dozer actions */
  67. //-------------------------------------------------------------------------------------------------
  68. enum DozerActionType
  69. {
  70. DOZER_ACTION_PICK_ACTION_POS, ///< pick a location "around" the target to do our action
  71. DOZER_ACTION_MOVE_TO_ACTION_POS,///< move to our action pos we've picked
  72. DOZER_ACTION_DO_ACTION, ///< do our action at the position, build/repair/etc ...
  73. };
  74. //-------------------------------------------------------------------------------------------------
  75. /** Dozer picks a position around the target to do the action */
  76. //-------------------------------------------------------------------------------------------------
  77. class DozerActionPickActionPosState : public State
  78. {
  79. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerActionPickActionPosState, "DozerActionPickActionPosState")
  80. public:
  81. DozerActionPickActionPosState( StateMachine *machine, DozerTask task );
  82. virtual StateReturnType update( void );
  83. protected:
  84. // snapshot interface
  85. virtual void crc( Xfer *xfer );
  86. virtual void xfer( Xfer *xfer );
  87. virtual void loadPostProcess();
  88. protected:
  89. DozerTask m_task; ///< our task
  90. Int m_failedAttempts; /**< counter for successive unsuccessfull attempts to pick
  91. and move to an action position */
  92. };
  93. EMPTY_DTOR(DozerActionPickActionPosState)
  94. //-------------------------------------------------------------------------------------------------
  95. //-------------------------------------------------------------------------------------------------
  96. DozerActionPickActionPosState::DozerActionPickActionPosState( StateMachine *machine,
  97. DozerTask task ) :
  98. State( machine, "DozerActionPickActionPosState" )
  99. {
  100. m_task = task;
  101. m_failedAttempts = 0;
  102. } // end DozerActionPickActionPosState
  103. // ------------------------------------------------------------------------------------------------
  104. /** CRC */
  105. // ------------------------------------------------------------------------------------------------
  106. void DozerActionPickActionPosState::crc( Xfer *xfer )
  107. {
  108. } // end crc
  109. // ------------------------------------------------------------------------------------------------
  110. /** Xfer Method */
  111. // ------------------------------------------------------------------------------------------------
  112. void DozerActionPickActionPosState::xfer( Xfer *xfer )
  113. {
  114. // version
  115. XferVersion currentVersion = 1;
  116. XferVersion version = currentVersion;
  117. xfer->xferVersion( &version, currentVersion );
  118. xfer->xferUser(&m_task, sizeof(m_task));
  119. xfer->xferInt(&m_failedAttempts);
  120. } // end xfer
  121. // ------------------------------------------------------------------------------------------------
  122. /** Load post process */
  123. // ------------------------------------------------------------------------------------------------
  124. void DozerActionPickActionPosState::loadPostProcess( void )
  125. {
  126. } // end loadPostProcess
  127. //-------------------------------------------------------------------------------------------------
  128. /** Pick a position around the target */
  129. //-------------------------------------------------------------------------------------------------
  130. StateReturnType DozerActionPickActionPosState::update( void )
  131. {
  132. StateMachine *machine = getMachine();
  133. Object *dozer = machine->getOwner();
  134. // get dozer ai update interface
  135. AIUpdateInterface *ai = dozer->getAIUpdateInterface();
  136. if( !ai )
  137. {
  138. return STATE_FAILURE;
  139. }
  140. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  141. if( !dozerAI )
  142. {
  143. return STATE_FAILURE;
  144. }
  145. // get the object we're concerned with for our task
  146. Object *goalObject = TheGameLogic->findObjectByID( dozerAI->getTaskTarget( m_task ) );
  147. // if there is no goal, get out of this machine with a failure code (success is done in the action state )
  148. if( goalObject == NULL )
  149. {
  150. // to be clean get rid of the goal object we set
  151. getMachine()->setGoalObject( NULL );
  152. // cancel our task
  153. dozerAI->cancelTask( m_task );
  154. // exit with failure
  155. return STATE_FAILURE;
  156. } // end if
  157. // pick a location to move to
  158. Coord3D goalPos;
  159. const Coord3D *pos = dozerAI->getDockPoint( m_task, DOZER_DOCK_POINT_START );
  160. if( pos )
  161. goalPos = *pos;
  162. else
  163. {
  164. // pick a spot to use
  165. //
  166. // find the vector from goal object to us ... we will start our search on this angle so
  167. // that we "approach" a closer point rather than a point on a random side
  168. //
  169. Coord2D v;
  170. v.x = dozer->getPosition()->x - goalObject->getPosition()->x;
  171. v.y = dozer->getPosition()->y - goalObject->getPosition()->y;
  172. Real radius = goalObject->getGeometryInfo().getBoundingSphereRadius();
  173. FindPositionOptions fpOptions;
  174. fpOptions.minRadius = radius;
  175. fpOptions.maxRadius = radius;
  176. fpOptions.startAngle = v.toAngle();
  177. if( ThePartitionManager->findPositionAround( goalObject->getPosition(),
  178. &fpOptions,
  179. &goalPos ) == FALSE )
  180. {
  181. // return STATE_FAILURE; no, we don't ever want dozers to fail, particularly
  182. // if ai.
  183. goalPos = *goalObject->getPosition();
  184. } // end if
  185. //
  186. // we only ignore the goal object when we did the point selection in this more
  187. // "random" method cause we could pick a place inside the object
  188. //
  189. ai->ignoreObstacle( goalObject );
  190. } // end if
  191. // set goal position and object
  192. machine->setGoalObject( goalObject );
  193. machine->setGoalPosition( &goalPos );
  194. ai->ignoreObstacle(goalObject);
  195. ai->aiMoveToPosition( &goalPos, CMD_FROM_AI );
  196. return STATE_SUCCESS;
  197. } // end update
  198. //-------------------------------------------------------------------------------------------------
  199. /** Dozer moves to the action position */
  200. //-------------------------------------------------------------------------------------------------
  201. class DozerActionMoveToActionPosState : public State
  202. {
  203. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerActionMoveToActionPosState, "DozerActionMoveToActionPosState")
  204. public:
  205. DozerActionMoveToActionPosState( StateMachine *machine, DozerTask task ) : State( machine, "DozerActionMoveToActionPosState" ) { m_task = task; }
  206. virtual StateReturnType update( void );
  207. protected:
  208. // snapshot interface
  209. virtual void crc( Xfer *xfer );
  210. virtual void xfer( Xfer *xfer );
  211. virtual void loadPostProcess();
  212. protected:
  213. DozerTask m_task; ///< our task
  214. };
  215. EMPTY_DTOR(DozerActionMoveToActionPosState)
  216. // ------------------------------------------------------------------------------------------------
  217. /** CRC */
  218. // ------------------------------------------------------------------------------------------------
  219. void DozerActionMoveToActionPosState::crc( Xfer *xfer )
  220. {
  221. } // end crc
  222. // ------------------------------------------------------------------------------------------------
  223. /** Xfer Method */
  224. // ------------------------------------------------------------------------------------------------
  225. void DozerActionMoveToActionPosState::xfer( Xfer *xfer )
  226. {
  227. // version
  228. XferVersion currentVersion = 1;
  229. XferVersion version = currentVersion;
  230. xfer->xferVersion( &version, currentVersion );
  231. xfer->xferUser(&m_task, sizeof(m_task));
  232. } // end xfer
  233. // ------------------------------------------------------------------------------------------------
  234. /** Load post process */
  235. // ------------------------------------------------------------------------------------------------
  236. void DozerActionMoveToActionPosState::loadPostProcess( void )
  237. {
  238. } // end loadPostProcess
  239. //-------------------------------------------------------------------------------------------------
  240. /** We are supposed to be on route to our action position now, see when we get there or
  241. * detect that we have encountered a problem that's going to cause it to give up */
  242. //-------------------------------------------------------------------------------------------------
  243. StateReturnType DozerActionMoveToActionPosState::update( void )
  244. {
  245. Object *goalObject = getMachineGoalObject();
  246. Object *dozer = getMachineOwner();
  247. // sanity
  248. if( goalObject == NULL || dozer == NULL )
  249. return STATE_FAILURE;
  250. AIUpdateInterface *ai = dozer->getAIUpdateInterface();
  251. ObjectID currentRepairer = goalObject->getSoleHealingBenefactor();
  252. if( m_task==DOZER_TASK_REPAIR && currentRepairer != INVALID_ID && currentRepairer != dozer->getID() )//oops I guess someone beat me to it!
  253. {
  254. if ( ai )
  255. {
  256. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  257. if ( dozerAI )
  258. dozerAI->internalTaskComplete( m_task );
  259. }
  260. getMachine()->setGoalObject( NULL );
  261. return STATE_FAILURE;
  262. }
  263. // Double oops, someone else has taken over the build responsibility for this building. We can't double up.
  264. // This can happen because a guy who is moving to build who is told to build will idle for a frame after registering.
  265. // Two workers, two started buildings.
  266. // Grab both workers, resume on one building
  267. // First will register, second will see first as active and say no
  268. // Grab both, click on other building
  269. // First will register, and idle for a frame, second will see first as not active and say yes
  270. // Next frame, first will start the build task, without reasking validity
  271. // Infinite number of workers can be told to build something with just two in progress buildings
  272. if( (m_task == DOZER_TASK_BUILD) && goalObject && (goalObject->getBuilderID() != dozer->getID()) )
  273. {
  274. // Geebus. Returning failure is ignored, so you have to explicitly make the ai stop trying to restart the machine
  275. if ( ai )
  276. {
  277. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  278. if ( dozerAI )
  279. dozerAI->internalTaskComplete( m_task );
  280. }
  281. getMachine()->setGoalObject( NULL );
  282. return STATE_FAILURE;
  283. }
  284. // if distance between us and our goal position is close enough
  285. const Coord3D *goalPos = getMachine()->getGoalPosition();
  286. Real distSqr = ThePartitionManager->getDistanceSquared( dozer, goalPos, FROM_BOUNDINGSPHERE_2D );
  287. const Real SLOP = 15.0f;
  288. Real allowableDistanceSqr = sqr(max( MIN_ACTION_TOLERANCE, dozer->getGeometryInfo().getBoundingSphereRadius() + SLOP ));
  289. if( distSqr <= allowableDistanceSqr )
  290. {
  291. if( m_task == DOZER_TASK_BUILD )
  292. {
  293. //
  294. // the object is now no longer awaiting construction, it is being constructed ... note
  295. // that we might possibly be here multiple times for a single object being built
  296. // but the setting of the same model condition doesn't have any adverse effects
  297. //
  298. goalObject->clearAndSetModelConditionFlags(
  299. MAKE_MODELCONDITION_MASK(MODELCONDITION_AWAITING_CONSTRUCTION),
  300. MAKE_MODELCONDITION_MASK2(MODELCONDITION_PARTIALLY_CONSTRUCTED, MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED));
  301. } // end if
  302. return STATE_SUCCESS;
  303. } // end if
  304. // if we're in the idle state fail our move
  305. // Failure transition is back to DOZER_ACTION_PICK_ACTION_POS, so
  306. // it is ok to fail. jba.
  307. if( ai && ai->isIdle() )
  308. return STATE_FAILURE;
  309. return STATE_CONTINUE;
  310. } // end update
  311. //-------------------------------------------------------------------------------------------------
  312. /** Dozer does the "action" */
  313. //-------------------------------------------------------------------------------------------------
  314. class DozerActionDoActionState : public State
  315. {
  316. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerActionDoActionState, "DozerActionDoActionState")
  317. public:
  318. DozerActionDoActionState( StateMachine *machine, DozerTask task ) : State( machine, "DozerActionDoActionState" )
  319. {
  320. m_task = task;
  321. //Added By Sadullah Nader
  322. // Initializations missing and needed
  323. m_enterFrame = 0;
  324. }
  325. virtual StateReturnType update( void );
  326. virtual StateReturnType onEnter( void );
  327. virtual void onExit( StateExitType status ) { }
  328. protected:
  329. // snapshot interface
  330. virtual void crc( Xfer *xfer );
  331. virtual void xfer( Xfer *xfer );
  332. virtual void loadPostProcess();
  333. protected:
  334. DozerTask m_task; ///< our task
  335. UnsignedInt m_enterFrame; ///< frame we entered this state on
  336. };
  337. EMPTY_DTOR(DozerActionDoActionState)
  338. // ------------------------------------------------------------------------------------------------
  339. /** CRC */
  340. // ------------------------------------------------------------------------------------------------
  341. void DozerActionDoActionState::crc( Xfer *xfer )
  342. {
  343. } // end crc
  344. // ------------------------------------------------------------------------------------------------
  345. /** Xfer Method */
  346. // ------------------------------------------------------------------------------------------------
  347. void DozerActionDoActionState::xfer( Xfer *xfer )
  348. {
  349. // version
  350. XferVersion currentVersion = 1;
  351. XferVersion version = currentVersion;
  352. xfer->xferVersion( &version, currentVersion );
  353. xfer->xferUser(&m_task, sizeof(m_task));
  354. xfer->xferUnsignedInt(&m_enterFrame);
  355. } // end xfer
  356. // ------------------------------------------------------------------------------------------------
  357. /** Load post process */
  358. // ------------------------------------------------------------------------------------------------
  359. void DozerActionDoActionState::loadPostProcess( void )
  360. {
  361. } // end loadPostProcess
  362. //-------------------------------------------------------------------------------------------------
  363. /** Entering the do action state */
  364. //-------------------------------------------------------------------------------------------------
  365. StateReturnType DozerActionDoActionState::onEnter( void )
  366. {
  367. Object *dozer = getMachineOwner();
  368. DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
  369. if( !dozerAI )
  370. {
  371. return STATE_FAILURE;
  372. }
  373. // DozerAIUpdate *dozerAI = static_cast<DozerAIUpdate *>(dozer->getAIUpdateInterface());
  374. // record the frame we came in on
  375. m_enterFrame = TheGameLogic->getFrame();
  376. // when building, we have additional movement that we will for docking
  377. if( m_task == DOZER_TASK_BUILD )
  378. dozerAI->setBuildSubTask( DOZER_SELECT_BUILD_DOCK_LOCATION );
  379. return STATE_CONTINUE;
  380. } // end onEnter
  381. //-------------------------------------------------------------------------------------------------
  382. /** Do the action */
  383. //-------------------------------------------------------------------------------------------------
  384. StateReturnType DozerActionDoActionState::update( void )
  385. {
  386. Object *goalObject = getMachineGoalObject();
  387. Object *dozer = getMachineOwner();
  388. AIUpdateInterface *ai = dozer->getAIUpdateInterface();
  389. if( !ai )
  390. {
  391. return STATE_FAILURE;
  392. }
  393. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  394. if( !dozerAI )
  395. {
  396. return STATE_FAILURE;
  397. }
  398. // DozerAIUpdate *dozerAI = static_cast<DozerAIUpdate *>(dozer->getAIUpdateInterface());
  399. // const UnsignedInt ACTION_TIME = LOGICFRAMES_PER_SECOND * 4 ; // frames to spend here in this state doing the action
  400. // check for object gone
  401. if( goalObject == NULL )
  402. return STATE_FAILURE;
  403. if ( dozer->isDisabledByType( DISABLED_UNMANNED ) )// Yipes, I've been sniped!
  404. return STATE_FAILURE;
  405. // do the task
  406. Bool complete = FALSE;
  407. switch( m_task )
  408. {
  409. //---------------------------------------------------------------------------------------------
  410. case DOZER_TASK_BUILD:
  411. {
  412. //GS Moved this inside Build, since you are allowed to Repair things that are not your player (canRepairObject handles it)
  413. if (dozer->getControllingPlayer() != goalObject->getControllingPlayer())//Yipes, SOmehow I have changed sides in mid build!
  414. return STATE_FAILURE;
  415. // if we need to select the dock location and move there do so
  416. if( dozerAI->getBuildSubTask() == DOZER_SELECT_BUILD_DOCK_LOCATION )
  417. {
  418. const Coord3D *dockLocation = dozerAI->getDockPoint( m_task, DOZER_DOCK_POINT_ACTION );
  419. if( dockLocation )
  420. ai->aiMoveToPosition( dockLocation, CMD_FROM_AI );
  421. // we're now moving to the dock location
  422. dozerAI->setBuildSubTask( DOZER_MOVING_TO_BUILD_DOCK_LOCATION );
  423. } // end if
  424. // if we're moving to the build dock location, when we become idle we are there
  425. if( dozerAI->getBuildSubTask() == DOZER_MOVING_TO_BUILD_DOCK_LOCATION )
  426. {
  427. if( ai->isIdle() )
  428. {
  429. dozerAI->setBuildSubTask( DOZER_DO_BUILD_AT_DOCK );
  430. // Get the audio sound and start playing the construction sound (get the sound
  431. // from the building itself)
  432. dozerAI->startBuildingSound( goalObject->getTemplate()->getPerUnitSound( "UnderConstruction" ), goalObject->getID() );
  433. }
  434. } // end if
  435. // only do the build if we've moved into the dock position
  436. if( dozerAI->getBuildSubTask() == DOZER_DO_BUILD_AT_DOCK )
  437. {
  438. // the builder is now actively constructing something
  439. dozer->setModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  440. // increase the construction percent of the goal object
  441. Int framesToBuild = goalObject->getTemplate()->calcTimeToBuild( dozer->getControllingPlayer() );
  442. Real percentProgressThisFrame = 100.0f / framesToBuild;
  443. goalObject->setConstructionPercent( goalObject->getConstructionPercent() +
  444. percentProgressThisFrame );
  445. //
  446. // every time we construct a piece of the goal object, the goal object gets a little
  447. // bit o health, note that we're bypassing the regular healing/damage methods
  448. // here and going straight for the change of the health
  449. //
  450. BodyModuleInterface *body = goalObject->getBodyModule();
  451. body->internalChangeHealth( body->getMaxHealth() / INT_TO_REAL( framesToBuild ) );
  452. //
  453. // since we've just actually contributed a "piece" to this building, we'll say that
  454. // we are now the producer and the builder
  455. //
  456. goalObject->setProducer( dozer );
  457. goalObject->setBuilder( dozer );
  458. // check for construction complete
  459. if( goalObject->getConstructionPercent() >= 100.0f )
  460. {
  461. // clear the under construction status
  462. goalObject->clearStatus( OBJECT_STATUS_UNDER_CONSTRUCTION );
  463. goalObject->clearStatus( OBJECT_STATUS_RECONSTRUCTING );
  464. // stop playing the construction sound!
  465. dozerAI->finishBuildingSound();
  466. // object will now be idle instead of in one of the construction actions
  467. goalObject->clearModelConditionFlags(
  468. MAKE_MODELCONDITION_MASK3(MODELCONDITION_AWAITING_CONSTRUCTION, MODELCONDITION_PARTIALLY_CONSTRUCTED, MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED));
  469. // set the construction at the 100% enum value
  470. goalObject->setConstructionPercent( CONSTRUCTION_COMPLETE );
  471. //
  472. // now that we're done with construction we evaluate the visual condition of
  473. // the object since while under construction visual changes due to damage state
  474. // do not occur. Note that is was important to call this after we cleared the
  475. // model conditions involving construction because part of this evaluation
  476. // is to auto populate the model with particle systems when special named
  477. // bones for the current given state are provided
  478. //
  479. body->evaluateVisualCondition();
  480. // this object now has energy influence in the player
  481. Player *player = goalObject->getControllingPlayer();
  482. if( player )
  483. {
  484. // notification for build completeion
  485. player->onStructureConstructionComplete( dozer, goalObject, dozerAI->getIsRebuild() );
  486. //
  487. // Now onCreates were called at construction start. Now at finish is when we
  488. // want the Game side of OnCreate
  489. //
  490. for (BehaviorModule** m = goalObject->getBehaviorModules(); *m; ++m)
  491. {
  492. CreateModuleInterface* create = (*m)->getCreate();
  493. if (!create)
  494. continue;
  495. create->onBuildComplete();
  496. }
  497. } // end if
  498. // Creation is another valid and essential time to call this. This building now Looks.
  499. goalObject->handlePartitionCellMaintenance();
  500. // this object how has influence in the controlling players' tech tree
  501. /// @todo need to write this
  502. // do some UI stuff for the constrolling player
  503. if( dozer->isLocallyControlled() )
  504. {
  505. // message the the building player
  506. UnicodeString format = TheGameText->fetch( "DOZER:ConstructionComplete" );
  507. UnicodeString objectName = goalObject->getTemplate()->getDisplayName();
  508. if( objectName.isEmpty() )
  509. {
  510. UnicodeString format = TheGameText->fetch( "INI:MissingDisplayName" );
  511. objectName.format( format, goalObject->getTemplate()->getName().str() );
  512. } // end if
  513. UnicodeString msg;
  514. msg.format( format.str(), objectName.str() );
  515. TheInGameUI->message( msg );
  516. AudioEventRTS audio = *dozer->getTemplate()->getVoiceTaskComplete();
  517. audio.setObjectID(dozer->getID());
  518. TheAudio->addAudioEvent(&audio);
  519. /// make radar neat-o attention grabber event at build location
  520. TheRadar->createEvent( goalObject->getPosition(), RADAR_EVENT_CONSTRUCTION );
  521. } // end if
  522. // this will allow us to exit the state machine with success
  523. complete = TRUE;
  524. // move off to the end dock position if present
  525. const Coord3D *endPos = dozerAI->getDockPoint( m_task, DOZER_DOCK_POINT_END );
  526. Coord3D pos = *dozer->getPosition();
  527. if( endPos ) {
  528. pos = *endPos;
  529. }
  530. // Our goal may be inside a building, if we are packing them in tight, so try adjusting. jba.
  531. TheAI->pathfinder()->adjustToPossibleDestination(dozer, ai->getLocomotorSet(), &pos);
  532. ai->aiMoveToPosition( &pos, CMD_FROM_AI );
  533. } // end if
  534. } // end if
  535. break;
  536. } // end build
  537. //---------------------------------------------------------------------------------------------
  538. case DOZER_TASK_REPAIR:
  539. {
  540. BodyModuleInterface *body = goalObject->getBodyModule();
  541. // check for fully "repaired"
  542. if( body->getHealth() == body->getMaxHealth() )
  543. {
  544. // issue repair complete message, we might want to remove this later cause it could be annoying
  545. TheInGameUI->message( "DOZER:RepairComplete" );
  546. // we're now complete
  547. complete = TRUE;
  548. } // end if
  549. else
  550. {
  551. Bool canHeal = TRUE;
  552. // if we are repairing a bridge, create scaffolding over the bridge if we need to
  553. if( goalObject->isKindOf( KINDOF_BRIDGE_TOWER ) )
  554. dozerAI->createBridgeScaffolding( goalObject );
  555. // the builder is now actively repairing something, we'll borrow the constructing animation
  556. dozer->setModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  557. //
  558. // when repairing bridges, we cannot actually do any repairing until the
  559. // scaffolding is extended and all the way complete
  560. //
  561. if( goalObject->isKindOf( KINDOF_BRIDGE_TOWER ) )
  562. {
  563. BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( goalObject );
  564. DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower interface\n") );
  565. Object *bridgeObject = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  566. DEBUG_ASSERTCRASH( bridgeObject, ("Unable to find bridge center object\n") );
  567. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridgeObject );
  568. DEBUG_ASSERTCRASH( bbi, ("Unable to find bridge interface from tower goal object during repair\n") );
  569. if( bbi->isScaffoldInMotion() == TRUE )
  570. canHeal = FALSE;
  571. } // end if
  572. // do healing
  573. if( canHeal )
  574. {
  575. // figure out how much health we will restore this frame
  576. Real health = body->getMaxHealth() * dozerAI->getRepairHealthPerSecond() /
  577. LOGICFRAMES_PER_SECOND;
  578. // try to give it a little bit-o-health
  579. if ( ! goalObject->attemptHealingFromSoleBenefactor(health, dozer, 2) )//this frame and the next
  580. {
  581. // goalObject->setStatus( OBJECT_STATUS_UNDERGOING_REPAIR );
  582. // This bit used to be set way back in DozerAIUpdate::privateRepair(), but it has been outmoded
  583. // so that several dozers/workers can target the same sick building for repair, but the first one to begin
  584. // this is done by attemptHealingFromSoleBenefactor() which lets the beneficiary of the healing
  585. // remember who has been healing it, and will return false to everybody else
  586. //or if the goalObject is already receiving healing, I must stop, since my healing is getting rejected here
  587. dozerAI->internalTaskComplete( m_task );
  588. getMachine()->setGoalObject( NULL );
  589. return STATE_FAILURE;
  590. }
  591. } // end if
  592. } // end else
  593. // play a repairing sound
  594. break;
  595. } // end repair
  596. //---------------------------------------------------------------------------------------------
  597. case DOZER_TASK_FORTIFY:
  598. {
  599. /// @todo write me
  600. break;
  601. } // end fortify
  602. //---------------------------------------------------------------------------------------------
  603. default:
  604. {
  605. DEBUG_CRASH(( "Unknown task for the dozer action do action state\n" ));
  606. return STATE_FAILURE;
  607. } // end default
  608. } // end switch
  609. // if we're complete with the task we exit success
  610. if( complete == TRUE )
  611. {
  612. // this task is now complete, remove it from dozer consideration
  613. dozerAI->internalTaskComplete( m_task );
  614. // to be clean get rid of the goal object we set
  615. getMachine()->setGoalObject( NULL );
  616. getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
  617. // we're done
  618. return STATE_SUCCESS;
  619. } // end if
  620. // just continue sitting here for a while
  621. getMachineOwner()->clearWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);// no mine clearing fun while I'm on the job
  622. return STATE_CONTINUE;
  623. } // end update
  624. //-------------------------------------------------------------------------------------------------
  625. /** The Dozer action state machine */
  626. //-------------------------------------------------------------------------------------------------
  627. class DozerActionStateMachine : public StateMachine
  628. {
  629. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( DozerActionStateMachine, "DozerActionStateMachine" );
  630. public:
  631. DozerActionStateMachine( Object *owner, DozerTask task );
  632. // virtual destructor prototypes provided by memory pool object
  633. protected:
  634. // snapshot interface
  635. virtual void crc( Xfer *xfer );
  636. virtual void xfer( Xfer *xfer );
  637. virtual void loadPostProcess();
  638. protected:
  639. DozerTask m_task; ///< the task of this action state machine
  640. };
  641. EMPTY_DTOR(DozerActionStateMachine)
  642. //-------------------------------------------------------------------------------------------------
  643. //-------------------------------------------------------------------------------------------------
  644. DozerActionStateMachine::DozerActionStateMachine( Object *owner, DozerTask task ) :
  645. StateMachine( owner, "DozerActionStateMachine" )
  646. {
  647. // initialize our task
  648. m_task = task;
  649. // order matters: first state is the default state.
  650. defineState( DOZER_ACTION_PICK_ACTION_POS, newInstance(DozerActionPickActionPosState)( this, task ), DOZER_ACTION_MOVE_TO_ACTION_POS, EXIT_MACHINE_WITH_FAILURE );
  651. defineState( DOZER_ACTION_MOVE_TO_ACTION_POS, newInstance(DozerActionMoveToActionPosState)( this, task ), DOZER_ACTION_DO_ACTION, DOZER_ACTION_PICK_ACTION_POS );
  652. defineState( DOZER_ACTION_DO_ACTION, newInstance(DozerActionDoActionState)( this, task ), EXIT_MACHINE_WITH_SUCCESS, EXIT_MACHINE_WITH_FAILURE );
  653. }
  654. // ------------------------------------------------------------------------------------------------
  655. /** CRC */
  656. // ------------------------------------------------------------------------------------------------
  657. void DozerActionStateMachine::crc( Xfer *xfer )
  658. {
  659. } // end crc
  660. // ------------------------------------------------------------------------------------------------
  661. /** Xfer Method */
  662. // ------------------------------------------------------------------------------------------------
  663. void DozerActionStateMachine::xfer( Xfer *xfer )
  664. {
  665. // version
  666. XferVersion currentVersion = 1;
  667. XferVersion version = currentVersion;
  668. xfer->xferVersion( &version, currentVersion );
  669. xfer->xferUser(&m_task, sizeof(m_task));
  670. } // end xfer
  671. // ------------------------------------------------------------------------------------------------
  672. /** Load post process */
  673. // ------------------------------------------------------------------------------------------------
  674. void DozerActionStateMachine::loadPostProcess( void )
  675. {
  676. } // end loadPostProcess
  677. ///////////////////////////////////////////////////////////////////////////////////////////////////
  678. ///////////////////////////////////////////////////////////////////////////////////////////////////
  679. ///////////////////////////////////////////////////////////////////////////////////////////////////
  680. //-------------------------------------------------------------------------------------------------
  681. //-------------------------------------------------------------------------------------------------
  682. static Object *findObjectToRepair( Object *dozer )
  683. {
  684. // sanity
  685. if( dozer == NULL )
  686. return NULL;
  687. if( !dozer->getAIUpdateInterface() )
  688. {
  689. return NULL;
  690. }
  691. const DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
  692. PartitionFilterSamePlayer filter1( dozer->getControllingPlayer() );
  693. PartitionFilterAcceptByKindOf filter2( MAKE_KINDOF_MASK( KINDOF_STRUCTURE ),
  694. KINDOFMASK_NONE );
  695. PartitionFilterSameMapStatus filterMapStatus(dozer);
  696. PartitionFilter *filters[] = { &filter1, &filter2, &filterMapStatus, NULL };
  697. ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( dozer->getPosition(),
  698. dozerAI->getBoredRange(),
  699. FROM_CENTER_2D,
  700. filters );
  701. MemoryPoolObjectHolder hold( iter );
  702. Object *obj;
  703. Object *closestRepairTarget = NULL;
  704. Real closestRepairTargetDistSqr = 0.0f;
  705. for( obj = iter->first(); obj; obj = iter->next() )
  706. {
  707. // ignore objects we cant repair
  708. if( TheActionManager->canRepairObject( dozer, obj, CMD_FROM_AI ) == FALSE )
  709. continue;
  710. // target the closest valid repair target
  711. if( closestRepairTarget == NULL )
  712. {
  713. closestRepairTarget = obj;
  714. closestRepairTargetDistSqr = ThePartitionManager->getDistanceSquared( dozer, obj, FROM_CENTER_2D );
  715. } // end if
  716. else
  717. {
  718. // only use this command center if it's closer than the last one we found
  719. Real distSqr = ThePartitionManager->getDistanceSquared( dozer, obj, FROM_CENTER_2D );
  720. if( distSqr < closestRepairTargetDistSqr )
  721. {
  722. closestRepairTarget = obj;
  723. closestRepairTargetDistSqr = distSqr;
  724. } // end if
  725. } // end else
  726. } // end for obj
  727. return closestRepairTarget;
  728. } // end findObjectToRepair
  729. //-------------------------------------------------------------------------------------------------
  730. //-------------------------------------------------------------------------------------------------
  731. static Object *findMine( Object *dozer )
  732. {
  733. // sanity
  734. if( dozer == NULL )
  735. return NULL;
  736. if( !dozer->getAIUpdateInterface() )
  737. {
  738. return NULL;
  739. }
  740. const DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
  741. // srj sez: only clear enemy or neutal mines. clearing allied mines (ie, OURS) is really dumb.
  742. // (and no, PossibleToAttack won't necessarily filter these out.)
  743. PartitionFilterRelationship filterTeam(dozer, PartitionFilterRelationship::ALLOW_ENEMIES | PartitionFilterRelationship::ALLOW_NEUTRAL);
  744. PartitionFilterPossibleToAttack filterAttack(ATTACK_NEW_TARGET, dozer, CMD_FROM_DOZER);
  745. PartitionFilterSameMapStatus filterMapStatus(dozer);
  746. PartitionFilter *filters[] = { &filterTeam, &filterAttack, &filterMapStatus, NULL };
  747. Object* mine = ThePartitionManager->getClosestObject(dozer, dozerAI->getBoredRange(), FROM_CENTER_2D, filters);
  748. return mine;
  749. } // end findMine
  750. //-------------------------------------------------------------------------------------------------
  751. /** Available primary Dozer states */
  752. //-------------------------------------------------------------------------------------------------
  753. enum
  754. {
  755. DOZER_PRIMARY_IDLE, ///< dozer is idle
  756. DOZER_PRIMARY_BUILD, ///< dozer is building a structure for the player
  757. DOZER_PRIMARY_REPAIR, ///< dozer is repairing something
  758. DOZER_PRIMARY_FORTIFY, ///< dozer is fortifying a civilian building, making it stronger
  759. DOZER_PRIMARY_GO_HOME, ///< dozer has nothing else to do so it will return a command center
  760. };
  761. //-------------------------------------------------------------------------------------------------
  762. /** Dozer primary idle state */
  763. //-------------------------------------------------------------------------------------------------
  764. class DozerPrimaryIdleState : public State
  765. {
  766. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerPrimaryIdleState, "DozerPrimaryIdleState")
  767. public:
  768. DozerPrimaryIdleState( StateMachine *machine ) : State( machine, "DozerPrimaryIdleState" )
  769. {
  770. m_idleTooLongTimestamp = 0;
  771. m_idlePlayerNumber = 0;
  772. m_isMarkedAsIdle = FALSE;
  773. }
  774. virtual StateReturnType update( void );
  775. virtual StateReturnType onEnter( void );
  776. virtual void onExit( StateExitType status );
  777. protected:
  778. // snapshot interface
  779. virtual void crc( Xfer *xfer );
  780. virtual void xfer( Xfer *xfer );
  781. virtual void loadPostProcess();
  782. protected:
  783. UnsignedInt m_idleTooLongTimestamp; ///< when this is more than our idle too long time we try to do something about it
  784. Int m_idlePlayerNumber; ///< Remeber what list we were added to.
  785. Bool m_isMarkedAsIdle;
  786. };
  787. EMPTY_DTOR(DozerPrimaryIdleState)
  788. // ------------------------------------------------------------------------------------------------
  789. /** CRC */
  790. // ------------------------------------------------------------------------------------------------
  791. void DozerPrimaryIdleState::crc( Xfer *xfer )
  792. {
  793. } // end crc
  794. // ------------------------------------------------------------------------------------------------
  795. /** Xfer Method */
  796. // ------------------------------------------------------------------------------------------------
  797. void DozerPrimaryIdleState::xfer( Xfer *xfer )
  798. {
  799. // version
  800. XferVersion currentVersion = 1;
  801. XferVersion version = currentVersion;
  802. xfer->xferVersion( &version, currentVersion );
  803. xfer->xferUnsignedInt(&m_idleTooLongTimestamp);
  804. xfer->xferInt(&m_idlePlayerNumber);
  805. xfer->xferBool(&m_isMarkedAsIdle);
  806. } // end xfer
  807. // ------------------------------------------------------------------------------------------------
  808. /** Load post process */
  809. // ------------------------------------------------------------------------------------------------
  810. void DozerPrimaryIdleState::loadPostProcess( void )
  811. {
  812. } // end loadPostProcess
  813. //-------------------------------------------------------------------------------------------------
  814. /** Upon entering the dozer primary idle state */
  815. //-------------------------------------------------------------------------------------------------
  816. StateReturnType DozerPrimaryIdleState::onEnter( void )
  817. {
  818. //
  819. // upon entering this state we begin tracking how long we're idle for to try to do something
  820. // about it every once in a while
  821. //
  822. m_idleTooLongTimestamp = TheGameLogic->getFrame();
  823. //if(TheGameLogic->getFrame() > m_idleNotifyTimestamp + NOTIFY_AFTER_TIME)
  824. m_isMarkedAsIdle = FALSE;
  825. getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
  826. return STATE_CONTINUE;
  827. } // end onEnter
  828. //-------------------------------------------------------------------------------------------------
  829. /** Upon exiting the dozer primary idle state */
  830. //-------------------------------------------------------------------------------------------------
  831. void DozerPrimaryIdleState::onExit( StateExitType status )
  832. {
  833. if(m_isMarkedAsIdle)
  834. {
  835. TheInGameUI->removeIdleWorker(getMachineOwner(), m_idlePlayerNumber);
  836. m_idlePlayerNumber = -1;
  837. m_isMarkedAsIdle = FALSE;
  838. }
  839. }
  840. //-------------------------------------------------------------------------------------------------
  841. /** Dozer idle behavior */
  842. //-------------------------------------------------------------------------------------------------
  843. StateReturnType DozerPrimaryIdleState::update( void )
  844. {
  845. Object *dozer = getMachineOwner();
  846. AIUpdateInterface *ai = dozer->getAIUpdateInterface();
  847. if( !ai )
  848. {
  849. return STATE_FAILURE;
  850. }
  851. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  852. if( !dozerAI )
  853. {
  854. return STATE_FAILURE;
  855. }
  856. //
  857. // These are to add into the IngameUI idle worker button thingy
  858. // we don't want to add in if we're already in the list or if
  859. // we're "Effectivly dead"
  860. //
  861. if( ai->isIdle() && !m_isMarkedAsIdle && !dozer->isEffectivelyDead())
  862. {
  863. m_idlePlayerNumber = dozer->getControllingPlayer()->getPlayerIndex();
  864. TheInGameUI->addIdleWorker(getMachineOwner());
  865. m_isMarkedAsIdle = TRUE;
  866. getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
  867. }
  868. if( m_isMarkedAsIdle && (!ai->isIdle() || dozer->isEffectivelyDead()))
  869. {
  870. TheInGameUI->removeIdleWorker(getMachineOwner(), m_idlePlayerNumber);
  871. m_idlePlayerNumber = -1;
  872. m_isMarkedAsIdle = FALSE;
  873. }
  874. //
  875. // if our actual real AI state machine is not idle we're doing something so let's reset
  876. // our idle too long timestamp
  877. //
  878. if( !ai->isIdle() )
  879. m_idleTooLongTimestamp = TheGameLogic->getFrame();
  880. //
  881. // if we're just sitting here in idle, and there is no task pending ... after so long we'll
  882. // try to go to the nearest command center
  883. //
  884. if( TheGameLogic->getFrame() - m_idleTooLongTimestamp > dozerAI->getBoredTime() &&
  885. dozerAI->isAnyTaskPending() == FALSE )
  886. {
  887. //
  888. // to prevent us from doing this potentially expensive logic to find objects every frame
  889. // we'll only try it every once in a while so set our idle too long timestamp to the
  890. // current frame
  891. //
  892. m_idleTooLongTimestamp = TheGameLogic->getFrame();
  893. // try to find something around us that we can repair
  894. Object *repairTarget = findObjectToRepair( dozer );
  895. if( repairTarget )
  896. {
  897. // issue the command
  898. ai->aiRepair( repairTarget, CMD_FROM_AI );
  899. //
  900. // in theory we would need to "interrupt" whatever it is the Dozer is doing now, but
  901. // we know we're in the idle state doing nothing so we don't really need to
  902. //
  903. } else {
  904. getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
  905. Object *mine = findMine(dozer);
  906. if (mine!=NULL) {
  907. ai->aiAttackObject( mine, 1, CMD_FROM_DOZER);
  908. }
  909. }
  910. } // end if
  911. return STATE_CONTINUE;
  912. } // end update
  913. //-------------------------------------------------------------------------------------------------
  914. /** Dozer action state */
  915. //-------------------------------------------------------------------------------------------------
  916. class DozerActionState : public State
  917. {
  918. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerActionState, "DozerActionState")
  919. public:
  920. DozerActionState( StateMachine *machine, DozerTask task ) : State( machine, "DozerActionState" )
  921. {
  922. m_task = task;
  923. m_actionMachine = newInstance(DozerActionStateMachine)( getMachineOwner(), task );
  924. m_actionMachine->initDefaultState();
  925. }
  926. /*
  927. Note that we DON'T use CONVERT_SLEEP_TO_CONTINUE; since we're not doing anything else
  928. interesting in update, we can sleep when this machine sleeps
  929. */
  930. virtual StateReturnType update( void ) { return m_actionMachine->updateStateMachine(); }
  931. virtual StateReturnType onEnter( void );
  932. virtual void onExit( StateExitType status );
  933. protected:
  934. // snapshot interface
  935. virtual void crc( Xfer *xfer );
  936. virtual void xfer( Xfer *xfer );
  937. virtual void loadPostProcess();
  938. protected:
  939. DozerTask m_task; // task for this state (and therefore sub-machine too)
  940. StateMachine *m_actionMachine;
  941. };
  942. inline DozerActionState::~DozerActionState( void ) { if (m_actionMachine) m_actionMachine->deleteInstance(); }
  943. // ------------------------------------------------------------------------------------------------
  944. /** CRC */
  945. // ------------------------------------------------------------------------------------------------
  946. void DozerActionState::crc( Xfer *xfer )
  947. {
  948. } // end crc
  949. // ------------------------------------------------------------------------------------------------
  950. /** Xfer Method */
  951. // ------------------------------------------------------------------------------------------------
  952. void DozerActionState::xfer( Xfer *xfer )
  953. {
  954. // version
  955. XferVersion currentVersion = 1;
  956. XferVersion version = currentVersion;
  957. xfer->xferVersion( &version, currentVersion );
  958. xfer->xferUser(&m_task, sizeof(m_task));
  959. xfer->xferSnapshot(m_actionMachine);
  960. } // end xfer
  961. // ------------------------------------------------------------------------------------------------
  962. /** Load post process */
  963. // ------------------------------------------------------------------------------------------------
  964. void DozerActionState::loadPostProcess( void )
  965. {
  966. } // end loadPostProcess
  967. // ------------------------------------------------------------------------------------------------
  968. // ------------------------------------------------------------------------------------------------
  969. StateReturnType DozerActionState::onEnter( void )
  970. {
  971. // save this as the current action of the dozer
  972. Object *dozer = getMachineOwner();
  973. if( !dozer->getAIUpdateInterface() )
  974. {
  975. return STATE_FAILURE;
  976. }
  977. DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
  978. dozerAI->setCurrentTask( m_task );
  979. //
  980. // we have a machine within this state all it's own ...
  981. // note that during an onEnter, since the action machine is persistent, anytime we transition
  982. // into doing an action, we need to reset our state machine to do that action as it may
  983. // have been left in any state from the previous time we ran it
  984. //
  985. m_actionMachine->resetToDefaultState();
  986. return STATE_CONTINUE;
  987. } // end onEnter
  988. // ------------------------------------------------------------------------------------------------
  989. // ------------------------------------------------------------------------------------------------
  990. void DozerActionState::onExit( StateExitType status )
  991. {
  992. // save the current action of the dozer as none
  993. Object *dozer = getMachineOwner();
  994. if( !dozer->getAIUpdateInterface() )
  995. {
  996. return;
  997. }
  998. DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
  999. dozerAI->setCurrentTask( DOZER_TASK_INVALID );
  1000. } // end onExit
  1001. //-------------------------------------------------------------------------------------------------
  1002. /** Dozer primary going home state */
  1003. //-------------------------------------------------------------------------------------------------
  1004. class DozerPrimaryGoingHomeState : public State
  1005. {
  1006. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerPrimaryGoingHomeState, "DozerPrimaryGoingHomeState")
  1007. protected:
  1008. // snapshot interface STUBBED no member vars
  1009. virtual void crc( Xfer *xfer ){};
  1010. virtual void xfer( Xfer *xfer ){};
  1011. virtual void loadPostProcess(){};
  1012. public:
  1013. DozerPrimaryGoingHomeState( StateMachine *machine ) : State( machine, "DozerPrimaryGoingHomeState" ) { }
  1014. virtual StateReturnType update( void ) { return STATE_FAILURE; }
  1015. };
  1016. EMPTY_DTOR(DozerPrimaryGoingHomeState)
  1017. //-------------------------------------------------------------------------------------------------
  1018. //-------------------------------------------------------------------------------------------------
  1019. DozerPrimaryStateMachine::DozerPrimaryStateMachine( Object *owner ) : StateMachine( owner, "DozerPrimaryStateMachine" )
  1020. {
  1021. static const StateConditionInfo idleConditions[] =
  1022. {
  1023. StateConditionInfo(isBuildMostImportant, DOZER_PRIMARY_BUILD, NULL),
  1024. StateConditionInfo(isRepairMostImportant, DOZER_PRIMARY_REPAIR, NULL),
  1025. StateConditionInfo(isFortifyMostImportant, DOZER_PRIMARY_FORTIFY, NULL),
  1026. StateConditionInfo(NULL, NULL, NULL) // keep last
  1027. };
  1028. // order matters: first state is the default state.
  1029. defineState( DOZER_PRIMARY_IDLE, newInstance(DozerPrimaryIdleState)( this ), INVALID_STATE_ID, INVALID_STATE_ID, idleConditions );
  1030. defineState( DOZER_PRIMARY_BUILD, newInstance(DozerActionState)( this, DOZER_TASK_BUILD ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
  1031. defineState( DOZER_PRIMARY_REPAIR, newInstance(DozerActionState)( this, DOZER_TASK_REPAIR ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
  1032. defineState( DOZER_PRIMARY_FORTIFY, newInstance(DozerActionState)( this, DOZER_TASK_FORTIFY ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
  1033. defineState( DOZER_PRIMARY_GO_HOME, newInstance(DozerPrimaryGoingHomeState)( this ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
  1034. } // end DozerPrimaryStateMachine
  1035. //-------------------------------------------------------------------------------------------------
  1036. //-------------------------------------------------------------------------------------------------
  1037. DozerPrimaryStateMachine::~DozerPrimaryStateMachine( void )
  1038. {
  1039. } // end ~DozerPrimaryStateMachine
  1040. // ------------------------------------------------------------------------------------------------
  1041. /** CRC */
  1042. // ------------------------------------------------------------------------------------------------
  1043. void DozerPrimaryStateMachine::crc( Xfer *xfer )
  1044. {
  1045. StateMachine::crc(xfer);
  1046. } // end crc
  1047. // ------------------------------------------------------------------------------------------------
  1048. /** Xfer Method */
  1049. // ------------------------------------------------------------------------------------------------
  1050. void DozerPrimaryStateMachine::xfer( Xfer *xfer )
  1051. {
  1052. XferVersion cv = 1;
  1053. XferVersion v = cv;
  1054. xfer->xferVersion( &v, cv );
  1055. StateMachine::xfer(xfer);
  1056. } // end xfer
  1057. // ------------------------------------------------------------------------------------------------
  1058. /** Load post process */
  1059. // ------------------------------------------------------------------------------------------------
  1060. void DozerPrimaryStateMachine::loadPostProcess( void )
  1061. {
  1062. StateMachine::loadPostProcess();
  1063. } // end loadPostProcess
  1064. //-------------------------------------------------------------------------------------------------
  1065. //-------------------------------------------------------------------------------------------------
  1066. Bool DozerPrimaryStateMachine::isBuildMostImportant( State *thisState, void* userData )
  1067. {
  1068. Object *dozer = thisState->getMachineOwner();
  1069. AIUpdateInterface *ai = dozer->getAIUpdateInterface();
  1070. if( !ai )
  1071. {
  1072. return FALSE;
  1073. }
  1074. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  1075. if( !dozerAI )
  1076. {
  1077. return FALSE;
  1078. }
  1079. if( !ai->isIdle() )
  1080. return FALSE; // busy doing something else
  1081. // if the most important task is us then return true
  1082. DozerTask task = dozerAI->getMostRecentCommand();
  1083. return task == DOZER_TASK_BUILD;
  1084. } // end isBuildMostImportant
  1085. //-------------------------------------------------------------------------------------------------
  1086. //-------------------------------------------------------------------------------------------------
  1087. Bool DozerPrimaryStateMachine::isRepairMostImportant( State *thisState, void* userData )
  1088. {
  1089. Object *dozer = thisState->getMachineOwner();
  1090. AIUpdateInterface *ai = dozer->getAIUpdateInterface();
  1091. if( !ai )
  1092. {
  1093. return false;
  1094. }
  1095. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  1096. if( !dozerAI )
  1097. {
  1098. return false;
  1099. }
  1100. if( !ai->isIdle() )
  1101. return FALSE; // busy doing something else
  1102. // if the most important task is us then return true
  1103. DozerTask task = dozerAI->getMostRecentCommand();
  1104. return task == DOZER_TASK_REPAIR;
  1105. } // end isRepairMostImportant
  1106. //-------------------------------------------------------------------------------------------------
  1107. //-------------------------------------------------------------------------------------------------
  1108. Bool DozerPrimaryStateMachine::isFortifyMostImportant( State *thisState, void* userData )
  1109. {
  1110. Object *dozer = thisState->getMachineOwner();
  1111. AIUpdateInterface *ai = dozer->getAIUpdateInterface();
  1112. if( !ai )
  1113. {
  1114. return false;
  1115. }
  1116. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  1117. if( !dozerAI )
  1118. {
  1119. return false;
  1120. }
  1121. if( !ai->isIdle() )
  1122. return FALSE; // busy doing something else
  1123. // if the most important task is us then return true
  1124. DozerTask task = dozerAI->getMostRecentCommand();
  1125. return task == DOZER_TASK_FORTIFY;
  1126. } // end isFortifyMostImportat
  1127. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1128. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1129. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1130. // ------------------------------------------------------------------------------------------------
  1131. // ------------------------------------------------------------------------------------------------
  1132. DozerAIUpdateModuleData::DozerAIUpdateModuleData( void )
  1133. {
  1134. m_repairHealthPercentPerSecond = 0.0f;
  1135. m_boredTime = 0.0f;
  1136. m_boredRange = 0.0f;
  1137. } // end DozerAIUpdateModuleData
  1138. // ------------------------------------------------------------------------------------------------
  1139. void DozerAIUpdateModuleData::buildFieldParse( MultiIniFieldParse& p)
  1140. {
  1141. AIUpdateModuleData::buildFieldParse( p );
  1142. static const FieldParse dataFieldParse[] =
  1143. {
  1144. { "RepairHealthPercentPerSecond", INI::parsePercentToReal, NULL, offsetof( DozerAIUpdateModuleData, m_repairHealthPercentPerSecond ) },
  1145. { "BoredTime", INI::parseDurationReal, NULL, offsetof( DozerAIUpdateModuleData, m_boredTime ) },
  1146. { "BoredRange", INI::parseReal, NULL, offsetof( DozerAIUpdateModuleData, m_boredRange ) },
  1147. { 0, 0, 0, 0 }
  1148. };
  1149. p.add( dataFieldParse );
  1150. } // end buildFieldParse
  1151. //-------------------------------------------------------------------------------------------------
  1152. //-------------------------------------------------------------------------------------------------
  1153. DozerAIUpdate::DozerAIUpdate( Thing *thing, const ModuleData* moduleData ) :
  1154. AIUpdateInterface( thing, moduleData )
  1155. {
  1156. Int i, j;
  1157. for( i = 0; i < DOZER_NUM_TASKS; i++ )
  1158. {
  1159. m_task[ i ].m_targetObjectID = INVALID_ID;
  1160. m_task[ i ].m_taskOrderFrame = 0;
  1161. for( j = 0; j < DOZER_NUM_DOCK_POINTS; j++ )
  1162. {
  1163. m_dockPoint[ i ][ j ].valid = FALSE;
  1164. m_dockPoint[ i ][ j ].location.zero();
  1165. } // end for j
  1166. } // end for i
  1167. m_currentTask = DOZER_TASK_INVALID;
  1168. m_buildSubTask = DOZER_SELECT_BUILD_DOCK_LOCATION; // irrelavant, but I want non-garbage value
  1169. //
  1170. // initialize the dozer machine to NULL, we want to do this and create it during the update
  1171. // implementation because at this point we don't have the object all setup
  1172. //
  1173. m_dozerMachine = NULL;
  1174. createMachines();
  1175. } // end DozerAIUpdate
  1176. //-------------------------------------------------------------------------------------------------
  1177. //-------------------------------------------------------------------------------------------------
  1178. DozerAIUpdate::~DozerAIUpdate( void )
  1179. {
  1180. // delete our behavior state machine
  1181. if( m_dozerMachine )
  1182. m_dozerMachine->deleteInstance();
  1183. // no orders
  1184. for( Int i = 0; i < DOZER_NUM_TASKS; i++ )
  1185. {
  1186. m_task[ i ].m_targetObjectID = INVALID_ID;
  1187. m_task[ i ].m_taskOrderFrame = 0;
  1188. } // end for i
  1189. } // end ~DozerAIUpdate
  1190. // ------------------------------------------------------------------------------------------------
  1191. /** Create any sub machines we need to do all our behavior */
  1192. // ------------------------------------------------------------------------------------------------
  1193. void DozerAIUpdate::createMachines( void )
  1194. {
  1195. if( m_dozerMachine == NULL )
  1196. {
  1197. m_dozerMachine = newInstance(DozerPrimaryStateMachine)( getObject() );
  1198. m_dozerMachine->initDefaultState();
  1199. } // end if
  1200. } // end createMachines
  1201. // ------------------------------------------------------------------------------------------------
  1202. /** Create the bridge scaffolding if necessary for the bridge that is attached to this tower */
  1203. // ------------------------------------------------------------------------------------------------
  1204. void DozerAIUpdate::createBridgeScaffolding( Object *bridgeTower )
  1205. {
  1206. // sanity
  1207. if( bridgeTower == NULL )
  1208. return;
  1209. // get the bridge behavior interface from the bridge object that this tower is a part of
  1210. BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( bridgeTower );
  1211. if( btbi == NULL )
  1212. return;
  1213. Object *bridgeObject = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1214. if( bridgeObject == NULL )
  1215. return;
  1216. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridgeObject );
  1217. if( bbi == NULL )
  1218. return;
  1219. // tell the bridge to create scaffolding if necessary
  1220. bbi->createScaffolding();
  1221. } // end createBridgeScaffolding
  1222. // ------------------------------------------------------------------------------------------------
  1223. /** Remove the bridge scaffolding from the bridge object that is attached to this tower */
  1224. // ------------------------------------------------------------------------------------------------
  1225. void DozerAIUpdate::removeBridgeScaffolding( Object *bridgeTower )
  1226. {
  1227. // sanity
  1228. if( bridgeTower == NULL )
  1229. return;
  1230. // get the bridge behavior interface from the bridge object that this tower is a part of
  1231. BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( bridgeTower );
  1232. if( btbi == NULL )
  1233. return;
  1234. Object *bridgeObject = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1235. if( bridgeObject == NULL )
  1236. return;
  1237. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridgeObject );
  1238. if( bbi == NULL )
  1239. return;
  1240. // tell the bridge to end any scaffolding from repairing
  1241. bbi->removeScaffolding();
  1242. } // end removeBridgeScaffolding
  1243. //-------------------------------------------------------------------------------------------------
  1244. //-------------------------------------------------------------------------------------------------
  1245. UpdateSleepTime DozerAIUpdate::update( void )
  1246. {
  1247. //
  1248. // NOTE: Any changes to DozerAIUpdate::* you probably want to reflect and copy into
  1249. // WorkerAIUPdate:* as well ... sigh
  1250. //
  1251. //
  1252. // now that we're really executing we have all the necessary object modules in place to
  1253. // correctly create a state machine and set the default state
  1254. //
  1255. createMachines();
  1256. // set us as being to able to move with super precision off grid locations
  1257. /*
  1258. if( getCurLocomotor() ) {
  1259. getCurLocomotor()->setUltraAccurate( TRUE );
  1260. getCurLocomotor()->setAllowInvalidPosition(TRUE);
  1261. }*/
  1262. // extend the normal AI system
  1263. UpdateSleepTime result;
  1264. result = AIUpdateInterface::update();
  1265. // do nothing if we're dead
  1266. ///@todo shouldn't this be at a higher level?
  1267. if( getObject()->isEffectivelyDead() )
  1268. return UPDATE_SLEEP_NONE;
  1269. // get and validate our current task
  1270. DozerTask currentTask = getCurrentTask();
  1271. if( currentTask != DOZER_TASK_INVALID )
  1272. {
  1273. ObjectID taskTarget = getTaskTarget( currentTask );
  1274. Object *targetObject = TheGameLogic->findObjectByID( taskTarget );
  1275. Bool invalidTask = FALSE;
  1276. // validate the task and the target
  1277. if( currentTask == DOZER_TASK_REPAIR &&
  1278. TheActionManager->canRepairObject( getObject(), targetObject, getLastCommandSource() ) == FALSE )
  1279. invalidTask = TRUE;
  1280. // cancel the task if it's now invalid
  1281. if( invalidTask == TRUE )
  1282. cancelTask( currentTask );
  1283. } // end if
  1284. else
  1285. getObject()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
  1286. // run our own state machine
  1287. m_dozerMachine->updateStateMachine();
  1288. return UPDATE_SLEEP_NONE;
  1289. } // end update
  1290. //-------------------------------------------------------------------------------------------------
  1291. /** The entry point of a construct command to the Dozer */
  1292. //-------------------------------------------------------------------------------------------------
  1293. Object *DozerAIUpdate::construct( const ThingTemplate *what,
  1294. const Coord3D *pos,
  1295. Real angle,
  1296. Player *owningPlayer,
  1297. Bool isRebuild )
  1298. {
  1299. // !!! NOTE: If you modify this you must modify the worker too !!!
  1300. // !!! Graham: Please please please have inspiration for how to *not* duplicate this code
  1301. // create our machines if they don't yet exist
  1302. ///@todo make 'construct' a real AI command and you won't need a special case
  1303. m_isRebuild = isRebuild;
  1304. createMachines();
  1305. // sanity
  1306. if( what == NULL || pos == NULL || owningPlayer == NULL )
  1307. return NULL;
  1308. // sanity
  1309. DEBUG_ASSERTCRASH( getObject()->getControllingPlayer() == owningPlayer,
  1310. ("Dozer::Construct - The controlling player of the Dozer is not the owning player passed in\n") );
  1311. // if we're not rebuilding, we have a few checks to pass first for sanity
  1312. if( isRebuild == FALSE )
  1313. {
  1314. // AI has weaker restriction on building
  1315. Bool dozerIsAI = owningPlayer->getPlayerType() == PLAYER_COMPUTER;
  1316. if( dozerIsAI )
  1317. {
  1318. // Just build it. The ai will validate, or cheat. jba.
  1319. } // end if
  1320. else
  1321. {
  1322. // make sure the player is capable of building this
  1323. if( TheBuildAssistant->canMakeUnit( getObject(), what ) != CANMAKE_OK)
  1324. return NULL;
  1325. // validate the the position to build at is valid
  1326. if( TheBuildAssistant->isLocationLegalToBuild( pos, what, angle,
  1327. BuildAssistant::TERRAIN_RESTRICTIONS |
  1328. BuildAssistant::CLEAR_PATH |
  1329. BuildAssistant::NO_OBJECT_OVERLAP |
  1330. BuildAssistant::SHROUD_REVEALED,
  1331. getObject(), NULL ) != LBC_OK )
  1332. return NULL;
  1333. } // end else
  1334. } // end if
  1335. //
  1336. // what will our initial status bits be, it is important to do this early
  1337. // before the hooks add/subtract power from a player are executed
  1338. //
  1339. UnsignedInt statusBits = OBJECT_STATUS_UNDER_CONSTRUCTION;
  1340. if( isRebuild )
  1341. BitSet( statusBits, OBJECT_STATUS_RECONSTRUCTING );
  1342. // create an object at the destination location
  1343. Object *obj = TheThingFactory->newObject( what, owningPlayer->getDefaultTeam(),
  1344. (ObjectStatusBits)statusBits );
  1345. // even though we haven't actually built anything yet, this keeps things tidy
  1346. obj->setProducer( getObject() );
  1347. obj->setBuilder( getObject() );
  1348. // take the required money away from the player
  1349. if( isRebuild == FALSE )
  1350. {
  1351. Money *money = owningPlayer->getMoney();
  1352. money->withdraw( what->calcCostToBuild( owningPlayer ) );
  1353. } // end if
  1354. // initialize object
  1355. obj->setPosition( pos );
  1356. obj->setOrientation( angle );
  1357. // Flatten the terrain underneath the object, then adjust to the flattened height. jba.
  1358. TheTerrainLogic->flattenTerrain(obj);
  1359. Coord3D adjustedPos = *pos;
  1360. adjustedPos.z = TheTerrainLogic->getGroundHeight(pos->x, pos->y);
  1361. obj->setPosition(&adjustedPos);
  1362. // Note - very important that we add to map AFTER we flatten terrain. jba.
  1363. TheAI->pathfinder()->addObjectToPathfindMap( obj );
  1364. // "callback" event for structure created (note that it's not yet "complete")
  1365. owningPlayer->onStructureCreated( getObject(), obj );
  1366. // set a construction percent for the new object to zero and a status for under construction
  1367. obj->setConstructionPercent( 0.0 );
  1368. // newly constructed objects start at one hit point
  1369. BodyModuleInterface *body = obj->getBodyModule();
  1370. body->internalChangeHealth( -body->getHealth() + 1.0f );
  1371. // set the model action state to awaiting construction
  1372. obj->clearAndSetModelConditionFlags(
  1373. MAKE_MODELCONDITION_MASK2(MODELCONDITION_PARTIALLY_CONSTRUCTED, MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED),
  1374. MAKE_MODELCONDITION_MASK(MODELCONDITION_AWAITING_CONSTRUCTION)
  1375. );
  1376. // we have a construction pending
  1377. newTask( DOZER_TASK_BUILD, obj );
  1378. return obj;
  1379. } // end construct
  1380. // ------------------------------------------------------------------------------------------------
  1381. /** Given our current task and repair target, can we accept this as a new repair target */
  1382. // ------------------------------------------------------------------------------------------------
  1383. Bool DozerAIUpdate::canAcceptNewRepair( Object *obj )
  1384. {
  1385. // sanity
  1386. if( obj == NULL )
  1387. return FALSE;
  1388. // if we're not repairing right now, we don't have any accept restrictions
  1389. if( getCurrentTask() != DOZER_TASK_REPAIR )
  1390. return TRUE;
  1391. // get current repair target
  1392. Object *currentRepair = TheGameLogic->findObjectByID( m_task[ DOZER_TASK_REPAIR ].m_targetObjectID );
  1393. if( currentRepair )
  1394. {
  1395. // check for same object
  1396. if( currentRepair == obj )
  1397. return FALSE;
  1398. // check for repairing any tower on the same bridge
  1399. if( currentRepair->isKindOf( KINDOF_BRIDGE_TOWER ) &&
  1400. obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
  1401. {
  1402. BridgeTowerBehaviorInterface *currentTowerInterface = NULL;
  1403. BridgeTowerBehaviorInterface *newTowerInterface = NULL;
  1404. currentTowerInterface = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( currentRepair );
  1405. newTowerInterface = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
  1406. // sanity
  1407. if( currentTowerInterface == NULL || newTowerInterface == NULL )
  1408. {
  1409. DEBUG_CRASH(( "Unable to find bridge tower interface on object\n" ));
  1410. return FALSE;
  1411. } // end if
  1412. // if they are part of the same bridge, ignore this repair command
  1413. if( currentTowerInterface->getBridgeID() == newTowerInterface->getBridgeID() )
  1414. return FALSE;
  1415. } // end if
  1416. } // end if, currentRepair object exists
  1417. // all is well
  1418. return TRUE;
  1419. } // end canAcceptNewRepair
  1420. // ------------------------------------------------------------------------------------------------
  1421. /** Issue an order for the Dozer to go repair the target 'obj' */
  1422. // ------------------------------------------------------------------------------------------------
  1423. void DozerAIUpdate::privateRepair( Object *obj, CommandSourceType cmdSource )
  1424. {
  1425. Object *dozer = getObject();
  1426. // if we are already repairing this target do nothing
  1427. if( canAcceptNewRepair( obj ) == FALSE )
  1428. return;
  1429. // sanity, if we can't repair the object then get out of there
  1430. if( TheActionManager->canRepairObject( dozer, obj, cmdSource ) == FALSE )
  1431. return;
  1432. // if this object is already actively being repaired by another dozertype we won't also try to go repair it
  1433. ObjectID currentRepairer = obj->getSoleHealingBenefactor();
  1434. if( currentRepairer != INVALID_ID && currentRepairer != dozer->getID() )
  1435. return;
  1436. // Bridges have been made indestructible, so these checks were in vain. -- ML
  1437. // if this object is a bridge tower, we need to check the status of the bridge in order
  1438. // to check for a 'duplicate repair'
  1439. //
  1440. //if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
  1441. //{
  1442. // BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
  1443. // DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower behavior interface\n") );
  1444. //
  1445. // Object *bridge = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1446. // DEBUG_ASSERTCRASH( bridge, ("Unable to find bridge object\n") );
  1447. // if( BitTest( bridge->getStatusBits(), OBJECT_STATUS_UNDERGOING_REPAIR ) == TRUE )
  1448. // return;
  1449. //
  1450. //} // end if
  1451. // for bridges, set the status for the bridge object
  1452. //if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
  1453. //{
  1454. // BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
  1455. // DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower behavior interface\n") );
  1456. //
  1457. // Object *bridge = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1458. // DEBUG_ASSERTCRASH( bridge, ("Unable to find bridge object\n") );
  1459. // bridge->setStatus( OBJECT_STATUS_UNDERGOING_REPAIR );
  1460. //
  1461. //} // end if
  1462. // start the new task
  1463. newTask( DOZER_TASK_REPAIR, obj );
  1464. } // end repair
  1465. // ------------------------------------------------------------------------------------------------
  1466. /** Resume construction on a building */
  1467. // ------------------------------------------------------------------------------------------------
  1468. void DozerAIUpdate::privateResumeConstruction( Object *obj, CommandSourceType cmdSource )
  1469. {
  1470. // sanity
  1471. if( obj == NULL )
  1472. return;
  1473. // make sure we can resume construction on this
  1474. if( TheActionManager->canResumeConstructionOf( getObject(), obj, cmdSource ) == FALSE )
  1475. return;
  1476. // start the new task for construction
  1477. newTask( DOZER_TASK_BUILD, obj );
  1478. } // end privateResumeConstruction
  1479. //-------------------------------------------------------------------------------------------------
  1480. //-------------------------------------------------------------------------------------------------
  1481. /*static*/ Bool DozerAIUpdate::findGoodBuildOrRepairPosition(const Object* me, const Object* target, Coord3D& positionOut)
  1482. {
  1483. // The place we go to build or repair is the closest spot from us to them
  1484. Coord3D ourPosition = *me->getPosition();
  1485. Coord3D theirPosition = *target->getPosition();
  1486. Coord3D bestPosition = theirPosition;// This answer is the best, as it includes findPositionAround
  1487. Coord3D workingPosition = theirPosition;// But if findPositionAround fails, we need to say something.
  1488. Vector3 offset( ourPosition.x - theirPosition.x,
  1489. ourPosition.y - theirPosition.y,
  1490. ourPosition.z - theirPosition.z );
  1491. offset.Normalize();
  1492. // This scaler makes FindPositionAround bias towards our side
  1493. offset = offset * (target->getGeometryInfo().getMajorRadius() / 2);
  1494. workingPosition.x += offset.X;
  1495. workingPosition.y += offset.Y;
  1496. workingPosition.z += offset.Z;
  1497. // this is a little cheesy... the idea is that we can only choose a location that is pretty close
  1498. // in z to the desired one. this prevents us from choosing a space at the bottom of a cliff when
  1499. // the space we want is at the top of the cliff. ideally we should do a funky terrain-zone compare
  1500. // but that isn't well-exposed... (srj)
  1501. const Real MAX_Z_DELTA = 10.0f;
  1502. FindPositionOptions fpOptions;
  1503. fpOptions.minRadius = 0.0f;
  1504. fpOptions.maxRadius = 100.0f;
  1505. fpOptions.sourceToPathToDest = me;// This makes it find a place forWhom can get to.
  1506. if (!me->isUsingAirborneLocomotor())
  1507. fpOptions.maxZDelta = MAX_Z_DELTA;
  1508. if( me->isUsingAirborneLocomotor() )
  1509. fpOptions.ignoreObject = target;// Flyers can ignore stuff, so they can approach right over the target if they want.
  1510. Bool spotFound = ThePartitionManager->findPositionAround( &workingPosition, &fpOptions, &bestPosition );
  1511. positionOut = spotFound ? bestPosition : workingPosition;
  1512. return spotFound;
  1513. }
  1514. //-------------------------------------------------------------------------------------------------
  1515. //-------------------------------------------------------------------------------------------------
  1516. /*static*/ Object* DozerAIUpdate::findGoodBuildOrRepairPositionAndTarget(Object* me, Object* target, Coord3D& positionOut)
  1517. {
  1518. if (target->isKindOf(KINDOF_BRIDGE))
  1519. {
  1520. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject(target);
  1521. if (bbi)
  1522. {
  1523. AIUpdateInterface* ai = me->getAI();
  1524. // have to repair at a tower.
  1525. Real bestDistSqr = 1e10f;
  1526. Object* bestTower = NULL;
  1527. for (Int i = 0; i < BRIDGE_MAX_TOWERS; ++i)
  1528. {
  1529. Object* tower = TheGameLogic->findObjectByID(bbi->getTowerID((BridgeTowerType)i));
  1530. if( tower )
  1531. {
  1532. Coord3D tmp;
  1533. Bool found = findGoodBuildOrRepairPosition(me, tower, tmp);
  1534. // do isPathAvail against the result of this, NOT the tower pos,
  1535. // since towers are often in cliff cells.
  1536. if (found && ai->isPathAvailable(&tmp))
  1537. {
  1538. Real thisDistSqr = sqr(me->getPosition()->x - tmp.x) + sqr(me->getPosition()->y - tmp.y);
  1539. if (thisDistSqr < bestDistSqr)
  1540. {
  1541. positionOut = tmp;
  1542. bestDistSqr = thisDistSqr;
  1543. bestTower = tower;
  1544. }
  1545. }
  1546. }
  1547. }
  1548. if (bestTower)
  1549. return bestTower;
  1550. DEBUG_CRASH(("should not happen, no reachable tower found"));
  1551. return NULL;
  1552. }
  1553. }
  1554. findGoodBuildOrRepairPosition(me, target, positionOut);
  1555. return target;
  1556. }
  1557. //-------------------------------------------------------------------------------------------------
  1558. /** Issue and order to the dozer */
  1559. //-------------------------------------------------------------------------------------------------
  1560. void DozerAIUpdate::newTask( DozerTask task, Object *target )
  1561. {
  1562. // sanity
  1563. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  1564. // sanity
  1565. if( target == NULL )
  1566. return;
  1567. //
  1568. // special check for the build task, we should never be given more than one of them ...
  1569. // for the other tasks we just forget what we were doing and the new target takes
  1570. // precedence for the task
  1571. //
  1572. if( task == DOZER_TASK_BUILD || task == DOZER_TASK_REPAIR )
  1573. {
  1574. // handle getting two tasks
  1575. if( isTaskPending( task ) == TRUE )
  1576. cancelTask( task );
  1577. // get our object
  1578. Object *me = getObject();
  1579. Coord3D position;
  1580. target = findGoodBuildOrRepairPositionAndTarget(me, target, position);
  1581. if (target == NULL)
  1582. return; // could happen for some bridges
  1583. //
  1584. // for building, we say that even "thinking" about building or rebuilding an object
  1585. // sets us as the current builder of that object. this allows any dozers that are
  1586. // ordered later to resume construction on something to see that somebody is already taking
  1587. // care of it and then they won't be even try to resume a build since we don't allow
  1588. // multiple dozers/workers to double up on construction efforts
  1589. //
  1590. if( task == DOZER_TASK_BUILD )
  1591. target->setBuilder( me );
  1592. m_dockPoint[ task ][ DOZER_DOCK_POINT_START ].valid = TRUE;
  1593. m_dockPoint[ task ][ DOZER_DOCK_POINT_START ].location = position;
  1594. m_dockPoint[ task ][ DOZER_DOCK_POINT_ACTION ].valid = TRUE;
  1595. m_dockPoint[ task ][ DOZER_DOCK_POINT_ACTION ].location = position;
  1596. Coord3D offset;
  1597. offset.set(position.x-target->getPosition()->x, position.y-target->getPosition()->y, 0);
  1598. offset.normalize();
  1599. offset.scale(5*PATHFIND_CELL_SIZE_F);
  1600. position.add(&offset); // move away from the dock point at the end of build.
  1601. m_dockPoint[ task ][ DOZER_DOCK_POINT_END ].valid = TRUE;
  1602. m_dockPoint[ task ][ DOZER_DOCK_POINT_END ].location = position;
  1603. } // end if
  1604. // set the new task target and the frame in which we got this order
  1605. m_task[ task ].m_targetObjectID = target->getID();
  1606. m_task[ task ].m_taskOrderFrame = TheGameLogic->getFrame();
  1607. // reset the dozer behavior so that it can re-evluate which task to continue working on
  1608. m_dozerMachine->resetToDefaultState();
  1609. } // end newTask
  1610. //-------------------------------------------------------------------------------------------------
  1611. /** Cancel a task and reset the dozer behavior state machine so that it can
  1612. * re-evaluate what it wants to do if it was working on the task being
  1613. * cancelled */
  1614. //-------------------------------------------------------------------------------------------------
  1615. void DozerAIUpdate::cancelTask( DozerTask task )
  1616. {
  1617. // clear the order
  1618. internalCancelTask( task );
  1619. // reset the machine to we can re-evaluate what we want to do
  1620. m_dozerMachine->resetToDefaultState();
  1621. } // end cancelTask
  1622. //-------------------------------------------------------------------------------------------------
  1623. /** Is there a given task waiting to be done */
  1624. //-------------------------------------------------------------------------------------------------
  1625. Bool DozerAIUpdate::isTaskPending( DozerTask task )
  1626. {
  1627. // sanity
  1628. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  1629. return m_task[ task ].m_targetObjectID != 0 ? TRUE : FALSE;
  1630. } // end isTaskPending
  1631. //-------------------------------------------------------------------------------------------------
  1632. /** Is there any task pending */
  1633. //-------------------------------------------------------------------------------------------------
  1634. Bool DozerAIUpdate::isAnyTaskPending( void )
  1635. {
  1636. for( Int i = 0; i < DOZER_NUM_TASKS; i++ )
  1637. if( isTaskPending( (DozerTask)i ) )
  1638. return TRUE;
  1639. return FALSE;
  1640. } // end isAnyTaskPending
  1641. //-------------------------------------------------------------------------------------------------
  1642. /** Get the target object of a given task */
  1643. //-------------------------------------------------------------------------------------------------
  1644. ObjectID DozerAIUpdate::getTaskTarget( DozerTask task )
  1645. {
  1646. // sanity
  1647. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  1648. return m_task[ task ].m_targetObjectID;
  1649. } // end getTaskTarget
  1650. //-------------------------------------------------------------------------------------------------
  1651. /** Set a task as successfully completed */
  1652. //-------------------------------------------------------------------------------------------------
  1653. void DozerAIUpdate::internalTaskComplete( DozerTask task )
  1654. {
  1655. // sanity
  1656. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  1657. // call the single method that gets called for completing and canceling tasks
  1658. internalTaskCompleteOrCancelled( task );
  1659. // remove the info for this task
  1660. m_task[ task ].m_targetObjectID = INVALID_ID;
  1661. m_task[ task ].m_taskOrderFrame = 0;
  1662. // remove dock point info for this task
  1663. for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ )
  1664. m_dockPoint[ task ][ i ].valid = FALSE;
  1665. } // end internalTaskComplete
  1666. //-------------------------------------------------------------------------------------------------
  1667. /** Clear a task from the Dozer for consideration, we can use this when a goal object becomes
  1668. * invalid/destroyed etc. */
  1669. //-------------------------------------------------------------------------------------------------
  1670. void DozerAIUpdate::internalCancelTask( DozerTask task )
  1671. {
  1672. // sanity
  1673. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  1674. // call the single method that gets called for completing and canceling tasks
  1675. internalTaskCompleteOrCancelled( task );
  1676. // remove the info for this task
  1677. m_task[ task ].m_targetObjectID = INVALID_ID;
  1678. m_task[ task ].m_taskOrderFrame = 0;
  1679. // remove dock point info for this task
  1680. for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ )
  1681. m_dockPoint[ task ][ i ].valid = FALSE;
  1682. // stop the dozer from moving
  1683. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  1684. ai->aiIdle( CMD_FROM_AI );
  1685. } // end internalCancelTask
  1686. // ------------------------------------------------------------------------------------------------
  1687. /** This method is called whenever a task is completed *OR* cancelled */
  1688. // ------------------------------------------------------------------------------------------------
  1689. void DozerAIUpdate::internalTaskCompleteOrCancelled( DozerTask task )
  1690. {
  1691. switch( task )
  1692. {
  1693. // --------------------------------------------------------------------------------------------
  1694. case DOZER_TASK_INVALID:
  1695. {
  1696. break; // do nothing, this is really no task
  1697. } // end invalid
  1698. // --------------------------------------------------------------------------------------------
  1699. case DOZER_TASK_BUILD:
  1700. {
  1701. // the builder is no longer actively building something
  1702. getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  1703. // And the thing we were working on is no longer being actively built
  1704. ///@todo This would be correct except that we don't have idle crane animations and it is December.
  1705. // Object* goalObject = TheGameLogic->findObjectByID(m_task[task].m_targetObjectID);
  1706. // if (goalObject != NULL)
  1707. // {
  1708. // goalObject->clearModelConditionState(MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED);
  1709. // }
  1710. break;
  1711. } // end build
  1712. // --------------------------------------------------------------------------------------------
  1713. case DOZER_TASK_REPAIR:
  1714. {
  1715. // the builder is no longer actively repairing something
  1716. getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  1717. // Bridges have been made indestructible, so the code below was meaningless. -- ML
  1718. // Object *obj = NULL;
  1719. // get object to reapir (if present)
  1720. //obj = TheGameLogic->findObjectByID( m_task[ task ].m_targetObjectID );
  1721. //
  1722. //if( obj )
  1723. //{
  1724. //
  1725. //
  1726. // when we're done repairing bridges, tell the scaffolding to go away and also remove
  1727. // the undergoing repair status from the bridge object itself
  1728. //
  1729. // if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
  1730. // {
  1731. // // remove the bridge scaffold
  1732. // removeBridgeScaffolding( obj );
  1733. //
  1734. // // clear the repair bit from the bridge
  1735. // BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
  1736. // DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower behavior interface\n") );
  1737. // Object *bridge = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1738. // DEBUG_ASSERTCRASH( bridge, ("Unable to find bridge object\n") );
  1739. // bridge->clearStatus( OBJECT_STATUS_UNDERGOING_REPAIR );
  1740. //
  1741. // } // end if
  1742. //
  1743. //} // end if
  1744. break;
  1745. } // end repair
  1746. // --------------------------------------------------------------------------------------------
  1747. case DOZER_TASK_FORTIFY:
  1748. {
  1749. break;
  1750. } // end fortify
  1751. // --------------------------------------------------------------------------------------------
  1752. default:
  1753. {
  1754. DEBUG_CRASH(( "internalTaskCompleteOrCancelled: Unknown Dozer task '%d'\n", task ));
  1755. break;
  1756. } // end default
  1757. } // end switch( task )
  1758. } // end internalTaskCompleteOrCancelled
  1759. //-------------------------------------------------------------------------------------------------
  1760. /** If we were building something, kill the active-construction flag on it */
  1761. //-------------------------------------------------------------------------------------------------
  1762. void DozerAIUpdate::onDelete( void )
  1763. {
  1764. Int i;
  1765. // cancel any of the tasks we had queued up
  1766. for( i = DOZER_TASK_FIRST; i < DOZER_NUM_TASKS; ++i )
  1767. {
  1768. if( isTaskPending( (DozerTask)i ) )
  1769. cancelTask( (DozerTask)i );
  1770. } // end for i
  1771. for( i = 0; i < DOZER_NUM_TASKS; i++ )
  1772. {
  1773. Object* goalObject = TheGameLogic->findObjectByID(m_task[i].m_targetObjectID);
  1774. if (goalObject != NULL)
  1775. {
  1776. goalObject->clearModelConditionState(MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED);
  1777. }
  1778. }
  1779. }
  1780. //-------------------------------------------------------------------------------------------------
  1781. /** Get the most recently issued task */
  1782. //-------------------------------------------------------------------------------------------------
  1783. DozerTask DozerAIUpdate::getMostRecentCommand( void )
  1784. {
  1785. Int i;
  1786. DozerTask mostRecentTask = DOZER_TASK_INVALID;
  1787. UnsignedInt mostRecentFrame = 0;
  1788. for( i = 0; i < DOZER_NUM_TASKS; i++ )
  1789. {
  1790. if( isTaskPending( (DozerTask)i ) )
  1791. {
  1792. if( m_task[ i ].m_taskOrderFrame > mostRecentFrame )
  1793. {
  1794. mostRecentTask = (DozerTask)i;
  1795. mostRecentFrame = m_task[ i ].m_taskOrderFrame;
  1796. } // end if
  1797. } // end if
  1798. } // end for i
  1799. return mostRecentTask;
  1800. } // end getMostRecentCommand
  1801. // ------------------------------------------------------------------------------------------------
  1802. // ------------------------------------------------------------------------------------------------
  1803. const Coord3D* DozerAIUpdate::getDockPoint( DozerTask task, DozerDockPoint point )
  1804. {
  1805. // sanity
  1806. if( task < 0 || task >= DOZER_NUM_TASKS )
  1807. return NULL;
  1808. // sanity
  1809. if( point < 0 || point >= DOZER_NUM_DOCK_POINTS )
  1810. return NULL;
  1811. // if the point has been set (is valid) then return it
  1812. if( m_dockPoint[ task ][ point ].valid )
  1813. return &m_dockPoint[ task ][ point ].location;
  1814. // no valid point has been set for this dock point on this task
  1815. return NULL;
  1816. } // end getDockPoint
  1817. // ------------------------------------------------------------------------------------------------
  1818. Real DozerAIUpdate::getRepairHealthPerSecond( void ) const
  1819. {
  1820. return getDozerAIUpdateModuleData()->m_repairHealthPercentPerSecond;
  1821. }
  1822. // ------------------------------------------------------------------------------------------------
  1823. Real DozerAIUpdate::getBoredTime( void ) const
  1824. {
  1825. return getDozerAIUpdateModuleData()->m_boredTime;
  1826. }
  1827. // ------------------------------------------------------------------------------------------------
  1828. Real DozerAIUpdate::getBoredRange( void ) const
  1829. {
  1830. if (getObject()->getControllingPlayer() &&
  1831. getObject()->getControllingPlayer()->getPlayerType() == PLAYER_COMPUTER) {
  1832. return TheAI->getAiData()->m_aiDozerBoredRadiusModifier*getDozerAIUpdateModuleData()->m_boredRange;
  1833. }
  1834. return getDozerAIUpdateModuleData()->m_boredRange;
  1835. }
  1836. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1837. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1838. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1839. //-------------------------------------------------------------------------------------------------
  1840. void DozerAIUpdate::aiDoCommand(const AICommandParms* parms)
  1841. {
  1842. //
  1843. // anytime we get a command, just remove any model condition that has us actively building
  1844. // if we need to show that, that bit will be set anyway again during the build process
  1845. //
  1846. getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  1847. if (!isAllowedToRespondToAiCommands(parms))
  1848. return;
  1849. // if we haven't made the dozer machine yet, do so now
  1850. createMachines();
  1851. switch( parms->m_cmd )
  1852. {
  1853. case AICMD_MOVE_AWAY_FROM_UNIT:
  1854. {
  1855. // We only want to do this if we aren't busy doing dozer things. jba.
  1856. // if we have no task right now, go idle so we can immediately respond to this
  1857. if( getCurrentTask() != DOZER_TASK_INVALID ) {
  1858. return; // just ignore it. jba.
  1859. }
  1860. }
  1861. // --------------------------------------------------------------------------------------------
  1862. case AICMD_REPAIR:
  1863. {
  1864. // if we have no task right now, go idle so we can immediately respond to this
  1865. if( getCurrentTask() == DOZER_TASK_INVALID )
  1866. aiIdle( CMD_FROM_AI );
  1867. // do the repair
  1868. privateRepair(parms->m_obj, parms->m_cmdSource);
  1869. break;
  1870. } // end repair
  1871. // --------------------------------------------------------------------------------------------
  1872. case AICMD_RESUME_CONSTRUCTION:
  1873. {
  1874. // if we have no task right now, go idle so we can immediately respond to this
  1875. if( getCurrentTask() == DOZER_TASK_INVALID )
  1876. aiIdle( CMD_FROM_AI );
  1877. // do the command
  1878. privateResumeConstruction( parms->m_obj, parms->m_cmdSource );
  1879. break;
  1880. } // end resume construction
  1881. // --------------------------------------------------------------------------------------------
  1882. default:
  1883. {
  1884. // if this is from the player, cancel our current task
  1885. if( parms->m_cmdSource == CMD_FROM_PLAYER && getCurrentTask() != DOZER_TASK_INVALID )
  1886. cancelTask( getCurrentTask() );
  1887. // issue the command
  1888. AIUpdateInterface::aiDoCommand(parms);
  1889. // when a player issues commands, this will cause the dozer to re-evaluate what it's doing
  1890. if( parms->m_cmdSource == CMD_FROM_PLAYER )
  1891. m_dozerMachine->resetToDefaultState();
  1892. break;
  1893. } // end default
  1894. } // end switch
  1895. } // end aiDoCommand
  1896. //------------------------------------------------------------------------------------------------
  1897. void DozerAIUpdate::startBuildingSound( const AudioEventRTS *sound, ObjectID constructionSiteID )
  1898. {
  1899. m_buildingSound = *sound;
  1900. m_buildingSound.setObjectID( constructionSiteID );
  1901. m_buildingSound.setPlayingHandle( TheAudio->addAudioEvent( &m_buildingSound ) );
  1902. }
  1903. //------------------------------------------------------------------------------------------------
  1904. void DozerAIUpdate::finishBuildingSound()
  1905. {
  1906. TheAudio->removeAudioEvent( m_buildingSound.getPlayingHandle() );
  1907. }
  1908. // ------------------------------------------------------------------------------------------------
  1909. /** CRC */
  1910. // ------------------------------------------------------------------------------------------------
  1911. void DozerAIUpdate::crc( Xfer *xfer )
  1912. {
  1913. // extend base class
  1914. AIUpdateInterface::crc(xfer);
  1915. } // end crc
  1916. // ------------------------------------------------------------------------------------------------
  1917. /** Xfer method
  1918. * Version Info:
  1919. * 1: Initial version */
  1920. // ------------------------------------------------------------------------------------------------
  1921. void DozerAIUpdate::xfer( Xfer *xfer )
  1922. {
  1923. // version
  1924. XferVersion currentVersion = 1;
  1925. XferVersion version = currentVersion;
  1926. xfer->xferVersion( &version, currentVersion );
  1927. // extend base class
  1928. AIUpdateInterface::xfer(xfer);
  1929. Int numTasks = DOZER_NUM_TASKS;
  1930. xfer->xferInt(&numTasks);
  1931. if (numTasks != DOZER_NUM_TASKS) {
  1932. DEBUG_CRASH(("DOZER_NUM_TASKS changed unexpectedly."));
  1933. throw SC_INVALID_DATA;
  1934. }
  1935. Int i, j;
  1936. for (i=0; i<DOZER_NUM_TASKS; i++) {
  1937. xfer->xferObjectID(&m_task[i].m_targetObjectID);
  1938. xfer->xferUnsignedInt(&m_task[i].m_taskOrderFrame);
  1939. }
  1940. xfer->xferSnapshot(m_dozerMachine);
  1941. xfer->xferUser(&m_currentTask, sizeof(m_currentTask));
  1942. Int dockPoints = DOZER_NUM_DOCK_POINTS;
  1943. xfer->xferInt(&dockPoints);
  1944. if (dockPoints!=DOZER_NUM_DOCK_POINTS) {
  1945. DEBUG_CRASH(("DOZER_NUM_DOCK_POINTS changed unexpectedly."));
  1946. throw SC_INVALID_DATA;
  1947. }
  1948. for (i=0; i<DOZER_NUM_TASKS; i++) {
  1949. for (j=0; j<DOZER_NUM_DOCK_POINTS; j++) {
  1950. xfer->xferBool(&m_dockPoint[i][j].valid);
  1951. xfer->xferCoord3D(&m_dockPoint[i][j].location);
  1952. }
  1953. }
  1954. xfer->xferUser(&m_buildSubTask, sizeof(m_buildSubTask));
  1955. } // end xfer
  1956. // ------------------------------------------------------------------------------------------------
  1957. /** Load post process */
  1958. // ------------------------------------------------------------------------------------------------
  1959. void DozerAIUpdate::loadPostProcess( void )
  1960. {
  1961. // extend base class
  1962. AIUpdateInterface::loadPostProcess();
  1963. } // end loadPostProcess