DozerAIUpdate.cpp 87 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: 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( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNDER_CONSTRUCTION ) );
  463. goalObject->clearStatus( MAKE_OBJECT_STATUS_MASK( 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. player->getAcademyStats()->recordProduction( goalObject, dozer );
  487. //
  488. // Now onCreates were called at construction start. Now at finish is when we
  489. // want the Game side of OnCreate
  490. //
  491. for (BehaviorModule** m = goalObject->getBehaviorModules(); *m; ++m)
  492. {
  493. CreateModuleInterface* create = (*m)->getCreate();
  494. if (!create)
  495. continue;
  496. create->onBuildComplete();
  497. }
  498. } // end if
  499. // Creation is another valid and essential time to call this. This building now Looks.
  500. goalObject->handlePartitionCellMaintenance();
  501. // UnderConstruction just cleared, so update our upgrades
  502. goalObject->updateUpgradeModules();
  503. // this object how has influence in the controlling players' tech tree
  504. /// @todo need to write this
  505. // do some UI stuff for the constrolling player
  506. if( dozer->isLocallyControlled() )
  507. {
  508. // message the the building player
  509. UnicodeString format = TheGameText->fetch( "DOZER:ConstructionComplete" );
  510. UnicodeString objectName = goalObject->getTemplate()->getDisplayName();
  511. if( objectName.isEmpty() )
  512. {
  513. UnicodeString format = TheGameText->fetch( "INI:MissingDisplayName" );
  514. objectName.format( format, goalObject->getTemplate()->getName().str() );
  515. } // end if
  516. UnicodeString msg;
  517. msg.format( format.str(), objectName.str() );
  518. TheInGameUI->message( msg );
  519. AudioEventRTS audio = *dozer->getTemplate()->getVoiceTaskComplete();
  520. audio.setObjectID(dozer->getID());
  521. TheAudio->addAudioEvent(&audio);
  522. /// make radar neat-o attention grabber event at build location
  523. TheRadar->createEvent( goalObject->getPosition(), RADAR_EVENT_CONSTRUCTION );
  524. } // end if
  525. // this will allow us to exit the state machine with success
  526. complete = TRUE;
  527. // move off to the end dock position if present
  528. const Coord3D *endPos = dozerAI->getDockPoint( m_task, DOZER_DOCK_POINT_END );
  529. Coord3D pos = *dozer->getPosition();
  530. if( endPos ) {
  531. pos = *endPos;
  532. }
  533. // Our goal may be inside a building, if we are packing them in tight, so try adjusting. jba.
  534. TheAI->pathfinder()->adjustToPossibleDestination(dozer, ai->getLocomotorSet(), &pos);
  535. ai->aiMoveToPosition( &pos, CMD_FROM_AI );
  536. } // end if
  537. } // end if
  538. break;
  539. } // end build
  540. //---------------------------------------------------------------------------------------------
  541. case DOZER_TASK_REPAIR:
  542. {
  543. BodyModuleInterface *body = goalObject->getBodyModule();
  544. // check for fully "repaired"
  545. if( body->getHealth() == body->getMaxHealth() )
  546. {
  547. // issue repair complete message, we might want to remove this later cause it could be annoying
  548. TheInGameUI->message( "DOZER:RepairComplete" );
  549. // we're now complete
  550. complete = TRUE;
  551. } // end if
  552. else
  553. {
  554. Bool canHeal = TRUE;
  555. // if we are repairing a bridge, create scaffolding over the bridge if we need to
  556. if( goalObject->isKindOf( KINDOF_BRIDGE_TOWER ) )
  557. dozerAI->createBridgeScaffolding( goalObject );
  558. // the builder is now actively repairing something, we'll borrow the constructing animation
  559. dozer->setModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  560. //
  561. // when repairing bridges, we cannot actually do any repairing until the
  562. // scaffolding is extended and all the way complete
  563. //
  564. if( goalObject->isKindOf( KINDOF_BRIDGE_TOWER ) )
  565. {
  566. BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( goalObject );
  567. DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower interface\n") );
  568. Object *bridgeObject = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  569. DEBUG_ASSERTCRASH( bridgeObject, ("Unable to find bridge center object\n") );
  570. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridgeObject );
  571. DEBUG_ASSERTCRASH( bbi, ("Unable to find bridge interface from tower goal object during repair\n") );
  572. if( bbi->isScaffoldInMotion() == TRUE )
  573. canHeal = FALSE;
  574. } // end if
  575. // do healing
  576. if( canHeal )
  577. {
  578. // figure out how much health we will restore this frame
  579. Real health = body->getMaxHealth() * dozerAI->getRepairHealthPerSecond() /
  580. LOGICFRAMES_PER_SECOND;
  581. // try to give it a little bit-o-health
  582. if ( ! goalObject->attemptHealingFromSoleBenefactor(health, dozer, 2) )//this frame and the next
  583. {
  584. // goalObject->setStatus( OBJECT_STATUS_UNDERGOING_REPAIR );
  585. // This bit used to be set way back in DozerAIUpdate::privateRepair(), but it has been outmoded
  586. // so that several dozers/workers can target the same sick building for repair, but the first one to begin
  587. // this is done by attemptHealingFromSoleBenefactor() which lets the beneficiary of the healing
  588. // remember who has been healing it, and will return false to everybody else
  589. //or if the goalObject is already receiving healing, I must stop, since my healing is getting rejected here
  590. dozerAI->internalTaskComplete( m_task );
  591. getMachine()->setGoalObject( NULL );
  592. return STATE_FAILURE;
  593. }
  594. } // end if
  595. } // end else
  596. // play a repairing sound
  597. break;
  598. } // end repair
  599. //---------------------------------------------------------------------------------------------
  600. case DOZER_TASK_FORTIFY:
  601. {
  602. /// @todo write me
  603. break;
  604. } // end fortify
  605. //---------------------------------------------------------------------------------------------
  606. default:
  607. {
  608. DEBUG_CRASH(( "Unknown task for the dozer action do action state\n" ));
  609. return STATE_FAILURE;
  610. } // end default
  611. } // end switch
  612. // if we're complete with the task we exit success
  613. if( complete == TRUE )
  614. {
  615. // this task is now complete, remove it from dozer consideration
  616. dozerAI->internalTaskComplete( m_task );
  617. // to be clean get rid of the goal object we set
  618. getMachine()->setGoalObject( NULL );
  619. getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
  620. // we're done
  621. return STATE_SUCCESS;
  622. } // end if
  623. // just continue sitting here for a while
  624. getMachineOwner()->clearWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);// no mine clearing fun while I'm on the job
  625. return STATE_CONTINUE;
  626. } // end update
  627. //-------------------------------------------------------------------------------------------------
  628. /** The Dozer action state machine */
  629. //-------------------------------------------------------------------------------------------------
  630. class DozerActionStateMachine : public StateMachine
  631. {
  632. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( DozerActionStateMachine, "DozerActionStateMachine" );
  633. public:
  634. DozerActionStateMachine( Object *owner, DozerTask task );
  635. // virtual destructor prototypes provided by memory pool object
  636. protected:
  637. // snapshot interface
  638. virtual void crc( Xfer *xfer );
  639. virtual void xfer( Xfer *xfer );
  640. virtual void loadPostProcess();
  641. protected:
  642. DozerTask m_task; ///< the task of this action state machine
  643. };
  644. EMPTY_DTOR(DozerActionStateMachine)
  645. //-------------------------------------------------------------------------------------------------
  646. //-------------------------------------------------------------------------------------------------
  647. DozerActionStateMachine::DozerActionStateMachine( Object *owner, DozerTask task ) :
  648. StateMachine( owner, "DozerActionStateMachine" )
  649. {
  650. // initialize our task
  651. m_task = task;
  652. // order matters: first state is the default state.
  653. defineState( DOZER_ACTION_PICK_ACTION_POS, newInstance(DozerActionPickActionPosState)( this, task ), DOZER_ACTION_MOVE_TO_ACTION_POS, EXIT_MACHINE_WITH_FAILURE );
  654. defineState( DOZER_ACTION_MOVE_TO_ACTION_POS, newInstance(DozerActionMoveToActionPosState)( this, task ), DOZER_ACTION_DO_ACTION, DOZER_ACTION_PICK_ACTION_POS );
  655. defineState( DOZER_ACTION_DO_ACTION, newInstance(DozerActionDoActionState)( this, task ), EXIT_MACHINE_WITH_SUCCESS, EXIT_MACHINE_WITH_FAILURE );
  656. }
  657. // ------------------------------------------------------------------------------------------------
  658. /** CRC */
  659. // ------------------------------------------------------------------------------------------------
  660. void DozerActionStateMachine::crc( Xfer *xfer )
  661. {
  662. } // end crc
  663. // ------------------------------------------------------------------------------------------------
  664. /** Xfer Method */
  665. // ------------------------------------------------------------------------------------------------
  666. void DozerActionStateMachine::xfer( Xfer *xfer )
  667. {
  668. // version
  669. XferVersion currentVersion = 1;
  670. XferVersion version = currentVersion;
  671. xfer->xferVersion( &version, currentVersion );
  672. xfer->xferUser(&m_task, sizeof(m_task));
  673. } // end xfer
  674. // ------------------------------------------------------------------------------------------------
  675. /** Load post process */
  676. // ------------------------------------------------------------------------------------------------
  677. void DozerActionStateMachine::loadPostProcess( void )
  678. {
  679. } // end loadPostProcess
  680. ///////////////////////////////////////////////////////////////////////////////////////////////////
  681. ///////////////////////////////////////////////////////////////////////////////////////////////////
  682. ///////////////////////////////////////////////////////////////////////////////////////////////////
  683. //-------------------------------------------------------------------------------------------------
  684. //-------------------------------------------------------------------------------------------------
  685. static Object *findObjectToRepair( Object *dozer )
  686. {
  687. // sanity
  688. if( dozer == NULL )
  689. return NULL;
  690. if( !dozer->getAIUpdateInterface() )
  691. {
  692. return NULL;
  693. }
  694. const DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
  695. PartitionFilterSamePlayer filter1( dozer->getControllingPlayer() );
  696. PartitionFilterAcceptByKindOf filter2( MAKE_KINDOF_MASK( KINDOF_STRUCTURE ),
  697. KINDOFMASK_NONE );
  698. PartitionFilterSameMapStatus filterMapStatus(dozer);
  699. PartitionFilter *filters[] = { &filter1, &filter2, &filterMapStatus, NULL };
  700. ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( dozer->getPosition(),
  701. dozerAI->getBoredRange(),
  702. FROM_CENTER_2D,
  703. filters );
  704. MemoryPoolObjectHolder hold( iter );
  705. Object *obj;
  706. Object *closestRepairTarget = NULL;
  707. Real closestRepairTargetDistSqr = 0.0f;
  708. for( obj = iter->first(); obj; obj = iter->next() )
  709. {
  710. // ignore objects we cant repair
  711. if( TheActionManager->canRepairObject( dozer, obj, CMD_FROM_AI ) == FALSE )
  712. continue;
  713. // target the closest valid repair target
  714. if( closestRepairTarget == NULL )
  715. {
  716. closestRepairTarget = obj;
  717. closestRepairTargetDistSqr = ThePartitionManager->getDistanceSquared( dozer, obj, FROM_CENTER_2D );
  718. } // end if
  719. else
  720. {
  721. // only use this command center if it's closer than the last one we found
  722. Real distSqr = ThePartitionManager->getDistanceSquared( dozer, obj, FROM_CENTER_2D );
  723. if( distSqr < closestRepairTargetDistSqr )
  724. {
  725. closestRepairTarget = obj;
  726. closestRepairTargetDistSqr = distSqr;
  727. } // end if
  728. } // end else
  729. } // end for obj
  730. return closestRepairTarget;
  731. } // end findObjectToRepair
  732. //-------------------------------------------------------------------------------------------------
  733. //-------------------------------------------------------------------------------------------------
  734. static Object *findMine( Object *dozer )
  735. {
  736. // sanity
  737. if( dozer == NULL )
  738. return NULL;
  739. if( !dozer->getAIUpdateInterface() )
  740. {
  741. return NULL;
  742. }
  743. const DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
  744. // srj sez: only clear enemy or neutal mines. clearing allied mines (ie, OURS) is really dumb.
  745. // (and no, PossibleToAttack won't necessarily filter these out.)
  746. PartitionFilterRelationship filterTeam(dozer, PartitionFilterRelationship::ALLOW_ENEMIES | PartitionFilterRelationship::ALLOW_NEUTRAL);
  747. PartitionFilterPossibleToAttack filterAttack(ATTACK_NEW_TARGET, dozer, CMD_FROM_DOZER);
  748. PartitionFilterSameMapStatus filterMapStatus(dozer);
  749. PartitionFilter *filters[] = { &filterTeam, &filterAttack, &filterMapStatus, NULL };
  750. Object* mine = ThePartitionManager->getClosestObject(dozer, dozerAI->getBoredRange(), FROM_CENTER_2D, filters);
  751. return mine;
  752. } // end findMine
  753. //-------------------------------------------------------------------------------------------------
  754. /** Available primary Dozer states */
  755. //-------------------------------------------------------------------------------------------------
  756. enum
  757. {
  758. DOZER_PRIMARY_IDLE, ///< dozer is idle
  759. DOZER_PRIMARY_BUILD, ///< dozer is building a structure for the player
  760. DOZER_PRIMARY_REPAIR, ///< dozer is repairing something
  761. DOZER_PRIMARY_FORTIFY, ///< dozer is fortifying a civilian building, making it stronger
  762. DOZER_PRIMARY_GO_HOME, ///< dozer has nothing else to do so it will return a command center
  763. };
  764. //-------------------------------------------------------------------------------------------------
  765. /** Dozer primary idle state */
  766. //-------------------------------------------------------------------------------------------------
  767. class DozerPrimaryIdleState : public State
  768. {
  769. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerPrimaryIdleState, "DozerPrimaryIdleState")
  770. public:
  771. DozerPrimaryIdleState( StateMachine *machine ) : State( machine, "DozerPrimaryIdleState" )
  772. {
  773. m_idleTooLongTimestamp = 0;
  774. m_idlePlayerNumber = 0;
  775. m_isMarkedAsIdle = FALSE;
  776. }
  777. virtual StateReturnType update( void );
  778. virtual StateReturnType onEnter( void );
  779. virtual void onExit( StateExitType status );
  780. protected:
  781. // snapshot interface
  782. virtual void crc( Xfer *xfer );
  783. virtual void xfer( Xfer *xfer );
  784. virtual void loadPostProcess();
  785. protected:
  786. UnsignedInt m_idleTooLongTimestamp; ///< when this is more than our idle too long time we try to do something about it
  787. Int m_idlePlayerNumber; ///< Remeber what list we were added to.
  788. Bool m_isMarkedAsIdle;
  789. };
  790. EMPTY_DTOR(DozerPrimaryIdleState)
  791. // ------------------------------------------------------------------------------------------------
  792. /** CRC */
  793. // ------------------------------------------------------------------------------------------------
  794. void DozerPrimaryIdleState::crc( Xfer *xfer )
  795. {
  796. } // end crc
  797. // ------------------------------------------------------------------------------------------------
  798. /** Xfer Method */
  799. // ------------------------------------------------------------------------------------------------
  800. void DozerPrimaryIdleState::xfer( Xfer *xfer )
  801. {
  802. // version
  803. XferVersion currentVersion = 1;
  804. XferVersion version = currentVersion;
  805. xfer->xferVersion( &version, currentVersion );
  806. xfer->xferUnsignedInt(&m_idleTooLongTimestamp);
  807. xfer->xferInt(&m_idlePlayerNumber);
  808. xfer->xferBool(&m_isMarkedAsIdle);
  809. } // end xfer
  810. // ------------------------------------------------------------------------------------------------
  811. /** Load post process */
  812. // ------------------------------------------------------------------------------------------------
  813. void DozerPrimaryIdleState::loadPostProcess( void )
  814. {
  815. } // end loadPostProcess
  816. //-------------------------------------------------------------------------------------------------
  817. /** Upon entering the dozer primary idle state */
  818. //-------------------------------------------------------------------------------------------------
  819. StateReturnType DozerPrimaryIdleState::onEnter( void )
  820. {
  821. //
  822. // upon entering this state we begin tracking how long we're idle for to try to do something
  823. // about it every once in a while
  824. //
  825. m_idleTooLongTimestamp = TheGameLogic->getFrame();
  826. //if(TheGameLogic->getFrame() > m_idleNotifyTimestamp + NOTIFY_AFTER_TIME)
  827. m_isMarkedAsIdle = FALSE;
  828. getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
  829. return STATE_CONTINUE;
  830. } // end onEnter
  831. //-------------------------------------------------------------------------------------------------
  832. /** Upon exiting the dozer primary idle state */
  833. //-------------------------------------------------------------------------------------------------
  834. void DozerPrimaryIdleState::onExit( StateExitType status )
  835. {
  836. if(m_isMarkedAsIdle)
  837. {
  838. TheInGameUI->removeIdleWorker(getMachineOwner(), m_idlePlayerNumber);
  839. m_idlePlayerNumber = -1;
  840. m_isMarkedAsIdle = FALSE;
  841. }
  842. }
  843. //-------------------------------------------------------------------------------------------------
  844. /** Dozer idle behavior */
  845. //-------------------------------------------------------------------------------------------------
  846. StateReturnType DozerPrimaryIdleState::update( void )
  847. {
  848. Object *dozer = getMachineOwner();
  849. AIUpdateInterface *ai = dozer->getAIUpdateInterface();
  850. if( !ai )
  851. {
  852. return STATE_FAILURE;
  853. }
  854. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  855. if( !dozerAI )
  856. {
  857. return STATE_FAILURE;
  858. }
  859. //
  860. // These are to add into the IngameUI idle worker button thingy
  861. // we don't want to add in if we're already in the list or if
  862. // we're "Effectivly dead"
  863. //
  864. if( ai->isIdle() && !m_isMarkedAsIdle && !dozer->isEffectivelyDead())
  865. {
  866. m_idlePlayerNumber = dozer->getControllingPlayer()->getPlayerIndex();
  867. TheInGameUI->addIdleWorker(getMachineOwner());
  868. m_isMarkedAsIdle = TRUE;
  869. getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
  870. }
  871. if( m_isMarkedAsIdle && (!ai->isIdle() || dozer->isEffectivelyDead()))
  872. {
  873. TheInGameUI->removeIdleWorker(getMachineOwner(), m_idlePlayerNumber);
  874. m_idlePlayerNumber = -1;
  875. m_isMarkedAsIdle = FALSE;
  876. }
  877. //
  878. // if our actual real AI state machine is not idle we're doing something so let's reset
  879. // our idle too long timestamp
  880. //
  881. if( !ai->isIdle() )
  882. m_idleTooLongTimestamp = TheGameLogic->getFrame();
  883. //
  884. // if we're just sitting here in idle, and there is no task pending ... after so long we'll
  885. // try to go to the nearest command center
  886. //
  887. if( TheGameLogic->getFrame() - m_idleTooLongTimestamp > dozerAI->getBoredTime() &&
  888. dozerAI->isAnyTaskPending() == FALSE )
  889. {
  890. //
  891. // to prevent us from doing this potentially expensive logic to find objects every frame
  892. // we'll only try it every once in a while so set our idle too long timestamp to the
  893. // current frame
  894. //
  895. m_idleTooLongTimestamp = TheGameLogic->getFrame();
  896. // try to find something around us that we can repair
  897. Object *repairTarget = findObjectToRepair( dozer );
  898. if( repairTarget )
  899. {
  900. // issue the command
  901. ai->aiRepair( repairTarget, CMD_FROM_AI );
  902. //
  903. // in theory we would need to "interrupt" whatever it is the Dozer is doing now, but
  904. // we know we're in the idle state doing nothing so we don't really need to
  905. //
  906. } else {
  907. getMachineOwner()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
  908. Object *mine = findMine(dozer);
  909. if (mine!=NULL) {
  910. ai->aiAttackObject( mine, 1, CMD_FROM_DOZER);
  911. }
  912. }
  913. } // end if
  914. return STATE_CONTINUE;
  915. } // end update
  916. //-------------------------------------------------------------------------------------------------
  917. /** Dozer action state */
  918. //-------------------------------------------------------------------------------------------------
  919. class DozerActionState : public State
  920. {
  921. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerActionState, "DozerActionState")
  922. public:
  923. DozerActionState( StateMachine *machine, DozerTask task ) : State( machine, "DozerActionState" )
  924. {
  925. m_task = task;
  926. m_actionMachine = newInstance(DozerActionStateMachine)( getMachineOwner(), task );
  927. m_actionMachine->initDefaultState();
  928. }
  929. /*
  930. Note that we DON'T use CONVERT_SLEEP_TO_CONTINUE; since we're not doing anything else
  931. interesting in update, we can sleep when this machine sleeps
  932. */
  933. virtual StateReturnType update( void ) { return m_actionMachine->updateStateMachine(); }
  934. virtual StateReturnType onEnter( void );
  935. virtual void onExit( StateExitType status );
  936. protected:
  937. // snapshot interface
  938. virtual void crc( Xfer *xfer );
  939. virtual void xfer( Xfer *xfer );
  940. virtual void loadPostProcess();
  941. protected:
  942. DozerTask m_task; // task for this state (and therefore sub-machine too)
  943. StateMachine *m_actionMachine;
  944. };
  945. inline DozerActionState::~DozerActionState( void ) { if (m_actionMachine) m_actionMachine->deleteInstance(); }
  946. // ------------------------------------------------------------------------------------------------
  947. /** CRC */
  948. // ------------------------------------------------------------------------------------------------
  949. void DozerActionState::crc( Xfer *xfer )
  950. {
  951. } // end crc
  952. // ------------------------------------------------------------------------------------------------
  953. /** Xfer Method */
  954. // ------------------------------------------------------------------------------------------------
  955. void DozerActionState::xfer( Xfer *xfer )
  956. {
  957. // version
  958. XferVersion currentVersion = 1;
  959. XferVersion version = currentVersion;
  960. xfer->xferVersion( &version, currentVersion );
  961. xfer->xferUser(&m_task, sizeof(m_task));
  962. xfer->xferSnapshot(m_actionMachine);
  963. } // end xfer
  964. // ------------------------------------------------------------------------------------------------
  965. /** Load post process */
  966. // ------------------------------------------------------------------------------------------------
  967. void DozerActionState::loadPostProcess( void )
  968. {
  969. } // end loadPostProcess
  970. // ------------------------------------------------------------------------------------------------
  971. // ------------------------------------------------------------------------------------------------
  972. StateReturnType DozerActionState::onEnter( void )
  973. {
  974. // save this as the current action of the dozer
  975. Object *dozer = getMachineOwner();
  976. if( !dozer->getAIUpdateInterface() )
  977. {
  978. return STATE_FAILURE;
  979. }
  980. DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
  981. dozerAI->setCurrentTask( m_task );
  982. //
  983. // we have a machine within this state all it's own ...
  984. // note that during an onEnter, since the action machine is persistent, anytime we transition
  985. // into doing an action, we need to reset our state machine to do that action as it may
  986. // have been left in any state from the previous time we ran it
  987. //
  988. m_actionMachine->resetToDefaultState();
  989. return STATE_CONTINUE;
  990. } // end onEnter
  991. // ------------------------------------------------------------------------------------------------
  992. // ------------------------------------------------------------------------------------------------
  993. void DozerActionState::onExit( StateExitType status )
  994. {
  995. // save the current action of the dozer as none
  996. Object *dozer = getMachineOwner();
  997. if( !dozer->getAIUpdateInterface() )
  998. {
  999. return;
  1000. }
  1001. DozerAIInterface *dozerAI = dozer->getAIUpdateInterface()->getDozerAIInterface();
  1002. dozerAI->setCurrentTask( DOZER_TASK_INVALID );
  1003. } // end onExit
  1004. //-------------------------------------------------------------------------------------------------
  1005. /** Dozer primary going home state */
  1006. //-------------------------------------------------------------------------------------------------
  1007. class DozerPrimaryGoingHomeState : public State
  1008. {
  1009. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DozerPrimaryGoingHomeState, "DozerPrimaryGoingHomeState")
  1010. protected:
  1011. // snapshot interface STUBBED no member vars
  1012. virtual void crc( Xfer *xfer ){};
  1013. virtual void xfer( Xfer *xfer ){};
  1014. virtual void loadPostProcess(){};
  1015. public:
  1016. DozerPrimaryGoingHomeState( StateMachine *machine ) : State( machine, "DozerPrimaryGoingHomeState" ) { }
  1017. virtual StateReturnType update( void ) { return STATE_FAILURE; }
  1018. };
  1019. EMPTY_DTOR(DozerPrimaryGoingHomeState)
  1020. //-------------------------------------------------------------------------------------------------
  1021. //-------------------------------------------------------------------------------------------------
  1022. DozerPrimaryStateMachine::DozerPrimaryStateMachine( Object *owner ) : StateMachine( owner, "DozerPrimaryStateMachine" )
  1023. {
  1024. static const StateConditionInfo idleConditions[] =
  1025. {
  1026. StateConditionInfo(isBuildMostImportant, DOZER_PRIMARY_BUILD, NULL),
  1027. StateConditionInfo(isRepairMostImportant, DOZER_PRIMARY_REPAIR, NULL),
  1028. StateConditionInfo(isFortifyMostImportant, DOZER_PRIMARY_FORTIFY, NULL),
  1029. StateConditionInfo(NULL, NULL, NULL) // keep last
  1030. };
  1031. // order matters: first state is the default state.
  1032. defineState( DOZER_PRIMARY_IDLE, newInstance(DozerPrimaryIdleState)( this ), INVALID_STATE_ID, INVALID_STATE_ID, idleConditions );
  1033. defineState( DOZER_PRIMARY_BUILD, newInstance(DozerActionState)( this, DOZER_TASK_BUILD ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
  1034. defineState( DOZER_PRIMARY_REPAIR, newInstance(DozerActionState)( this, DOZER_TASK_REPAIR ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
  1035. defineState( DOZER_PRIMARY_FORTIFY, newInstance(DozerActionState)( this, DOZER_TASK_FORTIFY ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
  1036. defineState( DOZER_PRIMARY_GO_HOME, newInstance(DozerPrimaryGoingHomeState)( this ), DOZER_PRIMARY_IDLE, DOZER_PRIMARY_IDLE );
  1037. } // end DozerPrimaryStateMachine
  1038. //-------------------------------------------------------------------------------------------------
  1039. //-------------------------------------------------------------------------------------------------
  1040. DozerPrimaryStateMachine::~DozerPrimaryStateMachine( void )
  1041. {
  1042. } // end ~DozerPrimaryStateMachine
  1043. // ------------------------------------------------------------------------------------------------
  1044. /** CRC */
  1045. // ------------------------------------------------------------------------------------------------
  1046. void DozerPrimaryStateMachine::crc( Xfer *xfer )
  1047. {
  1048. StateMachine::crc(xfer);
  1049. } // end crc
  1050. // ------------------------------------------------------------------------------------------------
  1051. /** Xfer Method */
  1052. // ------------------------------------------------------------------------------------------------
  1053. void DozerPrimaryStateMachine::xfer( Xfer *xfer )
  1054. {
  1055. XferVersion cv = 1;
  1056. XferVersion v = cv;
  1057. xfer->xferVersion( &v, cv );
  1058. StateMachine::xfer(xfer);
  1059. } // end xfer
  1060. // ------------------------------------------------------------------------------------------------
  1061. /** Load post process */
  1062. // ------------------------------------------------------------------------------------------------
  1063. void DozerPrimaryStateMachine::loadPostProcess( void )
  1064. {
  1065. StateMachine::loadPostProcess();
  1066. } // end loadPostProcess
  1067. //-------------------------------------------------------------------------------------------------
  1068. //-------------------------------------------------------------------------------------------------
  1069. Bool DozerPrimaryStateMachine::isBuildMostImportant( State *thisState, void* userData )
  1070. {
  1071. Object *dozer = thisState->getMachineOwner();
  1072. AIUpdateInterface *ai = dozer->getAIUpdateInterface();
  1073. if( !ai )
  1074. {
  1075. return FALSE;
  1076. }
  1077. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  1078. if( !dozerAI )
  1079. {
  1080. return FALSE;
  1081. }
  1082. if( !ai->isIdle() )
  1083. return FALSE; // busy doing something else
  1084. // if the most important task is us then return true
  1085. DozerTask task = dozerAI->getMostRecentCommand();
  1086. return task == DOZER_TASK_BUILD;
  1087. } // end isBuildMostImportant
  1088. //-------------------------------------------------------------------------------------------------
  1089. //-------------------------------------------------------------------------------------------------
  1090. Bool DozerPrimaryStateMachine::isRepairMostImportant( State *thisState, void* userData )
  1091. {
  1092. Object *dozer = thisState->getMachineOwner();
  1093. AIUpdateInterface *ai = dozer->getAIUpdateInterface();
  1094. if( !ai )
  1095. {
  1096. return false;
  1097. }
  1098. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  1099. if( !dozerAI )
  1100. {
  1101. return false;
  1102. }
  1103. if( !ai->isIdle() )
  1104. return FALSE; // busy doing something else
  1105. // if the most important task is us then return true
  1106. DozerTask task = dozerAI->getMostRecentCommand();
  1107. return task == DOZER_TASK_REPAIR;
  1108. } // end isRepairMostImportant
  1109. //-------------------------------------------------------------------------------------------------
  1110. //-------------------------------------------------------------------------------------------------
  1111. Bool DozerPrimaryStateMachine::isFortifyMostImportant( State *thisState, void* userData )
  1112. {
  1113. Object *dozer = thisState->getMachineOwner();
  1114. AIUpdateInterface *ai = dozer->getAIUpdateInterface();
  1115. if( !ai )
  1116. {
  1117. return false;
  1118. }
  1119. DozerAIInterface *dozerAI = ai->getDozerAIInterface();
  1120. if( !dozerAI )
  1121. {
  1122. return false;
  1123. }
  1124. if( !ai->isIdle() )
  1125. return FALSE; // busy doing something else
  1126. // if the most important task is us then return true
  1127. DozerTask task = dozerAI->getMostRecentCommand();
  1128. return task == DOZER_TASK_FORTIFY;
  1129. } // end isFortifyMostImportat
  1130. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1131. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1132. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1133. // ------------------------------------------------------------------------------------------------
  1134. // ------------------------------------------------------------------------------------------------
  1135. DozerAIUpdateModuleData::DozerAIUpdateModuleData( void )
  1136. {
  1137. m_repairHealthPercentPerSecond = 0.0f;
  1138. m_boredTime = 0.0f;
  1139. m_boredRange = 0.0f;
  1140. } // end DozerAIUpdateModuleData
  1141. // ------------------------------------------------------------------------------------------------
  1142. void DozerAIUpdateModuleData::buildFieldParse( MultiIniFieldParse& p)
  1143. {
  1144. AIUpdateModuleData::buildFieldParse( p );
  1145. static const FieldParse dataFieldParse[] =
  1146. {
  1147. { "RepairHealthPercentPerSecond", INI::parsePercentToReal, NULL, offsetof( DozerAIUpdateModuleData, m_repairHealthPercentPerSecond ) },
  1148. { "BoredTime", INI::parseDurationReal, NULL, offsetof( DozerAIUpdateModuleData, m_boredTime ) },
  1149. { "BoredRange", INI::parseReal, NULL, offsetof( DozerAIUpdateModuleData, m_boredRange ) },
  1150. { 0, 0, 0, 0 }
  1151. };
  1152. p.add( dataFieldParse );
  1153. } // end buildFieldParse
  1154. //-------------------------------------------------------------------------------------------------
  1155. //-------------------------------------------------------------------------------------------------
  1156. DozerAIUpdate::DozerAIUpdate( Thing *thing, const ModuleData* moduleData ) :
  1157. AIUpdateInterface( thing, moduleData )
  1158. {
  1159. Int i, j;
  1160. for( i = 0; i < DOZER_NUM_TASKS; i++ )
  1161. {
  1162. m_task[ i ].m_targetObjectID = INVALID_ID;
  1163. m_task[ i ].m_taskOrderFrame = 0;
  1164. for( j = 0; j < DOZER_NUM_DOCK_POINTS; j++ )
  1165. {
  1166. m_dockPoint[ i ][ j ].valid = FALSE;
  1167. m_dockPoint[ i ][ j ].location.zero();
  1168. } // end for j
  1169. } // end for i
  1170. m_currentTask = DOZER_TASK_INVALID;
  1171. m_buildSubTask = DOZER_SELECT_BUILD_DOCK_LOCATION; // irrelavant, but I want non-garbage value
  1172. //
  1173. // initialize the dozer machine to NULL, we want to do this and create it during the update
  1174. // implementation because at this point we don't have the object all setup
  1175. //
  1176. m_dozerMachine = NULL;
  1177. createMachines();
  1178. } // end DozerAIUpdate
  1179. //-------------------------------------------------------------------------------------------------
  1180. //-------------------------------------------------------------------------------------------------
  1181. DozerAIUpdate::~DozerAIUpdate( void )
  1182. {
  1183. // delete our behavior state machine
  1184. if( m_dozerMachine )
  1185. m_dozerMachine->deleteInstance();
  1186. // no orders
  1187. for( Int i = 0; i < DOZER_NUM_TASKS; i++ )
  1188. {
  1189. m_task[ i ].m_targetObjectID = INVALID_ID;
  1190. m_task[ i ].m_taskOrderFrame = 0;
  1191. } // end for i
  1192. } // end ~DozerAIUpdate
  1193. // ------------------------------------------------------------------------------------------------
  1194. /** Create any sub machines we need to do all our behavior */
  1195. // ------------------------------------------------------------------------------------------------
  1196. void DozerAIUpdate::createMachines( void )
  1197. {
  1198. if( m_dozerMachine == NULL )
  1199. {
  1200. m_dozerMachine = newInstance(DozerPrimaryStateMachine)( getObject() );
  1201. m_dozerMachine->initDefaultState();
  1202. } // end if
  1203. } // end createMachines
  1204. // ------------------------------------------------------------------------------------------------
  1205. /** Create the bridge scaffolding if necessary for the bridge that is attached to this tower */
  1206. // ------------------------------------------------------------------------------------------------
  1207. void DozerAIUpdate::createBridgeScaffolding( Object *bridgeTower )
  1208. {
  1209. // sanity
  1210. if( bridgeTower == NULL )
  1211. return;
  1212. // get the bridge behavior interface from the bridge object that this tower is a part of
  1213. BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( bridgeTower );
  1214. if( btbi == NULL )
  1215. return;
  1216. Object *bridgeObject = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1217. if( bridgeObject == NULL )
  1218. return;
  1219. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridgeObject );
  1220. if( bbi == NULL )
  1221. return;
  1222. // tell the bridge to create scaffolding if necessary
  1223. bbi->createScaffolding();
  1224. } // end createBridgeScaffolding
  1225. // ------------------------------------------------------------------------------------------------
  1226. /** Remove the bridge scaffolding from the bridge object that is attached to this tower */
  1227. // ------------------------------------------------------------------------------------------------
  1228. void DozerAIUpdate::removeBridgeScaffolding( Object *bridgeTower )
  1229. {
  1230. // sanity
  1231. if( bridgeTower == NULL )
  1232. return;
  1233. // get the bridge behavior interface from the bridge object that this tower is a part of
  1234. BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( bridgeTower );
  1235. if( btbi == NULL )
  1236. return;
  1237. Object *bridgeObject = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1238. if( bridgeObject == NULL )
  1239. return;
  1240. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( bridgeObject );
  1241. if( bbi == NULL )
  1242. return;
  1243. // tell the bridge to end any scaffolding from repairing
  1244. bbi->removeScaffolding();
  1245. } // end removeBridgeScaffolding
  1246. //-------------------------------------------------------------------------------------------------
  1247. //-------------------------------------------------------------------------------------------------
  1248. UpdateSleepTime DozerAIUpdate::update( void )
  1249. {
  1250. //
  1251. // NOTE: Any changes to DozerAIUpdate::* you probably want to reflect and copy into
  1252. // WorkerAIUPdate:* as well ... sigh
  1253. //
  1254. //
  1255. // now that we're really executing we have all the necessary object modules in place to
  1256. // correctly create a state machine and set the default state
  1257. //
  1258. createMachines();
  1259. // set us as being to able to move with super precision off grid locations
  1260. /*
  1261. if( getCurLocomotor() ) {
  1262. getCurLocomotor()->setUltraAccurate( TRUE );
  1263. getCurLocomotor()->setAllowInvalidPosition(TRUE);
  1264. }*/
  1265. // extend the normal AI system
  1266. UpdateSleepTime result;
  1267. result = AIUpdateInterface::update();
  1268. // do nothing if we're dead
  1269. ///@todo shouldn't this be at a higher level?
  1270. if( getObject()->isEffectivelyDead() )
  1271. return UPDATE_SLEEP_NONE;
  1272. // get and validate our current task
  1273. DozerTask currentTask = getCurrentTask();
  1274. if( currentTask != DOZER_TASK_INVALID )
  1275. {
  1276. ObjectID taskTarget = getTaskTarget( currentTask );
  1277. Object *targetObject = TheGameLogic->findObjectByID( taskTarget );
  1278. Bool invalidTask = FALSE;
  1279. // validate the task and the target
  1280. if( currentTask == DOZER_TASK_REPAIR &&
  1281. TheActionManager->canRepairObject( getObject(), targetObject, getLastCommandSource() ) == FALSE )
  1282. invalidTask = TRUE;
  1283. // cancel the task if it's now invalid
  1284. if( invalidTask == TRUE )
  1285. cancelTask( currentTask );
  1286. } // end if
  1287. else
  1288. getObject()->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);//maybe go clear some mines, if I feel like it
  1289. // run our own state machine
  1290. m_dozerMachine->updateStateMachine();
  1291. return UPDATE_SLEEP_NONE;
  1292. } // end update
  1293. //-------------------------------------------------------------------------------------------------
  1294. /** The entry point of a construct command to the Dozer */
  1295. //-------------------------------------------------------------------------------------------------
  1296. Object *DozerAIUpdate::construct( const ThingTemplate *what,
  1297. const Coord3D *pos,
  1298. Real angle,
  1299. Player *owningPlayer,
  1300. Bool isRebuild )
  1301. {
  1302. // !!! NOTE: If you modify this you must modify the worker too !!!
  1303. // !!! Graham: Please please please have inspiration for how to *not* duplicate this code
  1304. // create our machines if they don't yet exist
  1305. ///@todo make 'construct' a real AI command and you won't need a special case
  1306. m_isRebuild = isRebuild;
  1307. createMachines();
  1308. // sanity
  1309. if( what == NULL || pos == NULL || owningPlayer == NULL )
  1310. return NULL;
  1311. // sanity
  1312. DEBUG_ASSERTCRASH( getObject()->getControllingPlayer() == owningPlayer,
  1313. ("Dozer::Construct - The controlling player of the Dozer is not the owning player passed in\n") );
  1314. // if we're not rebuilding, we have a few checks to pass first for sanity
  1315. if( isRebuild == FALSE )
  1316. {
  1317. // AI has weaker restriction on building
  1318. Bool dozerIsAI = owningPlayer->getPlayerType() == PLAYER_COMPUTER;
  1319. if( dozerIsAI )
  1320. {
  1321. // Just build it. The ai will validate, or cheat. jba.
  1322. } // end if
  1323. else
  1324. {
  1325. // make sure the player is capable of building this
  1326. if( TheBuildAssistant->canMakeUnit( getObject(), what ) != CANMAKE_OK)
  1327. return NULL;
  1328. // validate the the position to build at is valid
  1329. if( TheBuildAssistant->isLocationLegalToBuild( pos, what, angle,
  1330. BuildAssistant::TERRAIN_RESTRICTIONS |
  1331. BuildAssistant::CLEAR_PATH |
  1332. BuildAssistant::NO_OBJECT_OVERLAP |
  1333. BuildAssistant::SHROUD_REVEALED,
  1334. getObject(), NULL ) != LBC_OK )
  1335. return NULL;
  1336. } // end else
  1337. } // end if
  1338. //
  1339. // what will our initial status bits be, it is important to do this early
  1340. // before the hooks add/subtract power from a player are executed
  1341. //
  1342. ObjectStatusMaskType statusBits = MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNDER_CONSTRUCTION );
  1343. if( isRebuild )
  1344. statusBits.set( OBJECT_STATUS_RECONSTRUCTING );
  1345. // create an object at the destination location
  1346. Object *obj = TheThingFactory->newObject( what, owningPlayer->getDefaultTeam(), statusBits );
  1347. // even though we haven't actually built anything yet, this keeps things tidy
  1348. obj->setProducer( getObject() );
  1349. obj->setBuilder( getObject() );
  1350. // take the required money away from the player
  1351. if( isRebuild == FALSE )
  1352. {
  1353. Money *money = owningPlayer->getMoney();
  1354. money->withdraw( what->calcCostToBuild( owningPlayer ) );
  1355. } // end if
  1356. // initialize object
  1357. obj->setPosition( pos );
  1358. obj->setOrientation( angle );
  1359. // Flatten the terrain underneath the object, then adjust to the flattened height. jba.
  1360. TheTerrainLogic->flattenTerrain(obj);
  1361. Coord3D adjustedPos = *pos;
  1362. adjustedPos.z = TheTerrainLogic->getGroundHeight(pos->x, pos->y);
  1363. obj->setPosition(&adjustedPos);
  1364. // Note - very important that we add to map AFTER we flatten terrain. jba.
  1365. TheAI->pathfinder()->addObjectToPathfindMap( obj );
  1366. // "callback" event for structure created (note that it's not yet "complete")
  1367. owningPlayer->onStructureCreated( getObject(), obj );
  1368. // set a construction percent for the new object to zero and a status for under construction
  1369. obj->setConstructionPercent( 0.0 );
  1370. // newly constructed objects start at one hit point
  1371. BodyModuleInterface *body = obj->getBodyModule();
  1372. body->internalChangeHealth( -body->getHealth() + 1.0f );
  1373. // set the model action state to awaiting construction
  1374. obj->clearAndSetModelConditionFlags(
  1375. MAKE_MODELCONDITION_MASK2(MODELCONDITION_PARTIALLY_CONSTRUCTED, MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED),
  1376. MAKE_MODELCONDITION_MASK(MODELCONDITION_AWAITING_CONSTRUCTION)
  1377. );
  1378. // we have a construction pending
  1379. newTask( DOZER_TASK_BUILD, obj );
  1380. return obj;
  1381. } // end construct
  1382. // ------------------------------------------------------------------------------------------------
  1383. /** Given our current task and repair target, can we accept this as a new repair target */
  1384. // ------------------------------------------------------------------------------------------------
  1385. Bool DozerAIUpdate::canAcceptNewRepair( Object *obj )
  1386. {
  1387. // sanity
  1388. if( obj == NULL )
  1389. return FALSE;
  1390. // if we're not repairing right now, we don't have any accept restrictions
  1391. if( getCurrentTask() != DOZER_TASK_REPAIR )
  1392. return TRUE;
  1393. // get current repair target
  1394. Object *currentRepair = TheGameLogic->findObjectByID( m_task[ DOZER_TASK_REPAIR ].m_targetObjectID );
  1395. if( currentRepair )
  1396. {
  1397. // check for same object
  1398. if( currentRepair == obj )
  1399. return FALSE;
  1400. // check for repairing any tower on the same bridge
  1401. if( currentRepair->isKindOf( KINDOF_BRIDGE_TOWER ) &&
  1402. obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
  1403. {
  1404. BridgeTowerBehaviorInterface *currentTowerInterface = NULL;
  1405. BridgeTowerBehaviorInterface *newTowerInterface = NULL;
  1406. currentTowerInterface = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( currentRepair );
  1407. newTowerInterface = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
  1408. // sanity
  1409. if( currentTowerInterface == NULL || newTowerInterface == NULL )
  1410. {
  1411. DEBUG_CRASH(( "Unable to find bridge tower interface on object\n" ));
  1412. return FALSE;
  1413. } // end if
  1414. // if they are part of the same bridge, ignore this repair command
  1415. if( currentTowerInterface->getBridgeID() == newTowerInterface->getBridgeID() )
  1416. return FALSE;
  1417. } // end if
  1418. } // end if, currentRepair object exists
  1419. // all is well
  1420. return TRUE;
  1421. } // end canAcceptNewRepair
  1422. // ------------------------------------------------------------------------------------------------
  1423. /** Issue an order for the Dozer to go repair the target 'obj' */
  1424. // ------------------------------------------------------------------------------------------------
  1425. void DozerAIUpdate::privateRepair( Object *obj, CommandSourceType cmdSource )
  1426. {
  1427. Object *dozer = getObject();
  1428. // if we are already repairing this target do nothing
  1429. if( canAcceptNewRepair( obj ) == FALSE )
  1430. return;
  1431. // sanity, if we can't repair the object then get out of there
  1432. if( TheActionManager->canRepairObject( dozer, obj, cmdSource ) == FALSE )
  1433. return;
  1434. // if this object is already actively being repaired by another dozertype we won't also try to go repair it
  1435. ObjectID currentRepairer = obj->getSoleHealingBenefactor();
  1436. if( currentRepairer != INVALID_ID && currentRepairer != dozer->getID() )
  1437. return;
  1438. // Bridges have been made indestructible, so these checks were in vain. -- ML
  1439. // if this object is a bridge tower, we need to check the status of the bridge in order
  1440. // to check for a 'duplicate repair'
  1441. //
  1442. //if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
  1443. //{
  1444. // BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
  1445. // DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower behavior interface\n") );
  1446. //
  1447. // Object *bridge = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1448. // DEBUG_ASSERTCRASH( bridge, ("Unable to find bridge object\n") );
  1449. // if( BitTest( bridge->getStatusBits(), OBJECT_STATUS_UNDERGOING_REPAIR ) == TRUE )
  1450. // return;
  1451. //
  1452. //} // end if
  1453. // for bridges, set the status for the bridge object
  1454. //if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
  1455. //{
  1456. // BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
  1457. // DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower behavior interface\n") );
  1458. //
  1459. // Object *bridge = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1460. // DEBUG_ASSERTCRASH( bridge, ("Unable to find bridge object\n") );
  1461. // bridge->setStatus( OBJECT_STATUS_UNDERGOING_REPAIR );
  1462. //
  1463. //} // end if
  1464. // start the new task
  1465. newTask( DOZER_TASK_REPAIR, obj );
  1466. } // end repair
  1467. // ------------------------------------------------------------------------------------------------
  1468. /** Resume construction on a building */
  1469. // ------------------------------------------------------------------------------------------------
  1470. void DozerAIUpdate::privateResumeConstruction( Object *obj, CommandSourceType cmdSource )
  1471. {
  1472. // sanity
  1473. if( obj == NULL )
  1474. return;
  1475. // make sure we can resume construction on this
  1476. if( TheActionManager->canResumeConstructionOf( getObject(), obj, cmdSource ) == FALSE )
  1477. return;
  1478. // start the new task for construction
  1479. newTask( DOZER_TASK_BUILD, obj );
  1480. } // end privateResumeConstruction
  1481. //-------------------------------------------------------------------------------------------------
  1482. //-------------------------------------------------------------------------------------------------
  1483. /*static*/ Bool DozerAIUpdate::findGoodBuildOrRepairPosition(const Object* me, const Object* target, Coord3D& positionOut)
  1484. {
  1485. // The place we go to build or repair is the closest spot from us to them
  1486. Coord3D ourPosition = *me->getPosition();
  1487. Coord3D theirPosition = *target->getPosition();
  1488. Coord3D bestPosition = theirPosition;// This answer is the best, as it includes findPositionAround
  1489. Coord3D workingPosition = theirPosition;// But if findPositionAround fails, we need to say something.
  1490. Vector3 offset( ourPosition.x - theirPosition.x,
  1491. ourPosition.y - theirPosition.y,
  1492. ourPosition.z - theirPosition.z );
  1493. offset.Normalize();
  1494. // This scaler makes FindPositionAround bias towards our side
  1495. offset = offset * (target->getGeometryInfo().getMajorRadius() / 2);
  1496. workingPosition.x += offset.X;
  1497. workingPosition.y += offset.Y;
  1498. workingPosition.z += offset.Z;
  1499. // this is a little cheesy... the idea is that we can only choose a location that is pretty close
  1500. // in z to the desired one. this prevents us from choosing a space at the bottom of a cliff when
  1501. // the space we want is at the top of the cliff. ideally we should do a funky terrain-zone compare
  1502. // but that isn't well-exposed... (srj)
  1503. const Real MAX_Z_DELTA = 10.0f;
  1504. FindPositionOptions fpOptions;
  1505. fpOptions.minRadius = 0.0f;
  1506. fpOptions.maxRadius = 100.0f;
  1507. fpOptions.sourceToPathToDest = me;// This makes it find a place forWhom can get to.
  1508. if (!me->isUsingAirborneLocomotor())
  1509. fpOptions.maxZDelta = MAX_Z_DELTA;
  1510. if( me->isUsingAirborneLocomotor() )
  1511. fpOptions.ignoreObject = target;// Flyers can ignore stuff, so they can approach right over the target if they want.
  1512. Bool spotFound = ThePartitionManager->findPositionAround( &workingPosition, &fpOptions, &bestPosition );
  1513. positionOut = spotFound ? bestPosition : workingPosition;
  1514. return spotFound;
  1515. }
  1516. //-------------------------------------------------------------------------------------------------
  1517. //-------------------------------------------------------------------------------------------------
  1518. /*static*/ Object* DozerAIUpdate::findGoodBuildOrRepairPositionAndTarget(Object* me, Object* target, Coord3D& positionOut)
  1519. {
  1520. if (target->isKindOf(KINDOF_BRIDGE))
  1521. {
  1522. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject(target);
  1523. if (bbi)
  1524. {
  1525. AIUpdateInterface* ai = me->getAI();
  1526. // have to repair at a tower.
  1527. Real bestDistSqr = 1e10f;
  1528. Object* bestTower = NULL;
  1529. for (Int i = 0; i < BRIDGE_MAX_TOWERS; ++i)
  1530. {
  1531. Object* tower = TheGameLogic->findObjectByID(bbi->getTowerID((BridgeTowerType)i));
  1532. if( tower )
  1533. {
  1534. Coord3D tmp;
  1535. Bool found = findGoodBuildOrRepairPosition(me, tower, tmp);
  1536. // do isPathAvail against the result of this, NOT the tower pos,
  1537. // since towers are often in cliff cells.
  1538. if (found && ai->isPathAvailable(&tmp))
  1539. {
  1540. Real thisDistSqr = sqr(me->getPosition()->x - tmp.x) + sqr(me->getPosition()->y - tmp.y);
  1541. if (thisDistSqr < bestDistSqr)
  1542. {
  1543. positionOut = tmp;
  1544. bestDistSqr = thisDistSqr;
  1545. bestTower = tower;
  1546. }
  1547. }
  1548. }
  1549. }
  1550. if (bestTower)
  1551. return bestTower;
  1552. DEBUG_CRASH(("should not happen, no reachable tower found"));
  1553. return NULL;
  1554. }
  1555. }
  1556. findGoodBuildOrRepairPosition(me, target, positionOut);
  1557. return target;
  1558. }
  1559. //-------------------------------------------------------------------------------------------------
  1560. /** Issue and order to the dozer */
  1561. //-------------------------------------------------------------------------------------------------
  1562. void DozerAIUpdate::newTask( DozerTask task, Object *target )
  1563. {
  1564. // sanity
  1565. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  1566. // sanity
  1567. if( target == NULL )
  1568. return;
  1569. //
  1570. // special check for the build task, we should never be given more than one of them ...
  1571. // for the other tasks we just forget what we were doing and the new target takes
  1572. // precedence for the task
  1573. //
  1574. if( task == DOZER_TASK_BUILD || task == DOZER_TASK_REPAIR )
  1575. {
  1576. // handle getting two tasks
  1577. if( isTaskPending( task ) == TRUE )
  1578. cancelTask( task );
  1579. // get our object
  1580. Object *me = getObject();
  1581. Coord3D position;
  1582. target = findGoodBuildOrRepairPositionAndTarget(me, target, position);
  1583. if (target == NULL)
  1584. return; // could happen for some bridges
  1585. //
  1586. // for building, we say that even "thinking" about building or rebuilding an object
  1587. // sets us as the current builder of that object. this allows any dozers that are
  1588. // ordered later to resume construction on something to see that somebody is already taking
  1589. // care of it and then they won't be even try to resume a build since we don't allow
  1590. // multiple dozers/workers to double up on construction efforts
  1591. //
  1592. if( task == DOZER_TASK_BUILD )
  1593. target->setBuilder( me );
  1594. m_dockPoint[ task ][ DOZER_DOCK_POINT_START ].valid = TRUE;
  1595. m_dockPoint[ task ][ DOZER_DOCK_POINT_START ].location = position;
  1596. m_dockPoint[ task ][ DOZER_DOCK_POINT_ACTION ].valid = TRUE;
  1597. m_dockPoint[ task ][ DOZER_DOCK_POINT_ACTION ].location = position;
  1598. Coord3D offset;
  1599. offset.set(position.x-target->getPosition()->x, position.y-target->getPosition()->y, 0);
  1600. offset.normalize();
  1601. offset.scale(5*PATHFIND_CELL_SIZE_F);
  1602. position.add(&offset); // move away from the dock point at the end of build.
  1603. m_dockPoint[ task ][ DOZER_DOCK_POINT_END ].valid = TRUE;
  1604. m_dockPoint[ task ][ DOZER_DOCK_POINT_END ].location = position;
  1605. } // end if
  1606. // set the new task target and the frame in which we got this order
  1607. m_task[ task ].m_targetObjectID = target->getID();
  1608. m_task[ task ].m_taskOrderFrame = TheGameLogic->getFrame();
  1609. // reset the dozer behavior so that it can re-evluate which task to continue working on
  1610. m_dozerMachine->resetToDefaultState();
  1611. } // end newTask
  1612. //-------------------------------------------------------------------------------------------------
  1613. /** Cancel a task and reset the dozer behavior state machine so that it can
  1614. * re-evaluate what it wants to do if it was working on the task being
  1615. * cancelled */
  1616. //-------------------------------------------------------------------------------------------------
  1617. void DozerAIUpdate::cancelTask( DozerTask task )
  1618. {
  1619. // clear the order
  1620. internalCancelTask( task );
  1621. // reset the machine to we can re-evaluate what we want to do
  1622. m_dozerMachine->resetToDefaultState();
  1623. } // end cancelTask
  1624. //-------------------------------------------------------------------------------------------------
  1625. /** Is there a given task waiting to be done */
  1626. //-------------------------------------------------------------------------------------------------
  1627. Bool DozerAIUpdate::isTaskPending( DozerTask task )
  1628. {
  1629. // sanity
  1630. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  1631. return m_task[ task ].m_targetObjectID != 0 ? TRUE : FALSE;
  1632. } // end isTaskPending
  1633. //-------------------------------------------------------------------------------------------------
  1634. /** Is there any task pending */
  1635. //-------------------------------------------------------------------------------------------------
  1636. Bool DozerAIUpdate::isAnyTaskPending( void )
  1637. {
  1638. for( Int i = 0; i < DOZER_NUM_TASKS; i++ )
  1639. if( isTaskPending( (DozerTask)i ) )
  1640. return TRUE;
  1641. return FALSE;
  1642. } // end isAnyTaskPending
  1643. //-------------------------------------------------------------------------------------------------
  1644. /** Get the target object of a given task */
  1645. //-------------------------------------------------------------------------------------------------
  1646. ObjectID DozerAIUpdate::getTaskTarget( DozerTask task )
  1647. {
  1648. // sanity
  1649. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  1650. return m_task[ task ].m_targetObjectID;
  1651. } // end getTaskTarget
  1652. //-------------------------------------------------------------------------------------------------
  1653. /** Set a task as successfully completed */
  1654. //-------------------------------------------------------------------------------------------------
  1655. void DozerAIUpdate::internalTaskComplete( DozerTask task )
  1656. {
  1657. // sanity
  1658. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  1659. // call the single method that gets called for completing and canceling tasks
  1660. internalTaskCompleteOrCancelled( task );
  1661. // remove the info for this task
  1662. m_task[ task ].m_targetObjectID = INVALID_ID;
  1663. m_task[ task ].m_taskOrderFrame = 0;
  1664. // remove dock point info for this task
  1665. for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ )
  1666. m_dockPoint[ task ][ i ].valid = FALSE;
  1667. } // end internalTaskComplete
  1668. //-------------------------------------------------------------------------------------------------
  1669. /** Clear a task from the Dozer for consideration, we can use this when a goal object becomes
  1670. * invalid/destroyed etc. */
  1671. //-------------------------------------------------------------------------------------------------
  1672. void DozerAIUpdate::internalCancelTask( DozerTask task )
  1673. {
  1674. // sanity
  1675. DEBUG_ASSERTCRASH( task >= 0 && task < DOZER_NUM_TASKS, ("Illegal dozer task '%d'\n", task) );
  1676. if(task < 0 || task >= DOZER_NUM_TASKS)
  1677. return; //DAMNIT! You CANNOT assert and then not handle the damn error! The. Code. Must. Not. Crash.
  1678. // call the single method that gets called for completing and canceling tasks
  1679. internalTaskCompleteOrCancelled( task );
  1680. // remove the info for this task
  1681. m_task[ task ].m_targetObjectID = INVALID_ID;
  1682. m_task[ task ].m_taskOrderFrame = 0;
  1683. // remove dock point info for this task
  1684. for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ )
  1685. m_dockPoint[ task ][ i ].valid = FALSE;
  1686. // stop the dozer from moving
  1687. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  1688. ai->aiIdle( CMD_FROM_AI );
  1689. } // end internalCancelTask
  1690. // ------------------------------------------------------------------------------------------------
  1691. /** This method is called whenever a task is completed *OR* cancelled */
  1692. // ------------------------------------------------------------------------------------------------
  1693. void DozerAIUpdate::internalTaskCompleteOrCancelled( DozerTask task )
  1694. {
  1695. switch( task )
  1696. {
  1697. // --------------------------------------------------------------------------------------------
  1698. case DOZER_TASK_INVALID:
  1699. {
  1700. break; // do nothing, this is really no task
  1701. } // end invalid
  1702. // --------------------------------------------------------------------------------------------
  1703. case DOZER_TASK_BUILD:
  1704. {
  1705. // the builder is no longer actively building something
  1706. getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  1707. // And the thing we were working on is no longer being actively built
  1708. ///@todo This would be correct except that we don't have idle crane animations and it is December.
  1709. // Object* goalObject = TheGameLogic->findObjectByID(m_task[task].m_targetObjectID);
  1710. // if (goalObject != NULL)
  1711. // {
  1712. // goalObject->clearModelConditionState(MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED);
  1713. // }
  1714. break;
  1715. } // end build
  1716. // --------------------------------------------------------------------------------------------
  1717. case DOZER_TASK_REPAIR:
  1718. {
  1719. // the builder is no longer actively repairing something
  1720. getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  1721. // Bridges have been made indestructible, so the code below was meaningless. -- ML
  1722. // Object *obj = NULL;
  1723. // get object to reapir (if present)
  1724. //obj = TheGameLogic->findObjectByID( m_task[ task ].m_targetObjectID );
  1725. //
  1726. //if( obj )
  1727. //{
  1728. //
  1729. //
  1730. // when we're done repairing bridges, tell the scaffolding to go away and also remove
  1731. // the undergoing repair status from the bridge object itself
  1732. //
  1733. // if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
  1734. // {
  1735. // // remove the bridge scaffold
  1736. // removeBridgeScaffolding( obj );
  1737. //
  1738. // // clear the repair bit from the bridge
  1739. // BridgeTowerBehaviorInterface *btbi = BridgeTowerBehavior::getBridgeTowerBehaviorInterfaceFromObject( obj );
  1740. // DEBUG_ASSERTCRASH( btbi, ("Unable to find bridge tower behavior interface\n") );
  1741. // Object *bridge = TheGameLogic->findObjectByID( btbi->getBridgeID() );
  1742. // DEBUG_ASSERTCRASH( bridge, ("Unable to find bridge object\n") );
  1743. // bridge->clearStatus( OBJECT_STATUS_UNDERGOING_REPAIR );
  1744. //
  1745. // } // end if
  1746. //
  1747. //} // end if
  1748. break;
  1749. } // end repair
  1750. // --------------------------------------------------------------------------------------------
  1751. case DOZER_TASK_FORTIFY:
  1752. {
  1753. break;
  1754. } // end fortify
  1755. // --------------------------------------------------------------------------------------------
  1756. default:
  1757. {
  1758. DEBUG_CRASH(( "internalTaskCompleteOrCancelled: Unknown Dozer task '%d'\n", task ));
  1759. break;
  1760. } // end default
  1761. } // end switch( task )
  1762. } // end internalTaskCompleteOrCancelled
  1763. //-------------------------------------------------------------------------------------------------
  1764. /** If we were building something, kill the active-construction flag on it */
  1765. //-------------------------------------------------------------------------------------------------
  1766. void DozerAIUpdate::onDelete( void )
  1767. {
  1768. Int i;
  1769. // cancel any of the tasks we had queued up
  1770. for( i = DOZER_TASK_FIRST; i < DOZER_NUM_TASKS; ++i )
  1771. {
  1772. if( isTaskPending( (DozerTask)i ) )
  1773. cancelTask( (DozerTask)i );
  1774. } // end for i
  1775. for( i = 0; i < DOZER_NUM_TASKS; i++ )
  1776. {
  1777. Object* goalObject = TheGameLogic->findObjectByID(m_task[i].m_targetObjectID);
  1778. if (goalObject != NULL)
  1779. {
  1780. goalObject->clearModelConditionState(MODELCONDITION_ACTIVELY_BEING_CONSTRUCTED);
  1781. }
  1782. }
  1783. }
  1784. //-------------------------------------------------------------------------------------------------
  1785. /** Get the most recently issued task */
  1786. //-------------------------------------------------------------------------------------------------
  1787. DozerTask DozerAIUpdate::getMostRecentCommand( void )
  1788. {
  1789. Int i;
  1790. DozerTask mostRecentTask = DOZER_TASK_INVALID;
  1791. UnsignedInt mostRecentFrame = 0;
  1792. for( i = 0; i < DOZER_NUM_TASKS; i++ )
  1793. {
  1794. if( isTaskPending( (DozerTask)i ) )
  1795. {
  1796. if( m_task[ i ].m_taskOrderFrame > mostRecentFrame )
  1797. {
  1798. mostRecentTask = (DozerTask)i;
  1799. mostRecentFrame = m_task[ i ].m_taskOrderFrame;
  1800. } // end if
  1801. } // end if
  1802. } // end for i
  1803. return mostRecentTask;
  1804. } // end getMostRecentCommand
  1805. // ------------------------------------------------------------------------------------------------
  1806. // ------------------------------------------------------------------------------------------------
  1807. const Coord3D* DozerAIUpdate::getDockPoint( DozerTask task, DozerDockPoint point )
  1808. {
  1809. // sanity
  1810. if( task < 0 || task >= DOZER_NUM_TASKS )
  1811. return NULL;
  1812. // sanity
  1813. if( point < 0 || point >= DOZER_NUM_DOCK_POINTS )
  1814. return NULL;
  1815. // if the point has been set (is valid) then return it
  1816. if( m_dockPoint[ task ][ point ].valid )
  1817. return &m_dockPoint[ task ][ point ].location;
  1818. // no valid point has been set for this dock point on this task
  1819. return NULL;
  1820. } // end getDockPoint
  1821. // ------------------------------------------------------------------------------------------------
  1822. Real DozerAIUpdate::getRepairHealthPerSecond( void ) const
  1823. {
  1824. return getDozerAIUpdateModuleData()->m_repairHealthPercentPerSecond;
  1825. }
  1826. // ------------------------------------------------------------------------------------------------
  1827. Real DozerAIUpdate::getBoredTime( void ) const
  1828. {
  1829. return getDozerAIUpdateModuleData()->m_boredTime;
  1830. }
  1831. // ------------------------------------------------------------------------------------------------
  1832. Real DozerAIUpdate::getBoredRange( void ) const
  1833. {
  1834. if (getObject()->getControllingPlayer() &&
  1835. getObject()->getControllingPlayer()->getPlayerType() == PLAYER_COMPUTER) {
  1836. return TheAI->getAiData()->m_aiDozerBoredRadiusModifier*getDozerAIUpdateModuleData()->m_boredRange;
  1837. }
  1838. return getDozerAIUpdateModuleData()->m_boredRange;
  1839. }
  1840. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1841. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1842. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1843. //-------------------------------------------------------------------------------------------------
  1844. void DozerAIUpdate::aiDoCommand(const AICommandParms* parms)
  1845. {
  1846. //
  1847. // anytime we get a command, just remove any model condition that has us actively building
  1848. // if we need to show that, that bit will be set anyway again during the build process
  1849. //
  1850. getObject()->clearModelConditionState( MODELCONDITION_ACTIVELY_CONSTRUCTING );
  1851. if (!isAllowedToRespondToAiCommands(parms))
  1852. return;
  1853. // if we haven't made the dozer machine yet, do so now
  1854. createMachines();
  1855. switch( parms->m_cmd )
  1856. {
  1857. case AICMD_MOVE_AWAY_FROM_UNIT:
  1858. {
  1859. Object *otherObj = parms->m_obj;
  1860. Bool otherIsDozer = false;
  1861. if (otherObj) {
  1862. otherIsDozer = otherObj->isKindOf(KINDOF_DOZER);
  1863. }
  1864. // We only want to do this if we aren't busy doing dozer things. jba.
  1865. // Or if the other guy is a dozer too.
  1866. if( !otherIsDozer && getCurrentTask() != DOZER_TASK_INVALID ) {
  1867. return; // just ignore it. jba.
  1868. }
  1869. // issue the command
  1870. AIUpdateInterface::aiDoCommand(parms);
  1871. break;
  1872. }
  1873. // --------------------------------------------------------------------------------------------
  1874. case AICMD_REPAIR:
  1875. {
  1876. // if we have no task right now, go idle so we can immediately respond to this
  1877. if( getCurrentTask() == DOZER_TASK_INVALID )
  1878. aiIdle( CMD_FROM_AI );
  1879. // do the repair
  1880. privateRepair(parms->m_obj, parms->m_cmdSource);
  1881. break;
  1882. } // end repair
  1883. // --------------------------------------------------------------------------------------------
  1884. case AICMD_RESUME_CONSTRUCTION:
  1885. {
  1886. // if we have no task right now, go idle so we can immediately respond to this
  1887. if( getCurrentTask() == DOZER_TASK_INVALID )
  1888. aiIdle( CMD_FROM_AI );
  1889. // do the command
  1890. privateResumeConstruction( parms->m_obj, parms->m_cmdSource );
  1891. break;
  1892. } // end resume construction
  1893. // --------------------------------------------------------------------------------------------
  1894. default:
  1895. {
  1896. // if this is from the player, cancel our current task
  1897. if( parms->m_cmdSource == CMD_FROM_PLAYER && getCurrentTask() != DOZER_TASK_INVALID )
  1898. cancelTask( getCurrentTask() );
  1899. // issue the command
  1900. AIUpdateInterface::aiDoCommand(parms);
  1901. // when a player issues commands, this will cause the dozer to re-evaluate what it's doing
  1902. if( parms->m_cmdSource == CMD_FROM_PLAYER )
  1903. m_dozerMachine->resetToDefaultState();
  1904. break;
  1905. } // end default
  1906. } // end switch
  1907. } // end aiDoCommand
  1908. //------------------------------------------------------------------------------------------------
  1909. void DozerAIUpdate::startBuildingSound( const AudioEventRTS *sound, ObjectID constructionSiteID )
  1910. {
  1911. m_buildingSound = *sound;
  1912. m_buildingSound.setObjectID( constructionSiteID );
  1913. m_buildingSound.setPlayingHandle( TheAudio->addAudioEvent( &m_buildingSound ) );
  1914. }
  1915. //------------------------------------------------------------------------------------------------
  1916. void DozerAIUpdate::finishBuildingSound()
  1917. {
  1918. TheAudio->removeAudioEvent( m_buildingSound.getPlayingHandle() );
  1919. }
  1920. // ------------------------------------------------------------------------------------------------
  1921. /** CRC */
  1922. // ------------------------------------------------------------------------------------------------
  1923. void DozerAIUpdate::crc( Xfer *xfer )
  1924. {
  1925. // extend base class
  1926. AIUpdateInterface::crc(xfer);
  1927. } // end crc
  1928. // ------------------------------------------------------------------------------------------------
  1929. /** Xfer method
  1930. * Version Info:
  1931. * 1: Initial version */
  1932. // ------------------------------------------------------------------------------------------------
  1933. void DozerAIUpdate::xfer( Xfer *xfer )
  1934. {
  1935. // version
  1936. XferVersion currentVersion = 1;
  1937. XferVersion version = currentVersion;
  1938. xfer->xferVersion( &version, currentVersion );
  1939. // extend base class
  1940. AIUpdateInterface::xfer(xfer);
  1941. Int numTasks = DOZER_NUM_TASKS;
  1942. xfer->xferInt(&numTasks);
  1943. if (numTasks != DOZER_NUM_TASKS) {
  1944. DEBUG_CRASH(("DOZER_NUM_TASKS changed unexpectedly."));
  1945. throw SC_INVALID_DATA;
  1946. }
  1947. Int i, j;
  1948. for (i=0; i<DOZER_NUM_TASKS; i++) {
  1949. xfer->xferObjectID(&m_task[i].m_targetObjectID);
  1950. xfer->xferUnsignedInt(&m_task[i].m_taskOrderFrame);
  1951. }
  1952. xfer->xferSnapshot(m_dozerMachine);
  1953. xfer->xferUser(&m_currentTask, sizeof(m_currentTask));
  1954. Int dockPoints = DOZER_NUM_DOCK_POINTS;
  1955. xfer->xferInt(&dockPoints);
  1956. if (dockPoints!=DOZER_NUM_DOCK_POINTS) {
  1957. DEBUG_CRASH(("DOZER_NUM_DOCK_POINTS changed unexpectedly."));
  1958. throw SC_INVALID_DATA;
  1959. }
  1960. for (i=0; i<DOZER_NUM_TASKS; i++) {
  1961. for (j=0; j<DOZER_NUM_DOCK_POINTS; j++) {
  1962. xfer->xferBool(&m_dockPoint[i][j].valid);
  1963. xfer->xferCoord3D(&m_dockPoint[i][j].location);
  1964. }
  1965. }
  1966. xfer->xferUser(&m_buildSubTask, sizeof(m_buildSubTask));
  1967. } // end xfer
  1968. // ------------------------------------------------------------------------------------------------
  1969. /** Load post process */
  1970. // ------------------------------------------------------------------------------------------------
  1971. void DozerAIUpdate::loadPostProcess( void )
  1972. {
  1973. // extend base class
  1974. AIUpdateInterface::loadPostProcess();
  1975. } // end loadPostProcess