ChinookAIUpdate.cpp 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677
  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. // ChinookAIUpdate.cpp //////////
  24. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  25. #define DEFINE_VETERANCY_NAMES // for TheVeterancyNames[]
  26. #include "Common/ActionManager.h"
  27. #include "Common/DrawModule.h"
  28. #include "Common/GameState.h"
  29. #include "Common/GlobalData.h"
  30. #include "Common/RandomValue.h"
  31. #include "Common/Team.h"
  32. #include "Common/ThingFactory.h"
  33. #include "Common/ThingTemplate.h"
  34. #include "Common/Xfer.h"
  35. #include "Common/Player.h"
  36. #include "Common/PlayerList.h"
  37. #include "GameClient/Drawable.h"
  38. #include "GameClient/GameClient.h"
  39. #include "GameClient/ParticleSys.h"
  40. #include "GameLogic/AIPathfind.h"
  41. #include "GameLogic/Locomotor.h"
  42. #include "GameLogic/Module/ContainModule.h"
  43. #include "GameLogic/Module/ChinookAIUpdate.h"
  44. #include "GameLogic/Module/PhysicsUpdate.h"
  45. #include "GameLogic/PartitionManager.h"
  46. const Real BIGNUM = 99999.0f;
  47. #ifdef _INTERNAL
  48. // for occasional debugging...
  49. //#pragma optimize("", off)
  50. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  51. #endif
  52. //-------------------------------------------------------------------------------------------------
  53. enum ChinookAIStateType
  54. {
  55. // note that these must be distinct (numerically) from AIStateType. ick.
  56. ChinookAIStateType_FIRST = 1000,
  57. TAKING_OFF,
  58. LANDING,
  59. MOVE_TO_AND_LAND,
  60. MOVE_TO_AND_EVAC,
  61. LAND_AND_EVAC,
  62. EVAC_AND_TAKEOFF,
  63. MOVE_TO_AND_EVAC_AND_EXIT,
  64. LAND_AND_EVAC_AND_EXIT,
  65. EVAC_AND_EXIT,
  66. TAKEOFF_AND_EXIT,
  67. HEAD_OFF_MAP,
  68. MOVE_TO_COMBAT_DROP,
  69. DO_COMBAT_DROP,
  70. MOVE_TO_AND_EVAC_AND_EXIT_INIT
  71. };
  72. //-------------------------------------------------------------------------------------------------
  73. //-------------------------------------------------------------------------------------------------
  74. //-------------------------------------------------------------------------------------------------
  75. //-------------------------------------------------------------------------------------------------
  76. static Real calcDistSqr(const Coord3D& a, const Coord3D& b)
  77. {
  78. return sqr(a.x-b.x) + sqr(a.y-b.y) + sqr(a.z-b.z);
  79. }
  80. //-------------------------------------------------------------------------------------------------
  81. static Object* getPotentialRappeller(Object* obj)
  82. {
  83. const ContainedItemsList* items = obj->getContain() ? obj->getContain()->getContainedItemsList() : NULL;
  84. if (items)
  85. {
  86. for (ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it )
  87. {
  88. Object* rider = *it;
  89. if (rider->isKindOf(KINDOF_CAN_RAPPEL))
  90. {
  91. return rider;
  92. }
  93. }
  94. }
  95. return NULL;
  96. }
  97. //----------------------------------------------------------------------------------------------------------
  98. class ChinookEvacuateState : public State
  99. {
  100. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ChinookEvacuateState, "ChinookEvacuateState")
  101. protected:
  102. // snapshot interface STUBBED - no member vars to save. jba.
  103. virtual void crc( Xfer *xfer ){};
  104. virtual void xfer( Xfer *xfer ){};
  105. virtual void loadPostProcess(){};
  106. public:
  107. ChinookEvacuateState( StateMachine *machine ) : State( machine, "ChinookEvacuateState" ) { }
  108. StateReturnType onEnter()
  109. {
  110. Object* obj = getMachineOwner();
  111. if( obj->getContain() )
  112. {
  113. obj->getContain()->removeAllContained(FALSE);
  114. }
  115. obj->getTeam()->setActive(); // why? I don't know.
  116. return STATE_SUCCESS;
  117. }
  118. virtual StateReturnType update()
  119. {
  120. return STATE_SUCCESS;
  121. }
  122. };
  123. EMPTY_DTOR(ChinookEvacuateState)
  124. //-------------------------------------------------------------------------------------------------
  125. class ChinookHeadOffMapState : public State
  126. {
  127. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ChinookHeadOffMapState, "ChinookHeadOffMapState")
  128. //I'm outta here
  129. protected:
  130. // snapshot interface STUBBED - no member vars to save. jba.
  131. virtual void crc( Xfer *xfer ){};
  132. virtual void xfer( Xfer *xfer ){};
  133. virtual void loadPostProcess(){};
  134. public:
  135. ChinookHeadOffMapState( StateMachine *machine ) : State( machine, "ChinookHeadOffMapState" ) {}
  136. StateReturnType onEnter() // Give move order out of town
  137. {
  138. Object *owner = getMachineOwner();
  139. ChinookAIUpdate* ai = (ChinookAIUpdate*)owner->getAIUpdateInterface();
  140. owner->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_RIDER8 ) );
  141. ai->aiMoveToPosition( ai->getOriginalPosition(), CMD_FROM_AI );
  142. ai->getCurLocomotor()->setAllowInvalidPosition(true);
  143. return STATE_CONTINUE;
  144. }
  145. StateReturnType update()
  146. {
  147. Object *owner = getMachineOwner();
  148. Region3D mapRegion;
  149. TheTerrainLogic->getExtentIncludingBorder( &mapRegion );
  150. if( !mapRegion.isInRegionNoZ( owner->getPosition() ) )
  151. {
  152. TheGameLogic->destroyObject(owner);
  153. return STATE_SUCCESS;
  154. }
  155. return STATE_CONTINUE;
  156. }
  157. void onExit()
  158. {
  159. Object *owner = getMachineOwner();
  160. owner->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_RIDER8 ) );
  161. }
  162. };
  163. EMPTY_DTOR(ChinookHeadOffMapState)
  164. //-------------------------------------------------------------------------------------------------
  165. class ChinookTakeoffOrLandingState : public State
  166. {
  167. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ChinookTakeoffOrLandingState, "ChinookTakeoffOrLandingState")
  168. private:
  169. Coord3D m_destLoc;
  170. Bool m_landing;
  171. protected:
  172. // snapshot interface
  173. virtual void crc( Xfer *xfer )
  174. {
  175. // empty
  176. }
  177. virtual void xfer( Xfer *xfer )
  178. {
  179. // version
  180. XferVersion currentVersion = 1;
  181. XferVersion version = currentVersion;
  182. xfer->xferVersion( &version, currentVersion );
  183. xfer->xferCoord3D(&m_destLoc);
  184. xfer->xferBool(&m_landing);
  185. }
  186. virtual void loadPostProcess()
  187. {
  188. // empty
  189. }
  190. public:
  191. ChinookTakeoffOrLandingState( StateMachine *machine, Bool landing ) : m_landing(landing), State( machine, "ChinookTakeoffOrLandingState" )
  192. {
  193. m_destLoc.zero();
  194. }
  195. virtual StateReturnType onEnter()
  196. {
  197. Object* obj = getMachineOwner();
  198. ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
  199. ai->friend_setFlightStatus(m_landing ? CHINOOK_LANDING : CHINOOK_TAKING_OFF);
  200. if( m_landing )
  201. {
  202. // A chinook given transport duty loses his supplies.
  203. while( ai->loseOneBox() );
  204. }
  205. // kill any drift...
  206. obj->getPhysics()->scrubVelocity2D(0);
  207. ai->chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  208. Locomotor* loco = ai->getCurLocomotor();
  209. loco->setUsePreciseZPos(true);
  210. loco->setUltraAccurate(true);
  211. m_destLoc = *obj->getPosition();
  212. const Bool onlyHealthyBridges = true; // ignore dead bridges.
  213. PathfindLayerEnum layerAtDest = TheTerrainLogic->getHighestLayerForDestination(&m_destLoc, onlyHealthyBridges);
  214. m_destLoc.z = TheTerrainLogic->getLayerHeight(m_destLoc.x, m_destLoc.y, layerAtDest);
  215. if (m_landing)
  216. {
  217. Coord3D tmp;
  218. FindPositionOptions options;
  219. options.maxRadius = obj->getGeometryInfo().getBoundingCircleRadius() * 100.0f;
  220. if (ThePartitionManager->findPositionAround(&m_destLoc, &options, &tmp))
  221. {
  222. m_destLoc = tmp;
  223. TheAI->pathfinder()->adjustToLandingDestination(obj, &m_destLoc);
  224. }
  225. // recalc, since it may have changed. note that findPositionAround() will ALWAYS
  226. // return a position on the ground proper, so if our initial search start pos was
  227. // above a bridge, this will put us below the bridge, which would be unfortunate.
  228. // so recheck to be sure. (note that the partitionmgr is 2d-only, so if it sez that
  229. // the position on the ground at that xy is clear, it will be clear for both the
  230. // ground proper and the bridge itself.) also note: don't call objectInteractsWithBridgeLayer(),
  231. // since it assumes that things that aren't close in z shouldn't interact.
  232. tmp = m_destLoc;
  233. tmp.z = obj->getPosition()->z;
  234. layerAtDest = TheTerrainLogic->getHighestLayerForDestination(&tmp, onlyHealthyBridges);
  235. m_destLoc.z = TheTerrainLogic->getLayerHeight(m_destLoc.x, m_destLoc.y, layerAtDest);
  236. obj->setLayer(layerAtDest);
  237. }
  238. else
  239. {
  240. m_destLoc.z += loco->getPreferredHeight();
  241. obj->setLayer(LAYER_GROUND);
  242. }
  243. return STATE_CONTINUE;
  244. }
  245. virtual StateReturnType update()
  246. {
  247. Object* obj = getMachineOwner();
  248. if (obj->isEffectivelyDead())
  249. return STATE_FAILURE;
  250. ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
  251. ai->setLocomotorGoalPositionExplicit(m_destLoc);
  252. const Real THRESH = 3.0f;
  253. const Real THRESH_SQR = THRESH*THRESH;
  254. if (calcDistSqr(*obj->getPosition(), m_destLoc) <= THRESH_SQR)
  255. return STATE_SUCCESS;
  256. return STATE_CONTINUE;
  257. }
  258. virtual void onExit( StateExitType status )
  259. {
  260. Object* obj = getMachineOwner();
  261. ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
  262. ai->friend_setFlightStatus(m_landing ? CHINOOK_LANDED : CHINOOK_FLYING);
  263. // Paranoia checks - sometimes onExit is called when we are
  264. // shutting down, and not all pieces are valid. CurLocomotor
  265. // is definitely null in some cases. jba.
  266. Locomotor* loco = ai->getCurLocomotor();
  267. if (loco)
  268. {
  269. loco->setUsePreciseZPos(false);
  270. loco->setUltraAccurate(false);
  271. // don't restore lift if dead -- this may fight with JetSlowDeathBehavior!
  272. if (!obj->isEffectivelyDead())
  273. loco->setMaxLift(BIGNUM);
  274. }
  275. if (m_landing)
  276. {
  277. ai->chooseLocomotorSet(LOCOMOTORSET_TAXIING);
  278. }
  279. else
  280. {
  281. // when takeoff is complete, always go back to layer-ground, rather than
  282. // some bridge layer.
  283. obj->setLayer(LAYER_GROUND);
  284. }
  285. }
  286. };
  287. EMPTY_DTOR(ChinookTakeoffOrLandingState)
  288. //-------------------------------------------------------------------------------------------------
  289. class ChinookCombatDropState : public State
  290. {
  291. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ChinookCombatDropState, "ChinookCombatDropState")
  292. private:
  293. struct RopeInfo
  294. {
  295. Drawable* ropeDrawable;
  296. DrawableID ropeID; // used only during save-load process
  297. Matrix3D dropStartMtx;
  298. Real ropeSpeed;
  299. Real ropeLen;
  300. Real ropeLenMax;
  301. UnsignedInt nextDropTime;
  302. std::list<ObjectID> rappellerIDs;
  303. };
  304. std::vector<RopeInfo> m_ropes;
  305. void removeDoneRappellers()
  306. {
  307. for (std::vector<RopeInfo>::iterator it = m_ropes.begin(); it != m_ropes.end(); ++it)
  308. {
  309. for (std::list<ObjectID>::iterator oit = it->rappellerIDs.begin(); oit != it->rappellerIDs.end(); )
  310. {
  311. Object* rappeller = TheGameLogic->findObjectByID(*oit);
  312. if (rappeller == NULL || rappeller->isEffectivelyDead() || !rappeller->isAboveTerrain())
  313. {
  314. oit = it->rappellerIDs.erase(oit);
  315. }
  316. else
  317. {
  318. ++oit;
  319. }
  320. }
  321. }
  322. }
  323. static void initRopeParms(Drawable* rope, Real length, Real width, const RGBColor& color, Real wobbleLen, Real wobbleAmp, Real wobbleRate)
  324. {
  325. RopeDrawInterface* tdi = NULL;
  326. for (DrawModule** d = rope->getDrawModules(); *d; ++d)
  327. {
  328. if ((tdi = (*d)->getRopeDrawInterface()) != NULL)
  329. {
  330. tdi->initRopeParms(length, width, color, wobbleLen, wobbleAmp, wobbleRate);
  331. }
  332. }
  333. }
  334. static void setRopeCurLen(Drawable* rope, Real length)
  335. {
  336. RopeDrawInterface* tdi = NULL;
  337. for (DrawModule** d = rope->getDrawModules(); *d; ++d)
  338. {
  339. if ((tdi = (*d)->getRopeDrawInterface()) != NULL)
  340. {
  341. tdi->setRopeCurLen(length);
  342. }
  343. }
  344. }
  345. static void setRopeSpeed(Drawable* rope, Real curSpeed, Real maxSpeed, Real accel)
  346. {
  347. RopeDrawInterface* tdi = NULL;
  348. for (DrawModule** d = rope->getDrawModules(); *d; ++d)
  349. {
  350. if ((tdi = (*d)->getRopeDrawInterface()) != NULL)
  351. {
  352. tdi->setRopeSpeed(curSpeed, maxSpeed, accel);
  353. }
  354. }
  355. }
  356. protected:
  357. // snapshot interface
  358. virtual void crc( Xfer *xfer )
  359. {
  360. // empty
  361. }
  362. virtual void xfer( Xfer *xfer )
  363. {
  364. // version
  365. const XferVersion currentVersion = 2;
  366. XferVersion version = currentVersion;
  367. xfer->xferVersion( &version, currentVersion );
  368. Int numRopes = m_ropes.size();
  369. xfer->xferInt(&numRopes);
  370. if (version >= 2)
  371. {
  372. if (xfer->getXferMode() == XFER_LOAD)
  373. {
  374. if (!m_ropes.empty())
  375. {
  376. DEBUG_CRASH(( "ChinookCombatDropState - ropes should be empty\n" ));
  377. throw SC_INVALID_DATA;
  378. }
  379. m_ropes.resize(numRopes);
  380. }
  381. for (Int i = 0; i < numRopes; ++i)
  382. {
  383. RopeInfo info;
  384. if (xfer->getXferMode() == XFER_SAVE)
  385. {
  386. info = m_ropes[i];
  387. // always overwrite this, since it's probably stale
  388. info.ropeID = info.ropeDrawable ? info.ropeDrawable->getID() : INVALID_DRAWABLE_ID;
  389. }
  390. xfer->xferDrawableID(&info.ropeID);
  391. xfer->xferMatrix3D(&info.dropStartMtx);
  392. xfer->xferReal(&info.ropeSpeed);
  393. xfer->xferReal(&info.ropeLen);
  394. xfer->xferReal(&info.ropeLenMax);
  395. xfer->xferUnsignedInt(&info.nextDropTime);
  396. xfer->xferSTLObjectIDList(&info.rappellerIDs);
  397. if (xfer->getXferMode() == XFER_LOAD)
  398. {
  399. info.ropeDrawable = NULL; // filled in via loadPostProcess
  400. m_ropes[i] = info;
  401. }
  402. }
  403. }
  404. }
  405. virtual void loadPostProcess()
  406. {
  407. for (std::vector<RopeInfo>::iterator it = m_ropes.begin(); it != m_ropes.end(); ++it)
  408. {
  409. it->ropeDrawable = TheGameClient->findDrawableByID(it->ropeID);
  410. // always nuke this, since we're done with it till we save/load again
  411. it->ropeID = INVALID_DRAWABLE_ID;
  412. }
  413. }
  414. public:
  415. ChinookCombatDropState( StateMachine *machine ): State( machine, "ChinookCombatDropState" ) { }
  416. // --------------
  417. virtual StateReturnType onEnter()
  418. {
  419. Object* obj = getMachineOwner();
  420. Drawable* draw = obj->getDrawable();
  421. if (draw == NULL)
  422. return STATE_FAILURE;
  423. ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
  424. const ChinookAIUpdateModuleData* d = ai->friend_getData();
  425. obj->setDisabled( DISABLED_HELD );
  426. ai->friend_setFlightStatus(CHINOOK_DOING_COMBAT_DROP);
  427. // A chinook given combat drop duty also loses his supplies.
  428. while( ai->loseOneBox() );
  429. UnsignedInt now = TheGameLogic->getFrame();
  430. const ThingTemplate* ropeTmpl = TheThingFactory->findTemplate(d->m_ropeName);
  431. const Int MAX_BONES = 32;
  432. Coord3D ropePos[MAX_BONES];
  433. Matrix3D dropMtx[MAX_BONES];
  434. Int ropeCount = draw->getPristineBonePositions("RopeStart", 1, ropePos, NULL, MAX_BONES);
  435. Int dropCount = draw->getPristineBonePositions("RopeEnd", 1, NULL, dropMtx, MAX_BONES);
  436. Int numRopes = d->m_numRopes;
  437. if (numRopes > ropeCount) numRopes = ropeCount;
  438. if (numRopes > dropCount) numRopes = dropCount;
  439. if (numRopes <= 0)
  440. return STATE_FAILURE;
  441. m_ropes.clear();
  442. for (Int i = 0; i < numRopes; ++i)
  443. {
  444. RopeInfo info;
  445. obj->convertBonePosToWorldPos( NULL, &dropMtx[i], NULL, &info.dropStartMtx );
  446. info.ropeDrawable = ropeTmpl ? TheThingFactory->newDrawable(ropeTmpl) : NULL;
  447. if (info.ropeDrawable)
  448. {
  449. obj->convertBonePosToWorldPos( &ropePos[i], NULL, &ropePos[i], NULL );
  450. info.ropeDrawable->setPosition(&ropePos[i]);
  451. info.ropeSpeed = 0.0f;
  452. info.ropeLen = 1.0f;
  453. const Bool onlyHealthyBridges = true; // ignore dead bridges.
  454. PathfindLayerEnum layerAtDest = TheTerrainLogic->getHighestLayerForDestination(&ropePos[i], onlyHealthyBridges);
  455. info.ropeLenMax = ropePos[i].z - TheTerrainLogic->getLayerHeight(ropePos[i].x, ropePos[i].y, layerAtDest) - d->m_ropeFinalHeight;
  456. initRopeParms(info.ropeDrawable, info.ropeLenMax, d->m_ropeWidth, d->m_ropeColor, d->m_ropeWobbleLen, d->m_ropeWobbleAmp, d->m_ropeWobbleRate);
  457. }
  458. info.nextDropTime = now + GameLogicRandomValue(d->m_perRopeDelayMin, d->m_perRopeDelayMax) - d->m_perRopeDelayMin;
  459. info.rappellerIDs.clear();
  460. m_ropes.push_back(info);
  461. }
  462. return STATE_CONTINUE;
  463. }
  464. // --------------
  465. virtual StateReturnType update()
  466. {
  467. Object* obj = getMachineOwner();
  468. ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
  469. const ChinookAIUpdateModuleData* d = ai->friend_getData();
  470. if (obj->isEffectivelyDead())
  471. {
  472. return STATE_FAILURE;
  473. }
  474. // first, eliminate "done" rappellers
  475. removeDoneRappellers();
  476. UnsignedInt now = TheGameLogic->getFrame();
  477. // ok, now check each rope: if it's empty, or we're at the next drop time, spawn a new rappeller
  478. Int numRopesInUse = 0;
  479. for (std::vector<RopeInfo>::iterator it = m_ropes.begin(); it != m_ropes.end(); ++it)
  480. {
  481. if (it->ropeLen < it->ropeLenMax)
  482. {
  483. it->ropeSpeed += fabs(TheGlobalData->m_gravity);
  484. if (it->ropeSpeed > d->m_ropeDropSpeed)
  485. it->ropeSpeed = d->m_ropeDropSpeed;
  486. it->ropeLen += it->ropeSpeed;
  487. setRopeCurLen(it->ropeDrawable, it->ropeLen);
  488. if (d->m_waitForRopesToDrop)
  489. {
  490. // can't use this rope till it's dropped all the way
  491. ++it->nextDropTime;
  492. continue;
  493. }
  494. }
  495. if (now >= it->nextDropTime)
  496. {
  497. Object* rappeller = getPotentialRappeller(obj);
  498. if (rappeller != NULL)
  499. {
  500. ExitInterface *exitInterface = obj->getObjectExitInterface();
  501. ExitDoorType exitDoor = exitInterface ? exitInterface->reserveDoorForExit(rappeller->getTemplate(), rappeller) : DOOR_NONE_AVAILABLE;
  502. if(exitDoor != DOOR_NONE_AVAILABLE)
  503. {
  504. exitInterface->exitObjectViaDoor(rappeller, exitDoor);
  505. }
  506. else
  507. {
  508. DEBUG_CRASH(("rappeller is not free to exit... what?"));
  509. }
  510. rappeller->setTransformMatrix(&it->dropStartMtx);
  511. AIUpdateInterface* rappellerAI = rappeller ? rappeller->getAIUpdateInterface() : NULL;
  512. if (rappellerAI)
  513. {
  514. rappellerAI->setDesiredSpeed(d->m_rappelSpeed);
  515. rappellerAI->aiRappelInto(getMachineGoalObject(), *getMachineGoalPosition(), CMD_FROM_AI);
  516. }
  517. it->rappellerIDs.push_back(rappeller->getID());
  518. it->nextDropTime = now + GameLogicRandomValue(d->m_perRopeDelayMin, d->m_perRopeDelayMax);
  519. }
  520. }
  521. if (!it->rappellerIDs.empty())
  522. {
  523. ++numRopesInUse;
  524. }
  525. }
  526. if (numRopesInUse == 0 && getPotentialRappeller(obj) == NULL)
  527. {
  528. // we're done!
  529. return STATE_SUCCESS;
  530. }
  531. return STATE_CONTINUE;
  532. }
  533. // --------------
  534. virtual void onExit( StateExitType status )
  535. {
  536. Object* obj = getMachineOwner();
  537. ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
  538. const ChinookAIUpdateModuleData* d = ai->friend_getData();
  539. obj->clearDisabled( DISABLED_HELD );
  540. ai->friend_setFlightStatus(CHINOOK_FLYING);
  541. if (obj->isEffectivelyDead())
  542. {
  543. // oops. drop the rangers.
  544. for (std::vector<RopeInfo>::iterator it = m_ropes.begin(); it != m_ropes.end(); ++it)
  545. {
  546. for (std::list<ObjectID>::iterator oit = it->rappellerIDs.begin(); oit != it->rappellerIDs.end(); ++oit)
  547. {
  548. Object* rappeller = TheGameLogic->findObjectByID(*oit);
  549. AIUpdateInterface* rappellerAI = rappeller ? rappeller->getAIUpdateInterface() : NULL;
  550. if (rappellerAI != NULL)
  551. {
  552. rappellerAI->aiIdle(CMD_FROM_AI);
  553. }
  554. }
  555. }
  556. }
  557. UnsignedInt now = TheGameLogic->getFrame();
  558. for (Int i = 0; i < m_ropes.size(); ++i)
  559. {
  560. if (m_ropes[i].ropeDrawable)
  561. {
  562. const UnsignedInt ROPE_EXPIRATION_TIME = LOGICFRAMES_PER_SECOND * 5;
  563. const Real initialSpeed = TheGlobalData->m_gravity * 30; // give it a little kick
  564. setRopeSpeed(m_ropes[i].ropeDrawable, initialSpeed, d->m_ropeDropSpeed, TheGlobalData->m_gravity);
  565. m_ropes[i].ropeDrawable->setExpirationDate(now + ROPE_EXPIRATION_TIME);
  566. m_ropes[i].ropeDrawable = NULL; // we're done with it, so null it so we won't save it
  567. }
  568. }
  569. m_ropes.clear();
  570. }
  571. };
  572. EMPTY_DTOR(ChinookCombatDropState)
  573. //-----------------------------------------------------------------------------------------------------------
  574. /**
  575. * Move to the GoalPosition, or GoalObject.
  576. */
  577. class ChinookMoveToBldgState : public AIMoveToState
  578. {
  579. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ChinookMoveToBldgState, "ChinookMoveToBldgState")
  580. private:
  581. Real m_oldPreferredHeight;
  582. Real m_newPreferredHeight;
  583. Real m_destZ;
  584. protected:
  585. // snapshot interface
  586. virtual void crc( Xfer *xfer )
  587. {
  588. // empty
  589. }
  590. virtual void xfer( Xfer *xfer )
  591. {
  592. // version
  593. XferVersion currentVersion = 1;
  594. XferVersion version = currentVersion;
  595. xfer->xferVersion( &version, currentVersion );
  596. xfer->xferReal(&m_oldPreferredHeight);
  597. xfer->xferReal(&m_newPreferredHeight);
  598. xfer->xferReal(&m_destZ);
  599. }
  600. virtual void loadPostProcess()
  601. {
  602. // empty
  603. }
  604. public:
  605. ChinookMoveToBldgState( StateMachine *machine ): AIMoveToState( machine ) { }
  606. virtual StateReturnType onEnter()
  607. {
  608. Object* obj = getMachineOwner();
  609. ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
  610. const ChinookAIUpdateModuleData* d = ai->friend_getData();
  611. Locomotor* loco = ai->getCurLocomotor();
  612. loco->setUltraAccurate(true);
  613. m_oldPreferredHeight = loco->getPreferredHeight();
  614. m_newPreferredHeight = m_oldPreferredHeight;
  615. const Coord3D* destPos;
  616. Object* bldg = getMachineGoalObject();
  617. if (bldg != NULL && !bldg->isEffectivelyDead() && bldg->isKindOf(KINDOF_STRUCTURE))
  618. {
  619. destPos = bldg->getPosition();
  620. m_newPreferredHeight = bldg->getGeometryInfo().getMaxHeightAbovePosition() + d->m_minDropHeight;
  621. if (m_newPreferredHeight < m_oldPreferredHeight)
  622. m_newPreferredHeight = m_oldPreferredHeight;
  623. }
  624. else
  625. {
  626. destPos = getMachineGoalPosition();
  627. }
  628. loco->setPreferredHeight(m_newPreferredHeight);
  629. m_destZ = TheTerrainLogic->getGroundHeight(destPos->x, destPos->y) + m_newPreferredHeight;
  630. return AIMoveToState::onEnter();
  631. }
  632. virtual StateReturnType update()
  633. {
  634. Object* obj = getMachineOwner();
  635. // the normal moveto state will bail when 2d pos matches; we need z, too
  636. StateReturnType status = AIMoveToState::update();
  637. const Real THRESH = 3.0f;
  638. if (status != STATE_CONTINUE && fabs(obj->getPosition()->z - m_destZ) > THRESH)
  639. status = STATE_CONTINUE;
  640. return status;
  641. }
  642. virtual void onExit( StateExitType status )
  643. {
  644. Object* obj = getMachineOwner();
  645. ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
  646. Locomotor* loco = ai->getCurLocomotor();
  647. loco->setPreferredHeight(m_oldPreferredHeight);
  648. loco->setUltraAccurate(false);
  649. AIMoveToState::onExit(status);
  650. }
  651. };
  652. EMPTY_DTOR(ChinookMoveToBldgState)
  653. //-----------------------------------------------------------------------------------------------------------
  654. /**
  655. * Store the original position we are assigned to for exit.
  656. */
  657. class ChinookRecordCreationState : public State
  658. {
  659. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ChinookRecordCreationState, "ChinookRecordCreationState")
  660. protected:
  661. // snapshot interface
  662. virtual void crc( Xfer *xfer )
  663. {
  664. // empty
  665. }
  666. virtual void xfer( Xfer *xfer )
  667. {
  668. // version
  669. XferVersion currentVersion = 1;
  670. XferVersion version = currentVersion;
  671. xfer->xferVersion( &version, currentVersion );
  672. }
  673. virtual void loadPostProcess()
  674. {
  675. // empty
  676. }
  677. public:
  678. ChinookRecordCreationState( StateMachine *machine ): State( machine, "ChinookRecordCreationState" ) { }
  679. virtual StateReturnType onEnter()
  680. {
  681. Object* obj = getMachineOwner();
  682. ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
  683. if( ai )
  684. {
  685. ai->recordOriginalPosition( *obj->getPosition() );
  686. }
  687. return STATE_SUCCESS;
  688. }
  689. virtual StateReturnType update()
  690. {
  691. return STATE_SUCCESS;
  692. }
  693. };
  694. EMPTY_DTOR(ChinookRecordCreationState)
  695. //-------------------------------------------------------------------------------------------------
  696. //-------------------------------------------------------------------------------------------------
  697. //-------------------------------------------------------------------------------------------------
  698. //-------------------------------------------------------------------------------------------------
  699. class ChinookAIStateMachine : public AIStateMachine
  700. {
  701. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( ChinookAIStateMachine, "ChinookAIStateMachine" );
  702. public:
  703. ChinookAIStateMachine( Object *owner, AsciiString name );
  704. };
  705. //-------------------------------------------------------------------------------------------------
  706. ChinookAIStateMachine::ChinookAIStateMachine(Object *owner, AsciiString name) : AIStateMachine(owner, name)
  707. {
  708. defineState( TAKING_OFF, newInstance(ChinookTakeoffOrLandingState)( this, false ), AI_IDLE, AI_IDLE );
  709. defineState( LANDING, newInstance(ChinookTakeoffOrLandingState)( this, true ), AI_IDLE, AI_IDLE );
  710. defineState( MOVE_TO_COMBAT_DROP, newInstance(ChinookMoveToBldgState)( this ), DO_COMBAT_DROP, AI_IDLE );
  711. defineState( DO_COMBAT_DROP, newInstance(ChinookCombatDropState)( this ), AI_IDLE, AI_IDLE );
  712. defineState( MOVE_TO_AND_LAND, newInstance(AIMoveToState)( this ), LANDING, AI_IDLE );
  713. defineState( MOVE_TO_AND_EVAC, newInstance(AIMoveToState)( this ), LAND_AND_EVAC, AI_IDLE );
  714. defineState( LAND_AND_EVAC, newInstance(ChinookTakeoffOrLandingState)( this, true ), EVAC_AND_TAKEOFF, AI_IDLE );
  715. defineState( EVAC_AND_TAKEOFF, newInstance(ChinookEvacuateState)( this ), TAKING_OFF, AI_IDLE );
  716. defineState( MOVE_TO_AND_EVAC_AND_EXIT_INIT, newInstance(ChinookRecordCreationState)( this ), MOVE_TO_AND_EVAC_AND_EXIT, AI_IDLE );
  717. defineState( MOVE_TO_AND_EVAC_AND_EXIT, newInstance(AIMoveToState)( this ), LAND_AND_EVAC_AND_EXIT, AI_IDLE );
  718. defineState( LAND_AND_EVAC_AND_EXIT, newInstance(ChinookTakeoffOrLandingState)( this, true ), EVAC_AND_EXIT, AI_IDLE );
  719. defineState( EVAC_AND_EXIT, newInstance(ChinookEvacuateState)( this ), TAKEOFF_AND_EXIT, AI_IDLE );
  720. defineState( TAKEOFF_AND_EXIT, newInstance(ChinookTakeoffOrLandingState)( this, false ), HEAD_OFF_MAP, AI_IDLE );
  721. defineState( HEAD_OFF_MAP, newInstance(ChinookHeadOffMapState)( this ), AI_IDLE, AI_IDLE );
  722. }
  723. //-------------------------------------------------------------------------------------------------
  724. ChinookAIStateMachine::~ChinookAIStateMachine()
  725. {
  726. }
  727. //-------------------------------------------------------------------------------------------------
  728. //-------------------------------------------------------------------------------------------------
  729. //-------------------------------------------------------------------------------------------------
  730. //-------------------------------------------------------------------------------------------------
  731. ChinookAIUpdateModuleData::ChinookAIUpdateModuleData()
  732. {
  733. m_numRopes = 4;
  734. m_ropeWidth = 0.5f;
  735. m_ropeColor.red = 0.9f;
  736. m_ropeColor.green = 0.8f;
  737. m_ropeColor.blue = 0.7f;
  738. m_perRopeDelayMin = 0x7fffffff;
  739. m_perRopeDelayMax = 0x7fffffff;
  740. m_ropeName = "GenericRope";
  741. m_waitForRopesToDrop = true;
  742. m_minDropHeight = 30.0f;
  743. m_ropeFinalHeight = 0.0f;
  744. m_ropeDropSpeed = 1e10f; // um, fast.
  745. m_rappelSpeed = fabs(TheGlobalData->m_gravity) * LOGICFRAMES_PER_SECOND * 0.5f;
  746. m_ropeWobbleLen = 10.0f;
  747. m_ropeWobbleAmp = 1.0f;
  748. m_ropeWobbleRate = 0.1f;
  749. m_rotorWashParticleSystem.clear();
  750. m_upgradedSupplyBoost = 0;
  751. }
  752. //-------------------------------------------------------------------------------------------------
  753. /*static*/ void ChinookAIUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  754. {
  755. SupplyTruckAIUpdateModuleData::buildFieldParse(p);
  756. static const FieldParse dataFieldParse[] =
  757. {
  758. { "RappelSpeed", INI::parseVelocityReal, 0, offsetof(ChinookAIUpdateModuleData, m_rappelSpeed) },
  759. { "RopeDropSpeed", INI::parseVelocityReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeDropSpeed) },
  760. { "RopeName", INI::parseAsciiString, 0, offsetof(ChinookAIUpdateModuleData, m_ropeName) },
  761. { "RopeFinalHeight", INI::parseReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeFinalHeight) },
  762. { "RopeWidth", INI::parseReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeWidth) },
  763. { "RopeWobbleLen", INI::parseReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeWobbleLen) },
  764. { "RopeWobbleAmplitude", INI::parseReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeWobbleAmp) },
  765. { "RopeWobbleRate", INI::parseAngularVelocityReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeWobbleRate) },
  766. { "RopeColor", INI::parseRGBColor, 0, offsetof(ChinookAIUpdateModuleData, m_ropeColor) },
  767. { "NumRopes", INI::parseUnsignedInt, 0, offsetof(ChinookAIUpdateModuleData, m_numRopes) },
  768. { "PerRopeDelayMin", INI::parseDurationUnsignedInt, 0, offsetof(ChinookAIUpdateModuleData, m_perRopeDelayMin) },
  769. { "PerRopeDelayMax", INI::parseDurationUnsignedInt, 0, offsetof(ChinookAIUpdateModuleData, m_perRopeDelayMax) },
  770. { "MinDropHeight", INI::parseReal, 0, offsetof(ChinookAIUpdateModuleData, m_minDropHeight) },
  771. { "WaitForRopesToDrop", INI::parseBool, 0, offsetof(ChinookAIUpdateModuleData, m_waitForRopesToDrop) },
  772. { "RotorWashParticleSystem", INI::parseAsciiString, NULL, offsetof( ChinookAIUpdateModuleData, m_rotorWashParticleSystem ) },
  773. { "UpgradedSupplyBoost", INI::parseInt, NULL, offsetof( ChinookAIUpdateModuleData, m_upgradedSupplyBoost) },
  774. { 0, 0, 0, 0 }
  775. };
  776. p.add(dataFieldParse);
  777. }
  778. //-------------------------------------------------------------------------------------------------
  779. //-------------------------------------------------------------------------------------------------
  780. //-------------------------------------------------------------------------------------------------
  781. //-------------------------------------------------------------------------------------------------
  782. AIStateMachine* ChinookAIUpdate::makeStateMachine()
  783. {
  784. return newInstance(ChinookAIStateMachine)( getObject(), "ChinookAIStateMachine");
  785. }
  786. //-------------------------------------------------------------------------------------------------
  787. ChinookAIUpdate::ChinookAIUpdate( Thing *thing, const ModuleData* moduleData ) : SupplyTruckAIUpdate( thing, moduleData )
  788. {
  789. m_hasPendingCommand = false;
  790. m_flightStatus = CHINOOK_FLYING; // yep, that's right, even if we start "on ground"
  791. m_airfieldForHealing = INVALID_ID;
  792. m_originalPos.zero();
  793. }
  794. //-------------------------------------------------------------------------------------------------
  795. ChinookAIUpdate::~ChinookAIUpdate()
  796. {
  797. }
  798. //-------------------------------------------------------------------------------------------------
  799. static ParkingPlaceBehaviorInterface* getPP(ObjectID id)
  800. {
  801. Object* airfield = TheGameLogic->findObjectByID( id );
  802. if (airfield == NULL || airfield->isEffectivelyDead() || !airfield->isKindOf(KINDOF_FS_AIRFIELD))
  803. return NULL;
  804. ParkingPlaceBehaviorInterface* pp = NULL;
  805. for (BehaviorModule** i = airfield->getBehaviorModules(); *i; ++i)
  806. {
  807. if ((pp = (*i)->getParkingPlaceBehaviorInterface()) != NULL)
  808. break;
  809. }
  810. return pp;
  811. }
  812. //-------------------------------------------------------------------------------------------------
  813. void ChinookAIUpdate::setAirfieldForHealing(ObjectID id)
  814. {
  815. // make sure we de-register with current one, if any
  816. if (m_airfieldForHealing != INVALID_ID && m_airfieldForHealing != id)
  817. {
  818. ParkingPlaceBehaviorInterface* pp = getPP(m_airfieldForHealing);
  819. if (pp != NULL)
  820. {
  821. pp->setHealee(getObject(), false);
  822. }
  823. }
  824. m_airfieldForHealing = id;
  825. }
  826. //-------------------------------------------------------------------------------------------------
  827. Bool ChinookAIUpdate::isIdle() const
  828. {
  829. // we need to do this because we enter an idle state briefly between takeoff/landing in these cases,
  830. // but scripting relies on us never claiming to be "idle"...
  831. if (m_hasPendingCommand)
  832. return false;
  833. Bool result = SupplyTruckAIUpdate::isIdle();
  834. if (result && m_flightStatus == CHINOOK_LANDED)
  835. {
  836. // ditto: if we are waiting to disgorge some folks, we aren't 'idle'
  837. ContainModuleInterface* contain = getObject()->getContain();
  838. if (contain && contain->hasObjectsWantingToEnterOrExit())
  839. result = false;
  840. }
  841. return result;
  842. }
  843. //-------------------------------------------------------------------------------------------------
  844. Bool ChinookAIUpdate::isCurrentlyFerryingSupplies() const
  845. {
  846. return SupplyTruckAIUpdate::isCurrentlyFerryingSupplies();
  847. }
  848. //-------------------------------------------------------------------------------------------------
  849. Bool ChinookAIUpdate::isAvailableForSupplying() const
  850. {
  851. if (!SupplyTruckAIUpdate::isAvailableForSupplying())
  852. return false;
  853. ContainModuleInterface* contain = getObject()->getContain();
  854. if( !contain || contain->hasObjectsWantingToEnterOrExit() || contain->getContainCount() || contain->isSpecialOverlordStyleContainer())
  855. return false;
  856. return true;
  857. }
  858. //-------------------------------------------------------------------------------------------------
  859. Bool ChinookAIUpdate::isAllowedToAdjustDestination() const
  860. {
  861. if (m_flightStatus == CHINOOK_LANDED)
  862. return false;
  863. if( getCurLocomotor()->isInvalidPositionAllowed() )
  864. {
  865. return FALSE;
  866. }
  867. return SupplyTruckAIUpdate::isAllowedToAdjustDestination();
  868. }
  869. //-------------------------------------------------------------------------------------------------
  870. ObjectID ChinookAIUpdate::getBuildingToNotPathAround() const
  871. {
  872. if (getAIStateType() == MOVE_TO_COMBAT_DROP || getAIStateType() == DO_COMBAT_DROP)
  873. {
  874. const Object* goalObj = getStateMachine()->getGoalObject();
  875. if (goalObj)
  876. return goalObj->getID();
  877. }
  878. return INVALID_ID;
  879. }
  880. //-------------------------------------------------------------------------------------------------
  881. AIFreeToExitType ChinookAIUpdate::getAiFreeToExit(const Object* exiter) const
  882. {
  883. if (m_flightStatus == CHINOOK_LANDED
  884. || (m_flightStatus == CHINOOK_DOING_COMBAT_DROP && exiter->isKindOf(KINDOF_CAN_RAPPEL)))
  885. return FREE_TO_EXIT;
  886. return WAIT_TO_EXIT;
  887. }
  888. //-------------------------------------------------------------------------------------------------
  889. Bool ChinookAIUpdate::chooseLocomotorSet(LocomotorSetType wst)
  890. {
  891. if (m_flightStatus == CHINOOK_LANDED)
  892. wst = LOCOMOTORSET_TAXIING;
  893. return SupplyTruckAIUpdate::chooseLocomotorSet(wst);
  894. }
  895. //-------------------------------------------------------------------------------------------------
  896. UpdateSleepTime ChinookAIUpdate::update()
  897. {
  898. ParkingPlaceBehaviorInterface* pp = getPP(m_airfieldForHealing);
  899. if (pp != NULL)
  900. {
  901. if (m_flightStatus == CHINOOK_LANDED &&
  902. !m_hasPendingCommand &&
  903. getObject()->getBodyModule()->getHealth() == getObject()->getBodyModule()->getMaxHealth())
  904. {
  905. // we're completely healed, so take off again
  906. pp->setHealee(getObject(), false);
  907. setMyState(TAKING_OFF, NULL, NULL, CMD_FROM_AI);
  908. }
  909. else
  910. {
  911. pp->setHealee(getObject(), m_flightStatus == CHINOOK_LANDED);
  912. }
  913. }
  914. else
  915. {
  916. setAirfieldForHealing(INVALID_ID);
  917. }
  918. // have to call our parent's isIdle, because we override it to never return true
  919. // when we have a pending command...
  920. ContainModuleInterface* contain = getObject()->getContain();
  921. if( contain )
  922. {
  923. if (SupplyTruckAIUpdate::isIdle())
  924. {
  925. Bool waitingToEnterOrExit = contain->hasObjectsWantingToEnterOrExit();
  926. if (m_hasPendingCommand)
  927. {
  928. AICommandParms parms(AICMD_MOVE_TO_POSITION, CMD_FROM_AI); // values don't matter, will be wiped by next line
  929. m_pendingCommand.reconstitute(parms);
  930. m_hasPendingCommand = false;
  931. aiDoCommand(&parms);
  932. }
  933. else if (waitingToEnterOrExit && m_flightStatus != CHINOOK_LANDED)
  934. {
  935. setMyState(LANDING, NULL, NULL, CMD_FROM_AI);
  936. }
  937. else if (!waitingToEnterOrExit && m_flightStatus == CHINOOK_LANDED && m_airfieldForHealing == INVALID_ID)
  938. {
  939. setMyState(TAKING_OFF, NULL, NULL, CMD_FROM_AI);
  940. }
  941. }
  942. if ( TheGameLogic->getFrame()%10 == 1 )
  943. {
  944. Object *victim = getCurrentVictim();
  945. if ( victim )
  946. {
  947. // privateAttackObject( victim, 9999, CMD_FROM_AI );
  948. //If we are attacking something, lets make sure our passengers follow suit
  949. if ( contain->isPassengerAllowedToFire() )
  950. {
  951. const ContainedItemsList *passengerList = contain->getContainedItemsList();
  952. ContainedItemsList::const_iterator passengerIterator;
  953. passengerIterator = passengerList->begin();
  954. while( passengerIterator != passengerList->end() )
  955. {
  956. Object *passenger = *passengerIterator;
  957. //Advance to the next iterator
  958. passengerIterator++;
  959. AIUpdateInterface *passengerAI = passenger->getAIUpdateInterface();
  960. if( passengerAI && (passengerAI->getCurrentVictim() == NULL) )
  961. {
  962. passengerAI->aiAttackObject( victim, 999, CMD_FROM_AI );
  963. }
  964. }
  965. }
  966. }
  967. }
  968. }
  969. // Just a handy spot to handle that groovy client effect of the rotor wash
  970. if ( getObject()->getShroudedStatus( ThePlayerList->getLocalPlayer()->getPlayerIndex()) == OBJECTSHROUD_CLEAR )
  971. {
  972. if ( m_flightStatus == CHINOOK_LANDING || m_flightStatus == CHINOOK_TAKING_OFF || m_flightStatus == CHINOOK_LANDED )
  973. {
  974. Coord3D pos = *getObject()->getPosition();
  975. Real chopperElevation = pos.z;
  976. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 3.0f;
  977. chopperElevation -= pos.z;
  978. if ( GameClientRandomValueReal( 0.0f, chopperElevation ) < 5.0f )
  979. {
  980. const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate( getChinookAIUpdateModuleData()->m_rotorWashParticleSystem );
  981. ParticleSystem *system;
  982. if( tmp )
  983. {
  984. system = TheParticleSystemManager->createParticleSystem( tmp );
  985. if( system )
  986. {
  987. system->setPosition( &pos );
  988. }
  989. }
  990. }
  991. }
  992. }
  993. return SupplyTruckAIUpdate::update();
  994. }
  995. //-------------------------------------------------------------------------------------------------
  996. void ChinookAIUpdate::setMyState( StateID cmd, Object* target, const Coord3D* pos, CommandSourceType cmdSource )
  997. {
  998. getStateMachine()->clear();
  999. getStateMachine()->setGoalObject( target );
  1000. setGoalPositionClipped(pos, cmdSource); // yeah, null is ok here.
  1001. setLastCommandSource( cmdSource );
  1002. getStateMachine()->setState( cmd );
  1003. }
  1004. //----------------------------------------------------------------------------------------
  1005. /**
  1006. * Get repaired at the repair depot
  1007. */
  1008. void ChinookAIUpdate::privateGetRepaired( Object *repairDepot, CommandSourceType cmdSource )
  1009. {
  1010. // we are already landing. just ignore it.
  1011. if (m_flightStatus == CHINOOK_LANDING || m_flightStatus == CHINOOK_LANDED)
  1012. return;
  1013. // sanity, if we can't get repaired from here get out of here
  1014. if( TheActionManager->canGetRepairedAt( getObject(), repairDepot, cmdSource ) == FALSE )
  1015. return;
  1016. setAirfieldForHealing(repairDepot->getID());
  1017. Coord3D pos = *repairDepot->getPosition();
  1018. Coord3D tmp;
  1019. FindPositionOptions options;
  1020. options.maxRadius = repairDepot->getGeometryInfo().getBoundingCircleRadius() * 100.0f;
  1021. if (ThePartitionManager->findPositionAround(&pos, &options, &tmp))
  1022. pos = tmp;
  1023. setMyState(MOVE_TO_AND_LAND, NULL, &pos, cmdSource);
  1024. }
  1025. //-------------------------------------------------------------------------------------------------
  1026. void ChinookAIUpdate::privateCombatDrop( Object* target, const Coord3D& pos, CommandSourceType cmdSource )
  1027. {
  1028. //
  1029. // when there is a target present, we must verify that we can logically do the action when
  1030. // we get commands from players (we'll assume AI knows what its doing)
  1031. //
  1032. if( target != NULL && cmdSource == CMD_FROM_PLAYER &&
  1033. TheActionManager->canEnterObject( getObject(), target, cmdSource, COMBATDROP_INTO ) == FALSE )
  1034. return;
  1035. Coord3D localPos = pos;
  1036. if (target == NULL)
  1037. {
  1038. // if target is null, we are dropping at a pos, not into a bldg.
  1039. // in this case, ensure there is no structure at the pos... this can happen
  1040. // if you combat-drop into a spot in the fog-of-war.
  1041. Coord3D tmp;
  1042. FindPositionOptions options;
  1043. options.maxRadius = getObject()->getGeometryInfo().getBoundingCircleRadius() * 100.0f;
  1044. if (ThePartitionManager->findPositionAround(&localPos, &options, &tmp))
  1045. {
  1046. localPos = tmp;
  1047. }
  1048. }
  1049. // start the combat drop process
  1050. setMyState(MOVE_TO_COMBAT_DROP, target, &localPos, cmdSource);
  1051. }
  1052. //-------------------------------------------------------------------------------------------------
  1053. void ChinookAIUpdate::aiDoCommand(const AICommandParms* parms)
  1054. {
  1055. // this gets reset every time a command is issued.
  1056. setAirfieldForHealing(INVALID_ID);
  1057. if (!isAllowedToRespondToAiCommands(parms))
  1058. return;
  1059. if (m_flightStatus == CHINOOK_TAKING_OFF ||
  1060. m_flightStatus == CHINOOK_LANDING ||
  1061. m_flightStatus == CHINOOK_DOING_COMBAT_DROP)
  1062. {
  1063. // have to wait for takeoff or landing (or rappel) to complete, just store the sucker.
  1064. m_pendingCommand.store(*parms);
  1065. m_hasPendingCommand = true;
  1066. return;
  1067. }
  1068. Bool passItThru = true;
  1069. switch (parms->m_cmd)
  1070. {
  1071. case AICMD_IDLE:
  1072. case AICMD_BUSY:
  1073. case AICMD_FOLLOW_EXITPRODUCTION_PATH:
  1074. {
  1075. // don't need (or want) to take off for these...
  1076. // just pass it thru.
  1077. }
  1078. break;
  1079. case AICMD_MOVE_TO_POSITION_AND_EVACUATE:
  1080. case AICMD_MOVE_TO_POSITION_AND_EVACUATE_AND_EXIT:
  1081. {
  1082. const Real THRESH = 3.0f;
  1083. const Real THRESH_SQR = THRESH*THRESH;
  1084. if (calcDistSqr(*getObject()->getPosition(), parms->m_pos) > THRESH_SQR &&
  1085. m_flightStatus == CHINOOK_LANDED)
  1086. {
  1087. // gotta take off first!
  1088. m_pendingCommand.store(*parms);
  1089. m_hasPendingCommand = true;
  1090. setMyState(TAKING_OFF, NULL, NULL, CMD_FROM_AI);
  1091. passItThru = false;
  1092. }
  1093. else
  1094. {
  1095. // do this INSTEAD of the standard stuff
  1096. setMyState(
  1097. (parms->m_cmd == AICMD_MOVE_TO_POSITION_AND_EVACUATE) ? MOVE_TO_AND_EVAC : MOVE_TO_AND_EVAC_AND_EXIT_INIT,
  1098. NULL, &parms->m_pos, CMD_FROM_AI);
  1099. passItThru = false;
  1100. }
  1101. }
  1102. break;
  1103. case AICMD_EXIT:
  1104. case AICMD_EVACUATE:
  1105. {
  1106. if (m_flightStatus != CHINOOK_LANDED)
  1107. {
  1108. // gotta land first!
  1109. m_pendingCommand.store(*parms);
  1110. m_hasPendingCommand = true;
  1111. setMyState(LANDING, NULL, NULL, CMD_FROM_AI);
  1112. passItThru = false;
  1113. }
  1114. }
  1115. break;
  1116. default:
  1117. {
  1118. if (m_flightStatus != CHINOOK_FLYING)
  1119. {
  1120. // gotta take off first!
  1121. m_pendingCommand.store(*parms);
  1122. m_hasPendingCommand = true;
  1123. setMyState(TAKING_OFF, NULL, NULL, CMD_FROM_AI);
  1124. passItThru = false;
  1125. }
  1126. }
  1127. break;
  1128. }
  1129. if (passItThru)
  1130. {
  1131. m_hasPendingCommand = false;
  1132. SupplyTruckAIUpdate::aiDoCommand(parms);
  1133. }
  1134. }
  1135. // ------------------------------------------------------------------------------------------------
  1136. /** CRC */
  1137. // ------------------------------------------------------------------------------------------------
  1138. void ChinookAIUpdate::crc( Xfer *xfer )
  1139. {
  1140. SupplyTruckAIUpdate::crc(xfer);
  1141. } // end crc
  1142. // ------------------------------------------------------------------------------------------------
  1143. /** Xfer method
  1144. * Version Info:
  1145. * 1: Initial version */
  1146. // ------------------------------------------------------------------------------------------------
  1147. void ChinookAIUpdate::xfer( Xfer *xfer )
  1148. {
  1149. // version
  1150. XferVersion currentVersion = 2;
  1151. XferVersion version = currentVersion;
  1152. xfer->xferVersion( &version, currentVersion );
  1153. // extend base class
  1154. SupplyTruckAIUpdate::xfer(xfer);
  1155. xfer->xferBool(&m_hasPendingCommand);
  1156. if (m_hasPendingCommand) {
  1157. m_pendingCommand.doXfer(xfer);
  1158. }
  1159. xfer->xferUser(&m_flightStatus, sizeof(m_flightStatus));
  1160. xfer->xferObjectID(&m_airfieldForHealing);
  1161. if( version >= 2 )
  1162. {
  1163. xfer->xferCoord3D( &m_originalPos );
  1164. }
  1165. } // end xfer
  1166. // ------------------------------------------------------------------------------------------------
  1167. /** Load post process */
  1168. // ------------------------------------------------------------------------------------------------
  1169. void ChinookAIUpdate::loadPostProcess( void )
  1170. {
  1171. SupplyTruckAIUpdate::loadPostProcess();
  1172. } // end loadPostProcess
  1173. //----------------------------------------------------------------------------------------
  1174. /**
  1175. * Enter idle state.
  1176. */
  1177. void ChinookAIUpdate::privateIdle(CommandSourceType cmdSource)
  1178. {
  1179. // Just an extra step, here, before extending idle to parent classes.
  1180. // Living in you own privateIdle-ho.
  1181. ContainModuleInterface* contain = getObject()->getContain();
  1182. if( contain != NULL )
  1183. {
  1184. Object *rider = (Object*)contain->friend_getRider();
  1185. if ( rider )
  1186. {
  1187. AIUpdateInterface *riderAI = rider->getAIUpdateInterface();
  1188. if( riderAI )
  1189. riderAI->aiIdle( cmdSource );
  1190. }
  1191. }
  1192. SupplyTruckAIUpdate::privateIdle( cmdSource );
  1193. }
  1194. //-------------------------------------------------------------------------------------------------
  1195. /**
  1196. * Attack given object
  1197. */
  1198. void ChinookAIUpdate::privateAttackObject( Object *victim, Int maxShotsToFire, CommandSourceType cmdSource )
  1199. {
  1200. if ( ! getObject()->isKindOf( KINDOF_CAN_ATTACK ) )
  1201. return;
  1202. ContainModuleInterface* contain = getObject()->getContain();
  1203. if( contain != NULL )
  1204. {
  1205. // As an extension of the normal attack, I may want to tell my passengers to attack
  1206. // too, but only if this is a direct command. (As opposed to a passive aquire)
  1207. if( (cmdSource == CMD_FROM_PLAYER || cmdSource == CMD_FROM_SCRIPT) )
  1208. {
  1209. //if ( contain->isPassengerAllowedToFire() )//moved to below
  1210. {
  1211. const ContainedItemsList *passengerList = contain->getContainedItemsList();
  1212. ContainedItemsList::const_iterator passengerIterator;
  1213. passengerIterator = passengerList->begin();
  1214. while( passengerIterator != passengerList->end() )
  1215. {
  1216. Object *passenger = *passengerIterator;
  1217. //Advance to the next iterator
  1218. passengerIterator++;
  1219. if ( ! contain->isPassengerAllowedToFire( passenger->getID() ) )
  1220. continue;
  1221. if ( ! passenger->isKindOf( KINDOF_INFANTRY ))
  1222. continue;
  1223. // If I am an overlord with a gattling upgrade, I do not tell it to fire if it is disabled
  1224. if ( passenger->isKindOf( KINDOF_PORTABLE_STRUCTURE ) )
  1225. {
  1226. if( passenger->isDisabledByType( DISABLED_HACKED )
  1227. || passenger->isDisabledByType( DISABLED_EMP )
  1228. || passenger->isDisabledByType( DISABLED_SUBDUED )
  1229. || passenger->isDisabledByType( DISABLED_PARALYZED) )
  1230. continue;
  1231. }
  1232. AIUpdateInterface *passengerAI = passenger->getAIUpdateInterface();
  1233. if( passengerAI )
  1234. {
  1235. passengerAI->aiAttackObject( victim, maxShotsToFire, cmdSource );
  1236. }
  1237. }
  1238. }
  1239. private___TellPortableStructureToAttackWithMe( victim, maxShotsToFire, cmdSource );
  1240. }
  1241. }
  1242. AIUpdateInterface::privateAttackObject( victim, maxShotsToFire, cmdSource );
  1243. }
  1244. void ChinookAIUpdate::private___TellPortableStructureToAttackWithMe( Object *victim, Int maxShotsToFire, CommandSourceType cmdSource )
  1245. {
  1246. ContainModuleInterface* contain = getObject()->getContain();
  1247. if( contain != NULL )
  1248. {
  1249. //--------- THE GATTLING UPGRADE OR THE GUYS IN THE BUNKER_NOT_A_BUNKER-------------
  1250. Object *rider = (Object*)contain->friend_getRider();
  1251. if ( rider
  1252. && rider->isKindOf( KINDOF_PORTABLE_STRUCTURE )
  1253. && !rider->isDisabledByType( DISABLED_HACKED )
  1254. && !rider->isDisabledByType( DISABLED_EMP )
  1255. && !rider->isDisabledByType( DISABLED_SUBDUED )
  1256. && !rider->isDisabledByType( DISABLED_PARALYZED) )
  1257. {
  1258. AIUpdateInterface *riderAI = rider->getAIUpdateInterface();
  1259. if( riderAI )
  1260. {
  1261. riderAI->aiAttackObject( victim, maxShotsToFire, cmdSource );
  1262. }
  1263. }
  1264. }
  1265. }
  1266. //-------------------------------------------------------------------------------------------------
  1267. /**
  1268. * Attack given object
  1269. */
  1270. void ChinookAIUpdate::privateForceAttackObject( Object *victim, Int maxShotsToFire, CommandSourceType cmdSource )
  1271. {
  1272. if ( ! getObject()->isKindOf( KINDOF_CAN_ATTACK ) )
  1273. return;
  1274. ContainModuleInterface* contain = getObject()->getContain();
  1275. if( contain != NULL )
  1276. {
  1277. // As an extension of the normal attack, I may want to tell my passengers to attack
  1278. // too, but only if this is a direct command. (As opposed to a passive aquire)
  1279. if( (cmdSource == CMD_FROM_PLAYER || cmdSource == CMD_FROM_SCRIPT) )
  1280. {
  1281. // if ( contain->isPassengerAllowedToFire() )
  1282. {
  1283. const ContainedItemsList *passengerList = contain->getContainedItemsList();
  1284. ContainedItemsList::const_iterator passengerIterator;
  1285. passengerIterator = passengerList->begin();
  1286. while( passengerIterator != passengerList->end() )
  1287. {
  1288. Object *passenger = *passengerIterator;
  1289. //Advance to the next iterator
  1290. passengerIterator++;
  1291. if ( ! contain->isPassengerAllowedToFire( passenger->getID() ) )
  1292. continue;
  1293. if ( ! passenger->isKindOf( KINDOF_INFANTRY ))
  1294. continue;
  1295. // If I am an overlord with a gattling upgrade, I do not tell it to fire if it is disabled
  1296. if ( passenger->isKindOf( KINDOF_PORTABLE_STRUCTURE ) )
  1297. {
  1298. if( passenger->isDisabledByType( DISABLED_HACKED )
  1299. || passenger->isDisabledByType( DISABLED_EMP )
  1300. || passenger->isDisabledByType( DISABLED_SUBDUED )
  1301. || passenger->isDisabledByType( DISABLED_PARALYZED) )
  1302. continue;
  1303. }
  1304. AIUpdateInterface *passengerAI = passenger->getAIUpdateInterface();
  1305. if( passengerAI )
  1306. {
  1307. passengerAI->aiForceAttackObject( victim, maxShotsToFire, cmdSource );
  1308. }
  1309. }
  1310. }
  1311. //--------- THE GATTLING UPGRADE OR THE GUYS IN THE BUNKER_NOT_A_BUNKER-------------
  1312. Object *rider = (Object*)contain->friend_getRider();
  1313. if ( rider
  1314. && rider->isKindOf( KINDOF_PORTABLE_STRUCTURE )
  1315. && !rider->isDisabledByType( DISABLED_HACKED )
  1316. && !rider->isDisabledByType( DISABLED_EMP )
  1317. && !rider->isDisabledByType( DISABLED_SUBDUED )
  1318. && !rider->isDisabledByType( DISABLED_PARALYZED) )
  1319. {
  1320. AIUpdateInterface *riderAI = rider->getAIUpdateInterface();
  1321. if( riderAI )
  1322. {
  1323. riderAI->aiForceAttackObject( victim, maxShotsToFire, cmdSource );
  1324. }
  1325. }
  1326. }
  1327. }
  1328. AIUpdateInterface::privateForceAttackObject( victim, maxShotsToFire, cmdSource );
  1329. }
  1330. //-------------------------------------------------------------------------------------------------
  1331. /**
  1332. * Attack given position
  1333. */
  1334. void ChinookAIUpdate::privateAttackPosition( const Coord3D *pos, Int maxShotsToFire, CommandSourceType cmdSource )
  1335. {
  1336. if ( ! getObject()->isKindOf( KINDOF_CAN_ATTACK ) )
  1337. return;
  1338. ContainModuleInterface* contain = getObject()->getContain();
  1339. if( contain != NULL )
  1340. {
  1341. // As an extension of the normal attack, I may want to tell my passengers to attack
  1342. // too, but only if this is a direct command. (As opposed to a passive aquire)
  1343. if( (cmdSource == CMD_FROM_PLAYER || cmdSource == CMD_FROM_SCRIPT) )
  1344. {
  1345. //if ( contain->isPassengerAllowedToFire() )
  1346. {
  1347. const ContainedItemsList *passengerList = contain->getContainedItemsList();
  1348. ContainedItemsList::const_iterator passengerIterator;
  1349. passengerIterator = passengerList->begin();
  1350. while( passengerIterator != passengerList->end() )
  1351. {
  1352. Object *passenger = *passengerIterator;
  1353. //Advance to the next iterator
  1354. passengerIterator++;
  1355. if ( ! contain->isPassengerAllowedToFire( passenger->getID() ) )
  1356. continue;
  1357. if ( ! passenger->isKindOf( KINDOF_INFANTRY ))
  1358. continue;
  1359. // If I am an overlord with a gattling upgrade, I do not tell it ti fire if it is disabled
  1360. if ( passenger->isKindOf( KINDOF_PORTABLE_STRUCTURE ) )
  1361. {
  1362. if( passenger->isDisabledByType( DISABLED_HACKED )
  1363. || passenger->isDisabledByType( DISABLED_EMP)
  1364. || passenger->isDisabledByType( DISABLED_SUBDUED)
  1365. || passenger->isDisabledByType( DISABLED_PARALYZED) )
  1366. continue;
  1367. }
  1368. AIUpdateInterface *passengerAI = passenger->getAIUpdateInterface();
  1369. if( passengerAI )
  1370. {
  1371. passengerAI->aiAttackPosition( pos, maxShotsToFire, cmdSource );
  1372. }
  1373. }
  1374. }
  1375. //--------- THE GATTLING UPGRADE OR THE GUYS IN THE BUNKER_NOT_A_BUNKER-------------
  1376. Object *rider = (Object*)contain->friend_getRider();
  1377. if ( rider
  1378. && rider->isKindOf( KINDOF_PORTABLE_STRUCTURE )
  1379. && !rider->isDisabledByType( DISABLED_HACKED )
  1380. && !rider->isDisabledByType( DISABLED_EMP )
  1381. && !rider->isDisabledByType( DISABLED_SUBDUED )
  1382. && !rider->isDisabledByType( DISABLED_PARALYZED) )
  1383. {
  1384. AIUpdateInterface *riderAI = rider->getAIUpdateInterface();
  1385. if( riderAI )
  1386. {
  1387. riderAI->aiAttackPosition( pos, maxShotsToFire, cmdSource );
  1388. }
  1389. }
  1390. }
  1391. }
  1392. AIUpdateInterface::privateAttackPosition( pos, maxShotsToFire, cmdSource );
  1393. }
  1394. //------------------------------------------------------------------------------------------------
  1395. Int ChinookAIUpdate::getUpgradedSupplyBoost() const
  1396. {
  1397. Player *player = getObject()->getControllingPlayer();
  1398. static const UpgradeTemplate *supplyLinesTemplate = TheUpgradeCenter->findUpgrade( "Upgrade_AmericaSupplyLines" );
  1399. if (player && supplyLinesTemplate && player->hasUpgradeComplete(supplyLinesTemplate))
  1400. return getChinookAIUpdateModuleData()->m_upgradedSupplyBoost;
  1401. else
  1402. return 0;
  1403. }