DeliverPayloadAIUpdate.cpp 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246
  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. // DeliverPayloadAIUpdate.cpp ////////////
  24. // Author: Graham Smallwood, March 2002
  25. // Desc: State machine that controls the approach and deployment of airborne cargo
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #define DEFINE_WEAPONSLOTTYPE_NAMES
  28. #include "Common/Player.h"
  29. #include "Common/RandomValue.h"
  30. #include "Common/ThingFactory.h"
  31. #include "Common/ThingTemplate.h"
  32. #include "Common/Xfer.h"
  33. #include "GameClient/Drawable.h"
  34. #include "GameClient/FXList.h"
  35. #include "GameClient/InGameUI.h"
  36. #include "GameLogic/Locomotor.h"
  37. #include "GameLogic/Module/SmartBombTargetHomingUpdate.h"
  38. #include "GameLogic/Module/GenerateMinefieldBehavior.h"
  39. #include "GameLogic/Module/DeliverPayloadAIUpdate.h"
  40. #include "GameLogic/Module/ParachuteContain.h"
  41. #include "GameLogic/Module/ContainModule.h"
  42. #include "GameLogic/Module/PhysicsUpdate.h"
  43. #include "GameLogic/Module/BodyModule.h"
  44. #include "GameLogic/Object.h"
  45. #include "GameLogic/PartitionManager.h"
  46. #include "GameLogic/Weapon.h"
  47. #include "GameLogic/WeaponSet.h"
  48. #ifdef _INTERNAL
  49. // for occasional debugging...
  50. //#pragma optimize("", off)
  51. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  52. #endif
  53. //-------------------------------------------------------------------------------------------------
  54. const FieldParse* DeliverPayloadData::getFieldParse()
  55. {
  56. static const FieldParse dataFieldParse[] =
  57. {
  58. { "DeliveryDistance", INI::parseReal, NULL, offsetof( DeliverPayloadData, m_distToTarget) },
  59. { "PreOpenDistance", INI::parseReal, NULL, offsetof( DeliverPayloadData, m_preOpenDistance) },
  60. { "MaxAttempts", INI::parseInt, NULL, offsetof( DeliverPayloadData, m_maxAttempts) },
  61. //Drop information
  62. { "DropDelay", INI::parseDurationUnsignedInt,NULL, offsetof( DeliverPayloadData, m_dropDelay ) },
  63. { "DropOffset", INI::parseCoord3D, NULL, offsetof( DeliverPayloadData, m_dropOffset ) },
  64. { "DropVariance", INI::parseCoord3D, NULL, offsetof( DeliverPayloadData, m_dropVariance ) },
  65. { "InheritTransportVelocity", INI::parseBool, NULL, offsetof( DeliverPayloadData, m_inheritTransportVelocity ) },
  66. { "ExitPitchRate", INI::parseAngularVelocityReal,NULL, offsetof( DeliverPayloadData, m_exitPitchRate ) },
  67. { "ParachuteDirectly", INI::parseBool, NULL, offsetof( DeliverPayloadData, m_isParachuteDirectly) },
  68. //Visible payload information (payload assumed to be show visibly and it's created only when dropped)
  69. { "VisibleItemsDroppedPerInterval", INI::parseInt, NULL, offsetof( DeliverPayloadData, m_visibleItemsDroppedPerInterval ) },
  70. { "VisibleDropBoneBaseName", INI::parseAsciiString, NULL, offsetof( DeliverPayloadData, m_visibleDropBoneName ) },
  71. { "VisibleSubObjectBaseName", INI::parseAsciiString, NULL, offsetof( DeliverPayloadData, m_visibleSubObjectName ) },
  72. { "VisibleNumBones", INI::parseInt, NULL, offsetof( DeliverPayloadData, m_visibleNumBones ) },
  73. { "VisiblePayloadTemplateName", INI::parseAsciiString, NULL, offsetof( DeliverPayloadData, m_visiblePayloadTemplateName ) },
  74. { "VisiblePayloadWeaponTemplate", INI::parseWeaponTemplate, NULL, offsetof( DeliverPayloadData, m_visiblePayloadWeaponTemplate ) },
  75. { "SelfDestructObject", INI::parseBool, NULL, offsetof( DeliverPayloadData, m_selfDestructObject ) },
  76. //Weapon based payload
  77. { "FireWeapon", INI::parseBool, NULL, offsetof( DeliverPayloadData, m_fireWeapon ) },
  78. //Specify an additional weaponslot to be fired while strafing
  79. { "DiveStartDistance", INI::parseReal, NULL, offsetof( DeliverPayloadData, m_diveStartDistance ) },
  80. { "DiveEndDistance", INI::parseReal, NULL, offsetof( DeliverPayloadData, m_diveEndDistance ) },
  81. { "StrafingWeaponSlot", INI::parseLookupList, TheWeaponSlotTypeNamesLookupList, offsetof( DeliverPayloadData, m_strafingWeaponSlot ) },
  82. { "StrafeWeaponFX", INI::parseFXList, NULL, offsetof( DeliverPayloadData, m_strafeFX ) },
  83. { "StrafeLength", INI::parseReal, NULL, offsetof( DeliverPayloadData, m_strafeLength ) },
  84. { "DeliveryDecal", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( DeliverPayloadData, m_deliveryDecalTemplate ) },
  85. { "DeliveryDecalRadius", INI::parseReal, NULL, offsetof(DeliverPayloadData, m_deliveryDecalRadius) },
  86. { 0, 0, 0, 0 }
  87. };
  88. return dataFieldParse;
  89. }
  90. //-------------------------------------------------------------------------------------------------
  91. //-------------------------------------------------------------------------------------------------
  92. //-------------------------------------------------------------------------------------------------
  93. //-------------------------------------------------------------------------------------------------
  94. AIStateMachine* DeliverPayloadAIUpdate::makeStateMachine()
  95. {
  96. return newInstance(AIStateMachine)( getObject(), "DeliverPayloadBasicAI");
  97. }
  98. //-------------------------------------------------------------------------------------------------
  99. DeliverPayloadAIUpdate::DeliverPayloadAIUpdate( Thing *thing, const ModuleData* moduleData ) : AIUpdateInterface( thing, moduleData )
  100. {
  101. m_deliverPayloadStateMachine = NULL;
  102. m_targetPos.zero();
  103. m_moveToPos.zero();
  104. m_visibleItemsDelivered = 0;
  105. m_deliveryDecal.clear();
  106. m_previousDistanceSqr = 0;
  107. m_freeToExit = FALSE;
  108. m_acceptingCommands = TRUE;
  109. // Added By Sadullah Nader
  110. // Initialization missing and needed
  111. m_diveState = DIVESTATE_PREDIVE;
  112. // End Add
  113. }
  114. //-------------------------------------------------------------------------------------------------
  115. DeliverPayloadAIUpdate::~DeliverPayloadAIUpdate( void )
  116. {
  117. m_deliveryDecal.clear();
  118. if (m_deliverPayloadStateMachine)
  119. m_deliverPayloadStateMachine->deleteInstance();
  120. }
  121. //-------------------------------------------------------------------------------------------------
  122. AIFreeToExitType DeliverPayloadAIUpdate::getAiFreeToExit(const Object* exiter) const
  123. {
  124. if( getObject()->isEffectivelyDead() )
  125. return NOT_FREE_TO_EXIT;
  126. return m_freeToExit ? FREE_TO_EXIT : NOT_FREE_TO_EXIT;
  127. }
  128. //-------------------------------------------------------------------------------------------------
  129. void DeliverPayloadAIUpdate::killDeliveryDecal()
  130. {
  131. m_deliveryDecal.clear();
  132. }
  133. //-------------------------------------------------------------------------------------------------
  134. Bool DeliverPayloadAIUpdate::isAllowedToRespondToAiCommands(const AICommandParms* parms) const
  135. {
  136. if (!m_acceptingCommands)
  137. {
  138. return false;
  139. }
  140. return AIUpdateInterface::isAllowedToRespondToAiCommands(parms);
  141. }
  142. //-------------------------------------------------------------------------------------------------
  143. UpdateSleepTime DeliverPayloadAIUpdate::update( void )
  144. {
  145. m_deliveryDecal.update();
  146. if(!(isAiInDeadState()) && m_deliverPayloadStateMachine)
  147. m_deliverPayloadStateMachine->updateStateMachine();
  148. //Handling diving logic (regardless of state)
  149. if( m_diveState != DIVESTATE_POSTDIVE )
  150. {
  151. if( m_diveState == DIVESTATE_PREDIVE )
  152. {
  153. //Check to see if we are close enough to start diving.
  154. Real startDiveDistanceSquared = sqr( getData()->m_diveStartDistance );
  155. Real currentDistanceSquared = ThePartitionManager->getDistanceSquared( getObject(), getTargetPos(), FROM_CENTER_2D );
  156. if( currentDistanceSquared <= startDiveDistanceSquared )
  157. {
  158. m_diveState = DIVESTATE_DIVING;
  159. getObject()->getAIUpdateInterface()->getCurLocomotor()->setUsePreciseZPos( true );
  160. AudioEventRTS soundDive = *(getObject()->getTemplate()->getPerUnitSound("StartDive"));
  161. if( soundDive.getEventName().isNotEmpty() )
  162. {
  163. soundDive.setPosition( getObject()->getPosition() );
  164. TheAudio->addAudioEvent( &soundDive );
  165. }
  166. }
  167. }
  168. else
  169. {
  170. //Check to see when we shall end diving
  171. Real endDiveDistanceSquared = sqr( getData()->m_diveEndDistance );
  172. Real currentDistanceSquared = ThePartitionManager->getDistanceSquared( getObject(), getTargetPos(), FROM_CENTER_3D );
  173. if( currentDistanceSquared <= endDiveDistanceSquared )
  174. {
  175. m_diveState = DIVESTATE_POSTDIVE;
  176. getObject()->getAIUpdateInterface()->getCurLocomotor()->setUsePreciseZPos( false );
  177. }
  178. if( m_data.m_strafingWeaponSlot != -1 )
  179. {
  180. //SHOOT!!
  181. const Coord3D *velocity = getObject()->getPhysics()->getVelocity();
  182. if( velocity->z < 5.0f )
  183. {
  184. //Calc strafe ratio
  185. Real startDiveDistance = getData()->m_diveStartDistance;
  186. Real endDiveDistance = sqrt( endDiveDistanceSquared );
  187. Real currentDistance = sqrt( currentDistanceSquared );
  188. Real diveRatio = (startDiveDistance - currentDistance) / (startDiveDistance - endDiveDistance);
  189. Coord3D velocity = *getObject()->getPhysics()->getVelocity();
  190. velocity.z = 0.0f;
  191. velocity.normalize();
  192. velocity.scale( diveRatio * 100.0f );
  193. Coord3D backwards = velocity;
  194. backwards.scale( 0.33f );
  195. Coord3D strafePoint = *getTargetPos();
  196. strafePoint.sub( &backwards );
  197. strafePoint.add( &velocity );
  198. strafePoint.z = TheTerrainLogic->getGroundHeight( strafePoint.x, strafePoint.y );
  199. // lock it just till the weapon is empty or the attack is "done"
  200. getObject()->setWeaponLock( m_data.m_strafingWeaponSlot, LOCKED_TEMPORARILY );
  201. getObject()->fireCurrentWeapon( &strafePoint );
  202. FXList::doFXPos( m_data.m_strafeFX, &strafePoint );
  203. }
  204. }
  205. }
  206. }
  207. AIUpdateInterface::update();
  208. return UPDATE_SLEEP_NONE; // ignore our parent, and never sleep
  209. }
  210. //-------------------------------------------------------------------------------------------------
  211. void DeliverPayloadAIUpdate::deliverPayload(
  212. const Coord3D *moveToPos,
  213. const Coord3D *targetPos,
  214. const DeliverPayloadData *data
  215. )
  216. {
  217. //****************************************************
  218. //THIS GETS CALLED VIA OBJECT CREATION LISTS ONLY!!!
  219. //****************************************************
  220. if (m_deliverPayloadStateMachine)
  221. m_deliverPayloadStateMachine->deleteInstance();
  222. m_deliverPayloadStateMachine = NULL;
  223. m_moveToPos = *moveToPos;
  224. m_targetPos = *targetPos;
  225. m_data = *data;
  226. m_deliveryDecal.clear();
  227. m_data.m_deliveryDecalTemplate.createRadiusDecal(*targetPos,
  228. m_data.m_deliveryDecalRadius, getObject()->getControllingPlayer(), m_deliveryDecal);
  229. if( m_data.m_diveStartDistance <= 0.0f )
  230. {
  231. //We can't dive, so setting it to post-dive will prevent
  232. //dive logic from being processed.
  233. m_diveState = DIVESTATE_POSTDIVE;
  234. }
  235. else
  236. {
  237. m_diveState = DIVESTATE_PREDIVE;
  238. }
  239. m_visibleItemsDelivered = 0;
  240. //If we have visible bones, then show each of those subobjects.
  241. Drawable *draw = getObject()->getDrawable();
  242. if( draw )
  243. {
  244. for( int i = 1; i <= m_data.m_visibleNumBones; i++ )
  245. {
  246. if( m_data.m_visibleSubObjectName.isNotEmpty() )
  247. {
  248. AsciiString name;
  249. name.format( "%s%02d", m_data.m_visibleSubObjectName.str(), i );
  250. draw->showSubObject( name, true );
  251. }
  252. }
  253. }
  254. // must make the state machine AFTER initing the other stuff, since it may inquire of its values...
  255. m_deliverPayloadStateMachine = newInstance(DeliverPayloadStateMachine)( getObject() );
  256. m_deliverPayloadStateMachine->initDefaultState();
  257. #ifdef _DEBUG
  258. m_deliverPayloadStateMachine->setName("DeliverPayloadSpecificAI");
  259. #endif
  260. }
  261. //-------------------------------------------------------------------------------------------------
  262. void DeliverPayloadAIUpdate::deliverPayloadViaModuleData( const Coord3D *moveToPos )
  263. {
  264. const DeliverPayloadAIUpdateModuleData *data = getDeliverPayloadAIUpdateModuleData();
  265. //****************************************************
  266. //THIS GETS CALLED FOR SCRIPTED REINFORCEMENTS ONLY!!!
  267. //These don't and can't use object creation lists.
  268. //****************************************************
  269. DeliverPayloadData dpData;
  270. dpData.m_dropOffset.set( &data->m_dropOffset );
  271. dpData.m_dropVariance.set( &data->m_dropVariance );
  272. dpData.m_distToTarget = data->m_maxDistanceToTarget;
  273. dpData.m_maxAttempts = data->m_maxNumberAttempts;
  274. dpData.m_dropDelay = data->m_dropDelay;
  275. dpData.m_deliveryDecalTemplate = data->m_deliveryDecalTemplate;
  276. dpData.m_deliveryDecalRadius = data->m_deliveryDecalRadius;
  277. deliverPayload( moveToPos, moveToPos, &dpData );
  278. }
  279. //-------------------------------------------------------------------------------------------------
  280. const ThingTemplate* DeliverPayloadAIUpdate::getPutInContainerTemplateViaModuleData() const
  281. {
  282. AsciiString name = getDeliverPayloadAIUpdateModuleData()->m_putInContainerName;
  283. return name.isEmpty() ? NULL : TheThingFactory->findTemplate( name );
  284. }
  285. //-------------------------------------------------------------------------------------------------
  286. Real DeliverPayloadAIUpdate::calcMinTurnRadius(Real* timeToTravelThatDist) const
  287. {
  288. const Locomotor* loco = getCurLocomotor();
  289. BodyDamageType bdt = getObject()->getBodyModule()->getDamageState();
  290. /// @todo srj -- this should probably use min-speed, not max-speed... fix after E3
  291. Real maxSpeed = loco->getMaxSpeedForCondition(bdt); // in dist/frame
  292. Real maxTurnRate = loco->getMaxTurnRate(bdt); // in rads/frame
  293. /*
  294. our minimum circumference will be like so:
  295. Real minTurnCircum = maxSpeed * (2*PI / maxTurnRate);
  296. so therefore our minimum turn radius is:
  297. Real minTurnRadius = minTurnCircum / 2*PI;
  298. so we just eliminate the middleman:
  299. */
  300. Real minTurnRadius = (maxTurnRate > 0.0f) ? (maxSpeed / maxTurnRate) : 999999.0f;
  301. if (timeToTravelThatDist)
  302. *timeToTravelThatDist = minTurnRadius / maxSpeed;
  303. return minTurnRadius;
  304. }
  305. //-------------------------------------------------------------------------------------------------
  306. Bool DeliverPayloadAIUpdate::isCloseEnoughToTarget()
  307. {
  308. // In addition to testing distance, it is also sensitive to being in/outward bound
  309. ////The new getPreOpenDistance() allows the deliver state to fire early, but only if inbound,
  310. ////so the doors can open and payload can get ready...
  311. Real allowedDistanceSqr = sqr( getAllowedDistanceToTarget() );
  312. Real currentDistanceSqr = ThePartitionManager->getDistanceSquared( getObject(), getTargetPos(), FROM_CENTER_2D );
  313. Bool inBound = m_previousDistanceSqr > currentDistanceSqr;
  314. m_previousDistanceSqr = currentDistanceSqr;// for the next test
  315. if ( inBound )
  316. allowedDistanceSqr = sqr(getAllowedDistanceToTarget() + getPreOpenDistance());
  317. //DEBUG_LOG(("Dist to target is %f (allowed %f)\n",sqrt(currentDistanceSqr),sqrt(allowedDistanceSqr)));
  318. if ( allowedDistanceSqr > currentDistanceSqr )
  319. return TRUE;
  320. return FALSE;
  321. }
  322. //-------------------------------------------------------------------------------------------------
  323. Bool DeliverPayloadAIUpdate::isOffMap() const
  324. {
  325. Region3D mapRegion;
  326. TheTerrainLogic->getExtentIncludingBorder( &mapRegion );
  327. if (!mapRegion.isInRegionNoZ( getObject()->getPosition() ))
  328. return true;
  329. return false;
  330. }
  331. // ------------------------------------------------------------------------------------------------
  332. /** CRC */
  333. // ------------------------------------------------------------------------------------------------
  334. void DeliverPayloadAIUpdate::crc( Xfer *xfer )
  335. {
  336. // extend base class
  337. AIUpdateInterface::crc(xfer);
  338. } // end crc
  339. // ------------------------------------------------------------------------------------------------
  340. /** Xfer method
  341. * Version Info:
  342. * 1: Initial version
  343. * 5: Whoops version. preOpenDistance just wasn't saved.
  344. */
  345. // ------------------------------------------------------------------------------------------------
  346. void DeliverPayloadAIUpdate::xfer( Xfer *xfer )
  347. {
  348. // version
  349. const XferVersion currentVersion = 5;
  350. XferVersion version = currentVersion;
  351. xfer->xferVersion( &version, currentVersion );
  352. // extend base class
  353. AIUpdateInterface::xfer(xfer);
  354. xfer->xferCoord3D(&m_targetPos);
  355. xfer->xferCoord3D(&m_moveToPos);
  356. xfer->xferInt(&m_visibleItemsDelivered);
  357. xfer->xferUser(&m_diveState, sizeof(m_diveState));
  358. DeliverPayloadData data = m_data;
  359. xfer->xferAsciiString(&data.m_visibleDropBoneName);
  360. xfer->xferAsciiString(&data.m_visibleSubObjectName);
  361. xfer->xferAsciiString(&data.m_visiblePayloadTemplateName);
  362. xfer->xferReal(&data.m_distToTarget);
  363. if( version >= 5 )
  364. xfer->xferReal(&data.m_preOpenDistance);
  365. xfer->xferInt(&data.m_maxAttempts);
  366. xfer->xferCoord3D(&data.m_dropOffset);
  367. xfer->xferCoord3D(&data.m_dropVariance);
  368. xfer->xferUnsignedInt(&data.m_dropDelay);
  369. xfer->xferBool(&data.m_fireWeapon);
  370. xfer->xferBool(&data.m_selfDestructObject);
  371. xfer->xferInt(&data.m_visibleNumBones);
  372. xfer->xferReal(&data.m_diveStartDistance);
  373. xfer->xferReal(&data.m_diveEndDistance);
  374. xfer->xferUser(&data.m_strafingWeaponSlot, sizeof(data.m_strafingWeaponSlot));
  375. xfer->xferInt(&data.m_visibleItemsDroppedPerInterval);
  376. xfer->xferBool(&data.m_inheritTransportVelocity);
  377. xfer->xferBool(&data.m_isParachuteDirectly);
  378. xfer->xferReal(&data.m_exitPitchRate);
  379. // const FXList *m_strafeFX;
  380. xfer->xferReal(&data.m_strafeLength);
  381. AsciiString weaponTemplateName;
  382. if (data.m_visiblePayloadWeaponTemplate)
  383. {
  384. weaponTemplateName = data.m_visiblePayloadWeaponTemplate->getName();
  385. }
  386. xfer->xferAsciiString(&weaponTemplateName);
  387. if( xfer->getXferMode() == XFER_LOAD && weaponTemplateName.isNotEmpty())
  388. {
  389. data.m_visiblePayloadWeaponTemplate = TheWeaponStore->findWeaponTemplate(weaponTemplateName);
  390. }
  391. data.m_deliveryDecalTemplate.xferRadiusDecalTemplate(xfer);
  392. xfer->xferReal(&data.m_deliveryDecalRadius);
  393. *((DeliverPayloadData*)&m_data) = data;
  394. Bool hasStateMachine = m_deliverPayloadStateMachine!=NULL;
  395. xfer->xferBool(&hasStateMachine);
  396. if (hasStateMachine && m_deliverPayloadStateMachine==NULL)
  397. {
  398. m_deliverPayloadStateMachine = newInstance(DeliverPayloadStateMachine)( getObject() );
  399. }
  400. if (m_deliverPayloadStateMachine && version >= 2)
  401. {
  402. xfer->xferSnapshot(m_deliverPayloadStateMachine);
  403. }
  404. m_deliveryDecal.xferRadiusDecal(xfer);
  405. if (version >= 2)
  406. {
  407. xfer->xferBool(&m_freeToExit);
  408. }
  409. if (version >= 3)
  410. {
  411. xfer->xferBool(&m_acceptingCommands);
  412. }
  413. if (version >= 4)
  414. {
  415. xfer->xferReal(&m_previousDistanceSqr);
  416. }
  417. } // end xfer
  418. // ------------------------------------------------------------------------------------------------
  419. /** Load post process */
  420. // ------------------------------------------------------------------------------------------------
  421. void DeliverPayloadAIUpdate::loadPostProcess( void )
  422. {
  423. // extend base class
  424. AIUpdateInterface::loadPostProcess();
  425. } // end loadPostProcess
  426. //-------------------------------------------------------------------------------------------------
  427. //-------------------------------------------------------------------------------------------------
  428. //-------------------------------------------------------------------------------------------------
  429. //-------------------------------------------------------------------------------------------------
  430. DeliverPayloadStateMachine::DeliverPayloadStateMachine( Object *owner ) : StateMachine( owner, "DeliverPayloadStateMachine" )
  431. {
  432. // DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  433. static const StateConditionInfo considerConditions[] =
  434. {
  435. StateConditionInfo(DeliverPayloadStateMachine::isOffMap, RECOVER_FROM_OFF_MAP, NULL),
  436. StateConditionInfo(NULL, NULL, NULL) // keep last
  437. };
  438. // order matters: first state is the default state.
  439. defineState( APPROACH, newInstance(ApproachState)( this ), DELIVERING, CONSIDER_NEW_APPROACH );
  440. defineState( DELIVERING, newInstance(DeliveringState)( this ), HEAD_OFF_MAP, CONSIDER_NEW_APPROACH );
  441. defineState( CONSIDER_NEW_APPROACH, newInstance(ConsiderNewApproachState)( this ), APPROACH, HEAD_OFF_MAP, considerConditions );
  442. defineState( RECOVER_FROM_OFF_MAP, newInstance(RecoverFromOffMapState)( this ), APPROACH, APPROACH );
  443. defineState( HEAD_OFF_MAP, newInstance(HeadOffMapState)( this ), CLEAN_UP, CLEAN_UP );
  444. defineState( CLEAN_UP, newInstance(CleanUpState)( this ), INVALID_STATE_ID, INVALID_STATE_ID );
  445. }
  446. //-------------------------------------------------------------------------------------------------
  447. DeliverPayloadStateMachine::~DeliverPayloadStateMachine()
  448. {
  449. }
  450. // ------------------------------------------------------------------------------------------------
  451. /** CRC */
  452. // ------------------------------------------------------------------------------------------------
  453. void DeliverPayloadStateMachine::crc( Xfer *xfer )
  454. {
  455. StateMachine::crc(xfer);
  456. } // end crc
  457. // ------------------------------------------------------------------------------------------------
  458. /** Xfer Method */
  459. // ------------------------------------------------------------------------------------------------
  460. void DeliverPayloadStateMachine::xfer( Xfer *xfer )
  461. {
  462. XferVersion cv = 1;
  463. XferVersion v = cv;
  464. xfer->xferVersion( &v, cv );
  465. StateMachine::xfer(xfer);
  466. } // end xfer
  467. // ------------------------------------------------------------------------------------------------
  468. /** Load post process */
  469. // ------------------------------------------------------------------------------------------------
  470. void DeliverPayloadStateMachine::loadPostProcess( void )
  471. {
  472. StateMachine::loadPostProcess();
  473. } // end loadPostProcess
  474. //-------------------------------------------------------------------------------------------------
  475. /*static*/ Bool DeliverPayloadStateMachine::isOffMap( State *thisState, void* userData )
  476. {
  477. Object *owner = thisState->getMachineOwner();
  478. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  479. if( !ai )
  480. {
  481. return TRUE;
  482. }
  483. return ai->isOffMap();
  484. }
  485. //-------------------------------------------------------------------------------------------------
  486. //-------------------------------------------------------------------------------------------------
  487. //-------------------------------------------------------------------------------------------------
  488. //-------------------------------------------------------------------------------------------------
  489. StateReturnType ApproachState::onEnter() // Give the move command
  490. {
  491. Object *owner = getMachineOwner();
  492. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  493. if( !ai )
  494. {
  495. return STATE_FAILURE;
  496. }
  497. if (ai->isAiInDeadState())
  498. {
  499. return STATE_FAILURE;
  500. }
  501. ai->aiMoveToPosition( ai->getMoveToPos(), CMD_FROM_AI );
  502. return STATE_CONTINUE;
  503. }
  504. //-------------------------------------------------------------------------------------------------
  505. StateReturnType ApproachState::update()
  506. {
  507. Object *owner = getMachineOwner();
  508. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  509. if( !ai )
  510. {
  511. return STATE_FAILURE;
  512. }
  513. if (ai->isAiInDeadState())
  514. {
  515. return STATE_FAILURE;
  516. }
  517. if (ai->isCloseEnoughToTarget())
  518. return STATE_SUCCESS;
  519. if (ai->getAIStateType() != AI_MOVE_TO)
  520. {
  521. if ( ai->getAIStateType() == AI_IDLE )
  522. // Because something outside us has told us to IDLE, so geez, ignore it and try our approach again
  523. return STATE_FAILURE;
  524. else
  525. {
  526. DEBUG_CRASH(("hmm, bailed from moveto state early... should this be possible?"));
  527. ai->aiMoveToPosition( ai->getMoveToPos(), CMD_FROM_AI );
  528. }
  529. }
  530. return STATE_CONTINUE;
  531. }
  532. //-------------------------------------------------------------------------------------------------
  533. //-------------------------------------------------------------------------------------------------
  534. //-------------------------------------------------------------------------------------------------
  535. // ------------------------------------------------------------------------------------------------
  536. /** CRC */
  537. // ------------------------------------------------------------------------------------------------
  538. void DeliveringState::crc( Xfer *xfer )
  539. {
  540. } // end crc
  541. // ------------------------------------------------------------------------------------------------
  542. /** Xfer Method */
  543. // ------------------------------------------------------------------------------------------------
  544. void DeliveringState::xfer( Xfer *xfer )
  545. {
  546. // version
  547. XferVersion currentVersion = 1;
  548. XferVersion version = currentVersion;
  549. xfer->xferVersion( &version, currentVersion );
  550. xfer->xferUnsignedInt(&m_dropDelayLeft);
  551. xfer->xferBool(&m_didOpen);
  552. } // end xfer
  553. // ------------------------------------------------------------------------------------------------
  554. /** Load post process */
  555. // ------------------------------------------------------------------------------------------------
  556. void DeliveringState::loadPostProcess( void )
  557. {
  558. } // end loadPostProcess
  559. //-------------------------------------------------------------------------------------------------
  560. StateReturnType DeliveringState::onEnter() // Open the pod bay doors, Hal
  561. {
  562. Object *owner = getMachineOwner();
  563. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  564. if( !ai )
  565. {
  566. return STATE_FAILURE;
  567. }
  568. /// @todo srj -- for now, this assumes at most one door
  569. owner->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_CLOSING, MODELCONDITION_DOOR_1_OPENING );
  570. m_dropDelayLeft = ai->getDoorDelay();
  571. m_didOpen = false;
  572. return STATE_CONTINUE;
  573. }
  574. //-------------------------------------------------------------------------------------------------
  575. StateReturnType DeliveringState::update() // Kick a dude out every so often
  576. {
  577. if (m_dropDelayLeft > 0)
  578. {
  579. --m_dropDelayLeft;
  580. return STATE_CONTINUE;
  581. }
  582. Object *owner = getMachineOwner();
  583. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  584. if( !ai )
  585. {
  586. return STATE_FAILURE;
  587. }
  588. ai->friend_setFreeToExit(true);
  589. m_didOpen = true;
  590. m_dropDelayLeft = ai->getDropDelay();
  591. if (!ai->isCloseEnoughToTarget())
  592. return STATE_FAILURE;
  593. const ContainedItemsList* items = owner->getContain() ? owner->getContain()->getContainedItemsList() : NULL;
  594. if( (!items || !items->size()) && ai->getVisibleItemsDelivered() == ai->getData()->m_visibleNumBones )
  595. {
  596. //We are out of payload to drop AND our visible payload is empty. It's possible for deliverers to
  597. //have one or the other or even both.
  598. return STATE_SUCCESS;
  599. }
  600. //Handle contained payload delivery.
  601. if( items )
  602. {
  603. Object* item = items->front();
  604. if( ai->isFireWeapon() )
  605. {
  606. Coord3D pos = *ai->getTargetPos();
  607. pos.x += ai->getDropOffset().x;
  608. pos.y += ai->getDropOffset().y;
  609. pos.z += ai->getDropOffset().z;
  610. owner->fireCurrentWeapon( &pos );
  611. TheGameLogic->destroyObject( item );
  612. }
  613. else
  614. {
  615. AIUpdateInterface* itemAI = item->getAIUpdateInterface();
  616. if (itemAI)
  617. {
  618. itemAI->aiExit(NULL, CMD_FROM_AI);
  619. }
  620. Coord3D pos = *item->getPosition();
  621. if (ai->getDropVariance().x > 0)
  622. pos.x += GameLogicRandomValueReal(-ai->getDropVariance().x, ai->getDropVariance().x);
  623. if (ai->getDropVariance().y > 0)
  624. pos.y += GameLogicRandomValueReal(-ai->getDropVariance().y, ai->getDropVariance().y);
  625. if (ai->getDropVariance().z > 0)
  626. pos.z += GameLogicRandomValueReal(-ai->getDropVariance().z, ai->getDropVariance().z);
  627. pos.x += ai->getDropOffset().x;
  628. pos.y += ai->getDropOffset().y;
  629. pos.z += ai->getDropOffset().z;
  630. item->setPosition(&pos);
  631. ContainModuleInterface *contain = item->getContain();
  632. if( ai->getData()->m_isParachuteDirectly && contain )
  633. {
  634. contain->setOverrideDestination( ai->getTargetPos() );// go for the direct hit
  635. }
  636. else if (itemAI)
  637. {
  638. // This will most likely be ignored, as the item is still in the transport, and therefore Held.
  639. itemAI->aiMoveToPosition( ai->getMoveToPos(), CMD_FROM_AI );
  640. }
  641. /// @todo srj -- urg. icky.
  642. static NameKeyType key_GenerateMinefieldBehavior = NAMEKEY("GenerateMinefieldBehavior");
  643. GenerateMinefieldBehavior* mfb = (GenerateMinefieldBehavior *)item->findUpdateModule(key_GenerateMinefieldBehavior);
  644. if (mfb)
  645. {
  646. mfb->setMinefieldTarget(ai->getMoveToPos());
  647. }
  648. static NameKeyType key_SmartBombTargetHomingUpdate = NAMEKEY("SmartBombTargetHomingUpdate");
  649. SmartBombTargetHomingUpdate* smthu = (SmartBombTargetHomingUpdate *)item->findUpdateModule(key_SmartBombTargetHomingUpdate);
  650. if (smthu)
  651. {
  652. smthu->SetTargetPosition( *ai->getMoveToPos() );
  653. }
  654. if( ai->getData()->m_inheritTransportVelocity )
  655. {
  656. Coord3D velocity = *owner->getPhysics()->getVelocity();
  657. item->getPhysics()->applyForce( &velocity );
  658. }
  659. }
  660. }
  661. //Handle visible payload delivery.
  662. if( ai->getVisibleItemsDelivered() < ai->getData()->m_visibleNumBones )
  663. {
  664. Int attemptDrops = ai->getData()->m_visibleItemsDroppedPerInterval;
  665. Drawable *draw = owner->getDrawable();
  666. Bool updateSubObjects = false;
  667. while( attemptDrops && ai->getVisibleItemsDelivered() < ai->getData()->m_visibleNumBones )
  668. {
  669. //Drop an item!
  670. if( draw )
  671. {
  672. //First hide the subobject as it becomes the delivered payload.
  673. if( ai->getData()->m_visibleSubObjectName.isNotEmpty() )
  674. {
  675. AsciiString name;
  676. name.format( "%s%02d", ai->getData()->m_visibleSubObjectName.str(), ai->getVisibleItemsDelivered() + 1 );
  677. draw->showSubObject( name, false );
  678. updateSubObjects = true;
  679. }
  680. //Second, create the subobject for real
  681. if( ai->getData()->m_visiblePayloadTemplateName.isNotEmpty() )
  682. {
  683. const ThingTemplate* thing = TheThingFactory->findTemplate( ai->getData()->m_visiblePayloadTemplateName.str() );
  684. Object *payload = TheThingFactory->newObject( thing, owner->getControllingPlayer()->getDefaultTeam() );
  685. if( payload )
  686. {
  687. payload->setProducer( owner );
  688. if( ai->getData()->m_visibleDropBoneName.isNotEmpty() )
  689. {
  690. Coord3D pos;
  691. AsciiString bone;
  692. bone.format( "%s%02d", ai->getData()->m_visibleDropBoneName.str(), ai->getVisibleItemsDelivered() + 1 );
  693. if( draw->getPristineBonePositions( ai->getData()->m_visibleDropBoneName.str(), ai->getVisibleItemsDelivered() + 1, &pos, NULL, 1 ) > 0 )
  694. {
  695. draw->convertBonePosToWorldPos( &pos, NULL, &pos, NULL );
  696. payload->setPosition( &pos );
  697. }
  698. else
  699. {
  700. payload->setPosition( owner->getPosition() );
  701. }
  702. }
  703. else
  704. {
  705. payload->setPosition( owner->getPosition() );
  706. }
  707. payload->setOrientation( owner->getOrientation() );
  708. //If we want the payload to inherit the transport's velocity, do so now.
  709. if( ai->getData()->m_inheritTransportVelocity )
  710. {
  711. Coord3D startingForce = *owner->getPhysics()->getVelocity();
  712. startingForce.scale( payload->getPhysics()->getMass() );
  713. payload->getPhysics()->applyMotiveForce( &startingForce );
  714. Coord3D backPosition = *owner->getPhysics()->getVelocity();
  715. backPosition.scale( -1.0f );
  716. backPosition.add( payload->getPosition() );
  717. payload->setPosition( &backPosition );
  718. }
  719. //Are we firing a missile?
  720. Bool projectileFired = false;
  721. for( BehaviorModule** u = payload->getBehaviorModules(); *u; ++u )
  722. {
  723. ProjectileUpdateInterface* pui = (*u)->getProjectileUpdateInterface();
  724. if( pui )
  725. {
  726. //Missile!
  727. const WeaponTemplate *weaponTemplate = ai->getData()->m_visiblePayloadWeaponTemplate;
  728. if( !weaponTemplate )
  729. {
  730. DEBUG_ASSERTCRASH( 0, ("%s tried to fire missile %s via DeliverPayload, and is missing required weapon template in ObjectCreationList.ini entry.",
  731. owner->getTemplate()->getName().str(), payload->getTemplate()->getName().str() ) );
  732. break;
  733. }
  734. VeterancyLevel v = owner->getVeterancyLevel();
  735. pui->projectileFireAtObjectOrPosition( NULL, ai->getTargetPos(), weaponTemplate, weaponTemplate->getProjectileExhaust(v) );
  736. projectileFired = true;
  737. //damageInfo.in.m_sourceID = pui->projectileGetLauncherID();
  738. break;
  739. }
  740. }
  741. //If not, are we dropping a bomb?
  742. if( !projectileFired )
  743. {
  744. //Bomb!
  745. if( ai->getData()->m_exitPitchRate != 0 )
  746. {
  747. //If the payload needs to pitch, do so now.
  748. Real pitchRate = payload->getPhysics()->getCenterOfMassOffset() * ai->getData()->m_exitPitchRate;
  749. payload->getPhysics()->setPitchRate( pitchRate );
  750. }
  751. //If payload can move independently, order the move. A lot of bombs don't have locomotors,
  752. //in which case the "start move sound" is played, but nothing else actually happens.
  753. if( payload->getAIUpdateInterface() )
  754. {
  755. payload->getAIUpdateInterface()->aiMoveToPosition( ai->getMoveToPos(), CMD_FROM_AI );
  756. }
  757. }
  758. }
  759. }
  760. }
  761. //Update the counters
  762. attemptDrops--;
  763. ai->setVisibleItemsDelivered( ai->getVisibleItemsDelivered() + 1 );
  764. }
  765. if( updateSubObjects )
  766. {
  767. draw->updateSubObjects();
  768. }
  769. }
  770. return STATE_CONTINUE;
  771. }
  772. //-------------------------------------------------------------------------------------------------
  773. void DeliveringState::onExit( StateExitType ) // Close the doors
  774. {
  775. Object *owner = getMachineOwner();
  776. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  777. if (ai)
  778. ai->friend_setFreeToExit(false);
  779. DEBUG_ASSERTCRASH(m_didOpen, ("Not enough time for the doors to open to drop anything!"));
  780. /// @todo srj -- for now, this assumes at most one door
  781. owner->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_OPENING, MODELCONDITION_DOOR_1_CLOSING );
  782. }
  783. //-------------------------------------------------------------------------------------------------
  784. //-------------------------------------------------------------------------------------------------
  785. //-------------------------------------------------------------------------------------------------
  786. // ------------------------------------------------------------------------------------------------
  787. /** CRC */
  788. // ------------------------------------------------------------------------------------------------
  789. void ConsiderNewApproachState::crc( Xfer *xfer )
  790. {
  791. } // end crc
  792. // ------------------------------------------------------------------------------------------------
  793. /** Xfer Method */
  794. // ------------------------------------------------------------------------------------------------
  795. void ConsiderNewApproachState::xfer( Xfer *xfer )
  796. {
  797. // version
  798. XferVersion currentVersion = 1;
  799. XferVersion version = currentVersion;
  800. xfer->xferVersion( &version, currentVersion );
  801. xfer->xferInt(&m_numberEntriesToState);
  802. } // end xfer
  803. // ------------------------------------------------------------------------------------------------
  804. /** Load post process */
  805. // ------------------------------------------------------------------------------------------------
  806. void ConsiderNewApproachState::loadPostProcess( void )
  807. {
  808. } // end loadPostProcess
  809. //-------------------------------------------------------------------------------------------------
  810. StateReturnType ConsiderNewApproachState::onEnter() // Increment local counter o' futility
  811. {
  812. Object *owner = getMachineOwner();
  813. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  814. if( !ai )
  815. {
  816. return STATE_FAILURE;
  817. }
  818. ++m_numberEntriesToState;
  819. DEBUG_LOG(("Considering approach #%d...\n",m_numberEntriesToState));
  820. if( m_numberEntriesToState > ai->getMaxNumberAttempts() )
  821. {
  822. DEBUG_LOG(("Too many approaches! Time to give up.\n"));
  823. return STATE_FAILURE;
  824. }
  825. // ok, play some games here... the pathfinder is pretty dumb about things with big turning radii,
  826. // so we have to force the issue here... basically, estimate our necessary turning radius
  827. // based on loco values, move off far enough so we can turn, then head back. (if we just say
  828. // "head back directly", the code will just keep turning in circles, not realizing that our
  829. // turning radius is too large for that ever to work.)
  830. Real minTurnRadius = ai->calcMinTurnRadius(NULL);
  831. // how far is "far enough"? we must be at least 2*radius dist away from our target.
  832. // (we add a little fudge since we may not be able to travel our max speed while
  833. // turning.)
  834. const Real DIST_FUDGE = 2.2f;
  835. Real minReApproachDist = minTurnRadius * DIST_FUDGE;
  836. const Coord3D* dir = owner->getUnitDirectionVector2D();
  837. Coord3D reApproachPoint;
  838. reApproachPoint.x = owner->getPosition()->x + dir->x * minReApproachDist;
  839. reApproachPoint.y = owner->getPosition()->y + dir->y * minReApproachDist;
  840. reApproachPoint.z = 0.0f; // yeah, yeah, should be terrainpos, but doesn't really matter here...
  841. ai->aiMoveToPosition( &reApproachPoint, CMD_FROM_AI );
  842. // we allow these guys to go to invalid (ie, off-map) positions
  843. ai->getCurLocomotor()->setAllowInvalidPosition(true);
  844. return STATE_CONTINUE;
  845. }
  846. //-------------------------------------------------------------------------------------------------
  847. StateReturnType ConsiderNewApproachState::update() // Success if we should try again, failure if we should give up
  848. {
  849. Object *owner = getMachineOwner();
  850. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  851. if( !ai )
  852. {
  853. return STATE_FAILURE;
  854. }
  855. // stay in this state 'till we reach our goal.
  856. if (ai->getAIStateType() != AI_MOVE_TO)
  857. {
  858. return STATE_SUCCESS;
  859. }
  860. return STATE_CONTINUE;
  861. }
  862. //-------------------------------------------------------------------------------------------------
  863. void ConsiderNewApproachState::onExit( StateExitType status )
  864. {
  865. Object *owner = getMachineOwner();
  866. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  867. if( !ai )
  868. {
  869. return;
  870. }
  871. // go back to normal.
  872. ai->getCurLocomotor()->setAllowInvalidPosition(true);
  873. }
  874. //-------------------------------------------------------------------------------------------------
  875. //-------------------------------------------------------------------------------------------------
  876. //-------------------------------------------------------------------------------------------------
  877. // ------------------------------------------------------------------------------------------------
  878. /** CRC */
  879. // ------------------------------------------------------------------------------------------------
  880. void RecoverFromOffMapState::crc( Xfer *xfer )
  881. {
  882. } // end crc
  883. // ------------------------------------------------------------------------------------------------
  884. /** Xfer Method */
  885. // ------------------------------------------------------------------------------------------------
  886. void RecoverFromOffMapState::xfer( Xfer *xfer )
  887. {
  888. // version
  889. XferVersion currentVersion = 1;
  890. XferVersion version = currentVersion;
  891. xfer->xferVersion( &version, currentVersion );
  892. xfer->xferUnsignedInt(&m_reEntryFrame);
  893. } // end xfer
  894. // ------------------------------------------------------------------------------------------------
  895. /** Load post process */
  896. // ------------------------------------------------------------------------------------------------
  897. void RecoverFromOffMapState::loadPostProcess( void )
  898. {
  899. } // end loadPostProcess
  900. //-------------------------------------------------------------------------------------------------
  901. StateReturnType RecoverFromOffMapState::onEnter() // Increment local counter o' futility
  902. {
  903. Object *owner = getMachineOwner();
  904. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  905. if( !ai )
  906. {
  907. return STATE_FAILURE;
  908. }
  909. // have him hold in place, if possible.
  910. ai->aiMoveToPosition( owner->getPosition(), CMD_FROM_AI );
  911. // a little cheesy... make a delay based on turn-radius time to simulate offscreen
  912. // maneuvering.
  913. Real timeToTravelThatDist;
  914. /*Real minTurnRadius =*/ ai->calcMinTurnRadius(&timeToTravelThatDist);
  915. m_reEntryFrame = TheGameLogic->getFrame() + REAL_TO_INT_CEIL(timeToTravelThatDist);
  916. // kill its momentum
  917. PhysicsBehavior* physics = owner->getPhysics();
  918. physics->resetDynamicPhysics();
  919. ThePartitionManager->unRegisterObject( owner );
  920. if( owner->getDrawable() )
  921. owner->getDrawable()->setDrawableHidden( true );
  922. return STATE_CONTINUE;
  923. }
  924. //-------------------------------------------------------------------------------------------------
  925. StateReturnType RecoverFromOffMapState::update() // Success if we should try again, failure if we should give up
  926. {
  927. if (TheGameLogic->getFrame() < m_reEntryFrame)
  928. return STATE_CONTINUE;
  929. Object *owner = getMachineOwner();
  930. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  931. if( !ai )
  932. {
  933. return STATE_FAILURE;
  934. }
  935. ThePartitionManager->registerObject( owner );
  936. if( owner->getDrawable() )
  937. owner->getDrawable()->setDrawableHidden( false );
  938. Coord3D enterCoord = TheTerrainLogic->findClosestEdgePoint( owner->getPosition() );
  939. if (owner->isAboveTerrain())
  940. enterCoord.z = owner->getPosition()->z;
  941. owner->setPosition(&enterCoord);
  942. Real enterAngle = atan2(ai->getMoveToPos()->y - enterCoord.y, ai->getMoveToPos()->x - enterCoord.x);
  943. owner->setOrientation(enterAngle);
  944. PhysicsBehavior* physics = owner->getPhysics();
  945. physics->resetDynamicPhysics();
  946. return STATE_SUCCESS;
  947. }
  948. //-------------------------------------------------------------------------------------------------
  949. //-------------------------------------------------------------------------------------------------
  950. //-------------------------------------------------------------------------------------------------
  951. //-------------------------------------------------------------------------------------------------
  952. StateReturnType HeadOffMapState::onEnter() // Give move order out of town
  953. {
  954. Object *owner = getMachineOwner();
  955. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  956. if( !ai )
  957. {
  958. return STATE_FAILURE;
  959. }
  960. ai->killDeliveryDecal();
  961. if ( ai->getData()->m_selfDestructObject ) //Lorenzen... this makes delivery unit pop away like a bubble...
  962. {
  963. TheGameLogic->destroyObject( owner );
  964. return STATE_CONTINUE;
  965. }
  966. //Coord3D exitCoord = TheTerrainLogic->findClosestEdgePoint( owner->getPosition() );
  967. // just keep moving straight ahead till we exit the map.
  968. Coord3D exitCoord = *owner->getPosition();
  969. const Coord3D* dir = owner->getUnitDirectionVector2D();
  970. Region3D terrainExtent;
  971. TheTerrainLogic->getExtent( &terrainExtent );
  972. const Real FUDGE = 1.2f;
  973. Real HUGE_DIST = FUDGE * sqrt(sqr(terrainExtent.hi.x - terrainExtent.lo.x) + sqr(terrainExtent.hi.y - terrainExtent.lo.y));
  974. exitCoord.x += dir->x * HUGE_DIST;
  975. exitCoord.y += dir->y * HUGE_DIST;
  976. ai->getCurLocomotor()->setAllowInvalidPosition(true);
  977. ai->getCurLocomotor()->setUltraAccurate(true); // set ultra-accurate just so AI won't try to adjust our dest
  978. ai->aiMoveToPosition( &exitCoord, CMD_FROM_AI );
  979. // once we get into head-off-map state, we're done... don't respond to anything else.
  980. // this addresses the bug where a plane delivering team "foo" hasn't made it off the map
  981. // before all of team "foo" lands, whereupon team "foo" is given a script command to
  982. // (say) move to a given point... and since the cargo plane might be a member of "foo"
  983. // it would respond to said command, which sucks and is confusing. might be better
  984. // to have these guys generally ignore most commands, but this is pretty safe, in that
  985. // AFAIK nothing with deliverpayload should ever respond to anything, not even IDLE,
  986. // once they start heading off the map. (srj)
  987. ai->friend_setAcceptingCommands(false);
  988. owner->getUnitDirectionVector3D( facingDirectionUponDelivery );
  989. return STATE_CONTINUE;
  990. }
  991. //-------------------------------------------------------------------------------------------------
  992. StateReturnType HeadOffMapState::update()
  993. {
  994. Object *owner = getMachineOwner();
  995. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
  996. if( !ai )
  997. {
  998. return STATE_FAILURE;
  999. }
  1000. if (ai->isOffMap())
  1001. return STATE_SUCCESS;
  1002. // greasy as this hack seems... if terrain or objects or whatever have turned me away from the map-edge that I had first headed for,...
  1003. //I blow up, rather than face eternally spinning on the point of a mineret or derrick or something awful.
  1004. if ( owner->getPhysics()->getTurning() != 0 )
  1005. {
  1006. Coord3D currentDirection;
  1007. owner->getUnitDirectionVector3D( currentDirection );
  1008. Real dot = facingDirectionUponDelivery.x * currentDirection.x
  1009. + facingDirectionUponDelivery.y * currentDirection.y
  1010. + facingDirectionUponDelivery.z * currentDirection.z;
  1011. if ( dot < 0.3f )
  1012. owner->kill();
  1013. }
  1014. // Coord3D pos = *owner->getPosition();
  1015. //
  1016. // if( TheTerrainLogic->getGroundHeight( pos->x, pos,y ) )
  1017. return STATE_CONTINUE;
  1018. }
  1019. //-------------------------------------------------------------------------------------------------
  1020. //-------------------------------------------------------------------------------------------------
  1021. //-------------------------------------------------------------------------------------------------
  1022. //-------------------------------------------------------------------------------------------------
  1023. StateReturnType CleanUpState::onEnter() // Delete my successful butt
  1024. {
  1025. if( getMachineOwner()->getContain() )
  1026. {
  1027. DEBUG_ASSERTCRASH(getMachineOwner()->getContain()->getContainCount() == 0, ("did not drop all items!"));
  1028. }
  1029. Object *owner = getMachineOwner();
  1030. TheGameLogic->destroyObject( owner );
  1031. return STATE_CONTINUE;
  1032. }