BattlePlanUpdate.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: BattlePlanUpdate.cpp //////////////////////////////////////////////////////////////////////////
  24. // Author: Kris Morness, September 2002
  25. // Desc: Update module to handle building states and battle plan execution & changes
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  28. #define DEFINE_MAXHEALTHCHANGETYPE_NAMES // for TheMaxHealthChangeTypeNames[]
  29. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  30. #include "Common/BitFlagsIO.h"
  31. #include "Common/Radar.h"
  32. #include "Common/PlayerList.h"
  33. #include "Common/ThingTemplate.h"
  34. #include "Common/ThingFactory.h"
  35. #include "Common/Player.h"
  36. #include "Common/Xfer.h"
  37. #include "GameClient/GameClient.h"
  38. #include "GameClient/Drawable.h"
  39. #include "GameClient/GameText.h"
  40. #include "GameClient/ParticleSys.h"
  41. #include "GameClient/FXList.h"
  42. #include "GameClient/ControlBar.h"
  43. #include "GameLogic/GameLogic.h"
  44. #include "GameLogic/PartitionManager.h"
  45. #include "GameLogic/Object.h"
  46. #include "GameLogic/ObjectIter.h"
  47. #include "GameLogic/Weaponset.h"
  48. #include "GameLogic/Weapon.h"
  49. #include "GameLogic/TerrainLogic.h"
  50. #include "GameLogic/Module/SpecialPowerModule.h"
  51. #include "GameLogic/Module/BattlePlanUpdate.h"
  52. #include "GameLogic/Module/PhysicsUpdate.h"
  53. #include "GameLogic/Module/ActiveBody.h"
  54. #include "GameLogic/Module/AIUpdate.h"
  55. #include "GameLogic/Module/StealthDetectorUpdate.h"
  56. #ifdef _INTERNAL
  57. // for occasional debugging...
  58. //#pragma optimize("", off)
  59. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  60. #endif
  61. //-------------------------------------------------------------------------------------------------
  62. //-------------------------------------------------------------------------------------------------
  63. BattlePlanUpdateModuleData::BattlePlanUpdateModuleData()
  64. {
  65. m_specialPowerTemplate = NULL;
  66. m_bombardmentPlanAnimationFrames = 0;
  67. m_holdTheLinePlanAnimationFrames = 0;
  68. m_searchAndDestroyPlanAnimationFrames = 0;
  69. m_battlePlanParalyzeFrames = 0;
  70. m_holdTheLineArmorDamageScalar = 1.0f;
  71. m_searchAndDestroySightRangeScalar = 1.0f;
  72. m_strategyCenterSearchAndDestroySightRangeScalar = 1.0f;
  73. m_strategyCenterSearchAndDestroyDetectsStealth = true;
  74. m_strategyCenterHoldTheLineMaxHealthScalar = 1.0f;
  75. m_strategyCenterHoldTheLineMaxHealthChangeType = PRESERVE_RATIO;
  76. }
  77. //-------------------------------------------------------------------------------------------------
  78. /*static*/ void BattlePlanUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  79. {
  80. ModuleData::buildFieldParse(p);
  81. static const FieldParse dataFieldParse[] =
  82. {
  83. { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( BattlePlanUpdateModuleData, m_specialPowerTemplate ) },
  84. { "BombardmentPlanAnimationTime", INI::parseDurationUnsignedInt, NULL, offsetof( BattlePlanUpdateModuleData, m_bombardmentPlanAnimationFrames ) },
  85. { "HoldTheLinePlanAnimationTime", INI::parseDurationUnsignedInt, NULL, offsetof( BattlePlanUpdateModuleData, m_holdTheLinePlanAnimationFrames ) },
  86. { "SearchAndDestroyPlanAnimationTime", INI::parseDurationUnsignedInt, NULL, offsetof( BattlePlanUpdateModuleData, m_searchAndDestroyPlanAnimationFrames ) },
  87. { "TransitionIdleTime", INI::parseDurationUnsignedInt, NULL, offsetof( BattlePlanUpdateModuleData, m_transitionIdleFrames ) },
  88. { "BombardmentPlanUnpackSoundName", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_bombardmentUnpackName ) },
  89. { "BombardmentPlanPackSoundName", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_bombardmentPackName ) },
  90. { "BombardmentMessageLabel", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_bombardmentMessageLabel ) },
  91. { "BombardmentAnnouncementName", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_bombardmentAnnouncementName ) },
  92. { "SearchAndDestroyPlanUnpackSoundName", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_searchAndDestroyUnpackName ) },
  93. { "SearchAndDestroyPlanIdleLoopSoundName",INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_searchAndDestroyIdleName ) },
  94. { "SearchAndDestroyPlanPackSoundName", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_searchAndDestroyPackName ) },
  95. { "SearchAndDestroyMessageLabel", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_searchAndDestroyMessageLabel ) },
  96. { "SearchAndDestroyAnnouncementName", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_searchAndDestroyAnnouncementName ) },
  97. { "HoldTheLinePlanUnpackSoundName", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_holdTheLineUnpackName ) },
  98. { "HoldTheLinePlanPackSoundName", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_holdTheLinePackName ) },
  99. { "HoldTheLineMessageLabel", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_holdTheLineMessageLabel ) },
  100. { "HoldTheLineAnnouncementName", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_holdTheLineAnnouncementName ) },
  101. { "ValidMemberKindOf", KindOfMaskType::parseFromINI, NULL, offsetof( BattlePlanUpdateModuleData, m_validMemberKindOf ) },
  102. { "InvalidMemberKindOf", KindOfMaskType::parseFromINI, NULL, offsetof( BattlePlanUpdateModuleData, m_invalidMemberKindOf ) },
  103. { "BattlePlanChangeParalyzeTime", INI::parseDurationUnsignedInt, NULL, offsetof( BattlePlanUpdateModuleData, m_battlePlanParalyzeFrames ) },
  104. { "HoldTheLinePlanArmorDamageScalar", INI::parseReal, NULL, offsetof( BattlePlanUpdateModuleData, m_holdTheLineArmorDamageScalar ) },
  105. { "SearchAndDestroyPlanSightRangeScalar", INI::parseReal, NULL, offsetof( BattlePlanUpdateModuleData, m_searchAndDestroySightRangeScalar ) },
  106. { "StrategyCenterSearchAndDestroySightRangeScalar", INI::parseReal, NULL, offsetof( BattlePlanUpdateModuleData, m_strategyCenterSearchAndDestroySightRangeScalar ) },
  107. { "StrategyCenterSearchAndDestroyDetectsStealth", INI::parseBool, NULL, offsetof( BattlePlanUpdateModuleData, m_strategyCenterSearchAndDestroyDetectsStealth ) },
  108. { "StrategyCenterHoldTheLineMaxHealthScalar", INI::parseReal, NULL, offsetof( BattlePlanUpdateModuleData, m_strategyCenterHoldTheLineMaxHealthScalar ) },
  109. { "StrategyCenterHoldTheLineMaxHealthChangeType", INI::parseIndexList, TheMaxHealthChangeTypeNames, offsetof( BattlePlanUpdateModuleData, m_strategyCenterHoldTheLineMaxHealthChangeType ) },
  110. { "VisionObjectName", INI::parseAsciiString, NULL, offsetof( BattlePlanUpdateModuleData, m_visionObjectName ) },
  111. { 0, 0, 0, 0 }
  112. };
  113. p.add(dataFieldParse);
  114. }
  115. //-------------------------------------------------------------------------------------------------
  116. BattlePlanUpdate::BattlePlanUpdate( Thing *thing, const ModuleData* moduleData ) :
  117. SpecialPowerUpdateModule( thing, moduleData ),
  118. m_bonuses(NULL)
  119. {
  120. const BattlePlanUpdateModuleData *data = getBattlePlanUpdateModuleData();
  121. m_status = TRANSITIONSTATUS_IDLE;
  122. m_currentPlan = PLANSTATUS_NONE;
  123. m_desiredPlan = PLANSTATUS_NONE;
  124. m_planAffectingArmy = PLANSTATUS_NONE;
  125. m_nextReadyFrame = 0;
  126. m_invalidSettings = false;
  127. m_centeringTurret = false;
  128. //Default the bonuses to no change.
  129. m_bonuses = newInstance(BattlePlanBonuses);
  130. m_bonuses->m_armorScalar = 1.0f;
  131. m_bonuses->m_sightRangeScalar = 1.0f;
  132. m_bonuses->m_bombardment = 0;
  133. m_bonuses->m_searchAndDestroy = 0;
  134. m_bonuses->m_holdTheLine = 0;
  135. m_bonuses->m_validKindOf = data->m_validMemberKindOf;
  136. m_bonuses->m_invalidKindOf = data->m_invalidMemberKindOf;
  137. m_visionObjectID = INVALID_ID;
  138. //------------------------//
  139. // Added by Sadullah Nader//
  140. //------------------------//
  141. m_specialPowerModule = NULL;
  142. //
  143. }
  144. //-------------------------------------------------------------------------------------------------
  145. //-------------------------------------------------------------------------------------------------
  146. BattlePlanUpdate::~BattlePlanUpdate( void )
  147. {
  148. TheAudio->removeAudioEvent( m_bombardmentUnpack.getPlayingHandle() );
  149. TheAudio->removeAudioEvent( m_bombardmentPack.getPlayingHandle() );
  150. TheAudio->removeAudioEvent( m_searchAndDestroyUnpack.getPlayingHandle() );
  151. TheAudio->removeAudioEvent( m_searchAndDestroyIdle.getPlayingHandle() );
  152. TheAudio->removeAudioEvent( m_searchAndDestroyPack.getPlayingHandle() );
  153. TheAudio->removeAudioEvent( m_holdTheLineUnpack.getPlayingHandle() );
  154. TheAudio->removeAudioEvent( m_holdTheLinePack.getPlayingHandle() );
  155. }
  156. // ------------------------------------------------------------------------------------------------
  157. /** On delete */
  158. // ------------------------------------------------------------------------------------------------
  159. void BattlePlanUpdate::onDelete()
  160. {
  161. // extend base class
  162. UpdateModule::onDelete();
  163. // delete our vision object, if it exists
  164. Object *obj;
  165. if( m_visionObjectID != INVALID_ID )
  166. {
  167. obj = TheGameLogic->findObjectByID( m_visionObjectID );
  168. if( obj )
  169. TheGameLogic->destroyObject( obj );
  170. } // end if
  171. // If we get destroyed, then make sure we remove our bonus!
  172. // srj sez: we can't do this in the dtor because our team
  173. // (and thus controlling player) has already been nulled by then...
  174. Player* player = getObject()->getControllingPlayer();
  175. // however, player CAN legitimately be null during game reset cycles
  176. // (and which point it doesn't really matter if we can remove the bonus or not)
  177. //DEBUG_ASSERTCRASH(player != NULL, ("Hmm, controller is null"));
  178. if( player && m_planAffectingArmy != PLANSTATUS_NONE )
  179. {
  180. player->changeBattlePlan( m_planAffectingArmy, -1, m_bonuses );
  181. }
  182. }
  183. //-------------------------------------------------------------------------------------------------
  184. // Validate that we have the necessary data from the ini file.
  185. //-------------------------------------------------------------------------------------------------
  186. void BattlePlanUpdate::onObjectCreated()
  187. {
  188. const BattlePlanUpdateModuleData *data = getBattlePlanUpdateModuleData();
  189. Object *obj = getObject();
  190. if( !data->m_specialPowerTemplate )
  191. {
  192. DEBUG_CRASH( ("%s object's BattlePlanUpdate lacks access to the SpecialPowerTemplate. Needs to be specified in ini.", obj->getTemplate()->getName().str() ) );
  193. m_invalidSettings = true;
  194. return;
  195. }
  196. m_specialPowerModule = obj->getSpecialPowerModule( data->m_specialPowerTemplate );
  197. //Create instances of the sounds required.
  198. m_bombardmentUnpack.setEventName( data->m_bombardmentUnpackName );
  199. m_bombardmentPack.setEventName( data->m_bombardmentPackName );
  200. m_bombardmentAnnouncement.setEventName( data->m_bombardmentAnnouncementName );
  201. m_searchAndDestroyUnpack.setEventName( data->m_searchAndDestroyUnpackName );
  202. m_searchAndDestroyIdle.setEventName( data->m_searchAndDestroyIdleName );
  203. m_searchAndDestroyPack.setEventName( data->m_searchAndDestroyPackName );
  204. m_searchAndDestroyAnnouncement.setEventName( data->m_searchAndDestroyAnnouncementName );
  205. m_holdTheLineUnpack.setEventName( data->m_holdTheLineUnpackName );
  206. m_holdTheLinePack.setEventName( data->m_holdTheLinePackName );
  207. m_holdTheLineAnnouncement.setEventName( data->m_holdTheLineAnnouncementName );
  208. TheAudio->getInfoForAudioEvent( &m_bombardmentUnpack );
  209. TheAudio->getInfoForAudioEvent( &m_bombardmentPack );
  210. TheAudio->getInfoForAudioEvent( &m_bombardmentAnnouncement );
  211. TheAudio->getInfoForAudioEvent( &m_searchAndDestroyUnpack );
  212. TheAudio->getInfoForAudioEvent( &m_searchAndDestroyIdle );
  213. TheAudio->getInfoForAudioEvent( &m_searchAndDestroyPack );
  214. TheAudio->getInfoForAudioEvent( &m_searchAndDestroyAnnouncement );
  215. TheAudio->getInfoForAudioEvent( &m_holdTheLineUnpack );
  216. TheAudio->getInfoForAudioEvent( &m_holdTheLinePack );
  217. TheAudio->getInfoForAudioEvent( &m_holdTheLineAnnouncement );
  218. getObject()->setWeaponSetFlag( WEAPONSET_VETERAN );
  219. AIUpdateInterface *ai = obj->getAI();
  220. if( ai )
  221. {
  222. // lock it just till the weapon is empty or the attack is "done"
  223. obj->setWeaponLock( PRIMARY_WEAPON, LOCKED_TEMPORARILY );
  224. }
  225. enableTurret( false );
  226. }
  227. //-------------------------------------------------------------------------------------------------
  228. Bool BattlePlanUpdate::initiateIntentToDoSpecialPower(const SpecialPowerTemplate *specialPowerTemplate, const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions )
  229. {
  230. if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate )
  231. {
  232. //Check to make sure our modules are connected.
  233. return FALSE;
  234. }
  235. //Set the desired status based on the command button option!
  236. if( BitTest( commandOptions, OPTION_ONE ) )
  237. {
  238. m_desiredPlan = PLANSTATUS_BOMBARDMENT;
  239. }
  240. else if( BitTest( commandOptions, OPTION_TWO ) )
  241. {
  242. m_desiredPlan = PLANSTATUS_HOLDTHELINE;
  243. }
  244. else if( BitTest( commandOptions, OPTION_THREE ) )
  245. {
  246. m_desiredPlan = PLANSTATUS_SEARCHANDDESTROY;
  247. }
  248. else
  249. {
  250. DEBUG_CRASH( ("Selected an unsupported strategy for strategy center.") );
  251. return FALSE;
  252. }
  253. getObject()->getControllingPlayer()->getAcademyStats()->recordBattlePlanSelected();
  254. return TRUE;
  255. }
  256. Bool BattlePlanUpdate::isPowerCurrentlyInUse( const CommandButton *command ) const
  257. {
  258. //@todo -- perhaps we may need this one day...
  259. return false;
  260. }
  261. //-------------------------------------------------------------------------------------------------
  262. CommandOption BattlePlanUpdate::getCommandOption() const
  263. {
  264. switch( m_desiredPlan )
  265. {
  266. case PLANSTATUS_BOMBARDMENT:
  267. return OPTION_ONE;
  268. case PLANSTATUS_HOLDTHELINE:
  269. return OPTION_TWO;
  270. case PLANSTATUS_SEARCHANDDESTROY:
  271. return OPTION_THREE;
  272. }
  273. return (CommandOption)0;
  274. }
  275. //-------------------------------------------------------------------------------------------------
  276. /** The update callback. */
  277. //-------------------------------------------------------------------------------------------------
  278. UpdateSleepTime BattlePlanUpdate::update()
  279. {
  280. if( m_invalidSettings )
  281. {
  282. // can't return UPDATE_SLEEP_FOREVER unless we are sleepy...
  283. return UPDATE_SLEEP_NONE;
  284. ///return UPDATE_SLEEP_FOREVER;
  285. }
  286. //const BattlePlanUpdateModuleData *data = getBattlePlanUpdateModuleData();
  287. //Object *obj = getObject();
  288. UnsignedInt now = TheGameLogic->getFrame();
  289. if( m_nextReadyFrame <= now )
  290. {
  291. switch( m_status )
  292. {
  293. case TRANSITIONSTATUS_IDLE:
  294. //There's only two cases where we are in an idle status -- upon initialization
  295. //when no plan has yet been selected. The other case is after we've finished
  296. //packing the previous plan up and are waiting to unpack the new state.
  297. if( m_desiredPlan != PLANSTATUS_NONE )
  298. {
  299. m_currentPlan = m_desiredPlan;
  300. setStatus( TRANSITIONSTATUS_UNPACKING );
  301. }
  302. break;
  303. case TRANSITIONSTATUS_UNPACKING:
  304. //If we're unpacking, we are forcing the user to wait until the plan is unpacked
  305. //before allowing him to select a new plan. The plan doesn't become active until
  306. //we're finished unpacking.
  307. setStatus( TRANSITIONSTATUS_ACTIVE );
  308. if( m_currentPlan == PLANSTATUS_BOMBARDMENT )
  309. {
  310. enableTurret( true );
  311. }
  312. break;
  313. case TRANSITIONSTATUS_ACTIVE:
  314. //If we're active and the user has selected a different plan, then we need to
  315. //pack up.
  316. if( m_currentPlan != m_desiredPlan )
  317. {
  318. if( m_currentPlan == PLANSTATUS_BOMBARDMENT )
  319. {
  320. //Special case situation -- in bombardment status, we need to center
  321. //the turret prior to packing up, so handle it here.
  322. AIUpdateInterface *ai = getObject()->getAI();
  323. if( ai )
  324. {
  325. if( isTurretInNaturalPosition() )
  326. {
  327. //It's centered, so pack
  328. setStatus( TRANSITIONSTATUS_PACKING );
  329. m_centeringTurret = false;
  330. enableTurret( false );
  331. }
  332. else if( !m_centeringTurret )
  333. {
  334. //It's not centered, and not trying to center, so order it to center.
  335. ai->aiIdle( CMD_FROM_AI );
  336. recenterTurret();
  337. m_centeringTurret = true;
  338. }
  339. }
  340. }
  341. else
  342. {
  343. setStatus( TRANSITIONSTATUS_PACKING );
  344. }
  345. }
  346. break;
  347. case TRANSITIONSTATUS_PACKING:
  348. //If we finished packing, then go idle until we can switch to our new plan.
  349. setStatus( TRANSITIONSTATUS_IDLE );
  350. break;
  351. }
  352. }
  353. return UPDATE_SLEEP_NONE;
  354. }
  355. // ------------------------------------------------------------------------------------------------
  356. /** Create vision objects for all players revealing this building to all */
  357. // ------------------------------------------------------------------------------------------------
  358. void BattlePlanUpdate::createVisionObject()
  359. {
  360. if (m_visionObjectID != INVALID_ID) // don't want two.
  361. return;
  362. const BattlePlanUpdateModuleData *data = getBattlePlanUpdateModuleData();
  363. Object *obj = getObject();
  364. // get template of object to create
  365. const ThingTemplate *tt = TheThingFactory->findTemplate( data->m_visionObjectName );
  366. DEBUG_ASSERTCRASH( tt, ("BattlePlanUpdate::setStatus - Invalid vision object name '%s'\n",
  367. data->m_visionObjectName.str()) );
  368. if (!tt)
  369. return;
  370. Player *pPlayer = ThePlayerList->getNeutralPlayer();
  371. // sanity
  372. if(!pPlayer)
  373. return;
  374. Object *visionObject;
  375. // create object for this player
  376. visionObject = TheThingFactory->newObject( tt, pPlayer->getDefaultTeam() );
  377. if( visionObject )
  378. {
  379. // record we have an object
  380. m_visionObjectID = visionObject->getID();
  381. // set position
  382. visionObject->setPosition( obj->getPosition() );
  383. // set the shroud clearing range
  384. visionObject->setShroudClearingRange( obj->getGeometryInfo().getBoundingSphereRadius() );
  385. } // end if
  386. } // end createVisionObject
  387. //-------------------------------------------------------------------------------------------------
  388. void BattlePlanUpdate::setStatus( TransitionStatus newStatus )
  389. {
  390. const BattlePlanUpdateModuleData *data = getBattlePlanUpdateModuleData();
  391. Object *obj = getObject();
  392. if( m_status == newStatus )
  393. {
  394. return;
  395. }
  396. TransitionStatus oldStatus = m_status;
  397. //Turn off old defining states and sounds
  398. switch( oldStatus )
  399. {
  400. case TRANSITIONSTATUS_IDLE:
  401. break;
  402. case TRANSITIONSTATUS_UNPACKING:
  403. switch( m_currentPlan )
  404. {
  405. case PLANSTATUS_BOMBARDMENT:
  406. obj->clearModelConditionState( MODELCONDITION_DOOR_1_OPENING );
  407. TheAudio->removeAudioEvent( m_bombardmentUnpack.getPlayingHandle() );
  408. break;
  409. case PLANSTATUS_HOLDTHELINE:
  410. obj->clearModelConditionState( MODELCONDITION_DOOR_2_OPENING );
  411. TheAudio->removeAudioEvent( m_holdTheLineUnpack.getPlayingHandle() );
  412. break;
  413. case PLANSTATUS_SEARCHANDDESTROY:
  414. obj->clearModelConditionState( MODELCONDITION_DOOR_3_OPENING );
  415. TheAudio->removeAudioEvent( m_searchAndDestroyUnpack.getPlayingHandle() );
  416. break;
  417. }
  418. break;
  419. case TRANSITIONSTATUS_ACTIVE:
  420. switch( m_currentPlan )
  421. {
  422. case PLANSTATUS_BOMBARDMENT:
  423. obj->clearModelConditionState( MODELCONDITION_DOOR_1_WAITING_TO_CLOSE );
  424. break;
  425. case PLANSTATUS_HOLDTHELINE:
  426. obj->clearModelConditionState( MODELCONDITION_DOOR_2_WAITING_TO_CLOSE );
  427. break;
  428. case PLANSTATUS_SEARCHANDDESTROY:
  429. obj->clearModelConditionState( MODELCONDITION_DOOR_3_WAITING_TO_CLOSE );
  430. TheAudio->removeAudioEvent( m_searchAndDestroyIdle.getPlayingHandle() );
  431. break;
  432. }
  433. break;
  434. case TRANSITIONSTATUS_PACKING:
  435. switch( m_currentPlan )
  436. {
  437. case PLANSTATUS_BOMBARDMENT:
  438. obj->clearModelConditionState( MODELCONDITION_DOOR_1_CLOSING );
  439. TheAudio->removeAudioEvent( m_bombardmentPack.getPlayingHandle() );
  440. break;
  441. case PLANSTATUS_HOLDTHELINE:
  442. obj->clearModelConditionState( MODELCONDITION_DOOR_2_CLOSING );
  443. TheAudio->removeAudioEvent( m_holdTheLinePack.getPlayingHandle() );
  444. break;
  445. case PLANSTATUS_SEARCHANDDESTROY:
  446. obj->clearModelConditionState( MODELCONDITION_DOOR_3_CLOSING );
  447. TheAudio->removeAudioEvent( m_searchAndDestroyPack.getPlayingHandle() );
  448. break;
  449. }
  450. break;
  451. }
  452. UnsignedInt now = TheGameLogic->getFrame();
  453. //Handle entering new status
  454. switch( newStatus )
  455. {
  456. case TRANSITIONSTATUS_IDLE:
  457. {
  458. m_currentPlan = PLANSTATUS_NONE;
  459. m_nextReadyFrame = now + data->m_transitionIdleFrames;
  460. break;
  461. }
  462. case TRANSITIONSTATUS_UNPACKING:
  463. {
  464. // play a radar blip showing the battle plan change in the color of the
  465. TheRadar->createPlayerEvent( obj->getControllingPlayer(),
  466. obj->getPosition(),
  467. RADAR_EVENT_BATTLE_PLAN );
  468. //this is now handled with ShroudRevealToAllRange in thingTemplate
  469. //createVisionObject();
  470. switch( m_currentPlan )
  471. {
  472. case PLANSTATUS_BOMBARDMENT:
  473. obj->setModelConditionState( MODELCONDITION_DOOR_1_OPENING );
  474. obj->getDrawable()->setAnimationLoopDuration( data->m_bombardmentPlanAnimationFrames );
  475. m_nextReadyFrame = now + data->m_bombardmentPlanAnimationFrames;
  476. if( m_bombardmentUnpack.getEventName().isNotEmpty() )
  477. {
  478. m_bombardmentUnpack.setObjectID( obj->getID() );
  479. m_bombardmentUnpack.setPlayingHandle( TheAudio->addAudioEvent( &m_bombardmentUnpack ) );
  480. }
  481. // display a message to *all* users
  482. TheInGameUI->message( TheGameText->fetch( data->m_bombardmentMessageLabel ) );
  483. if( m_bombardmentAnnouncement.getEventName().isEmpty() == FALSE )
  484. {
  485. m_bombardmentAnnouncement.setPosition( obj->getPosition() );
  486. TheAudio->addAudioEvent( &m_bombardmentAnnouncement );
  487. }
  488. break;
  489. case PLANSTATUS_HOLDTHELINE:
  490. obj->setModelConditionState( MODELCONDITION_DOOR_2_OPENING );
  491. obj->getDrawable()->setAnimationLoopDuration( data->m_holdTheLinePlanAnimationFrames );
  492. m_nextReadyFrame = now + data->m_holdTheLinePlanAnimationFrames;
  493. if( m_holdTheLineUnpack.getEventName().isNotEmpty() )
  494. {
  495. m_holdTheLineUnpack.setObjectID( obj->getID() );
  496. m_holdTheLineUnpack.setPlayingHandle( TheAudio->addAudioEvent( &m_holdTheLineUnpack ) );
  497. }
  498. // display a message to *all* users
  499. TheInGameUI->message( TheGameText->fetch( data->m_holdTheLineMessageLabel ) );
  500. if( m_holdTheLineAnnouncement.getEventName().isEmpty() == FALSE )
  501. {
  502. m_holdTheLineAnnouncement.setPosition( obj->getPosition() );
  503. TheAudio->addAudioEvent( &m_holdTheLineAnnouncement );
  504. }
  505. break;
  506. case PLANSTATUS_SEARCHANDDESTROY:
  507. obj->setModelConditionState( MODELCONDITION_DOOR_3_OPENING );
  508. obj->getDrawable()->setAnimationLoopDuration( data->m_searchAndDestroyPlanAnimationFrames );
  509. m_nextReadyFrame = now + data->m_searchAndDestroyPlanAnimationFrames;
  510. if( m_searchAndDestroyUnpack.getEventName().isNotEmpty() )
  511. {
  512. m_searchAndDestroyUnpack.setObjectID( obj->getID() );
  513. m_searchAndDestroyUnpack.setPlayingHandle( TheAudio->addAudioEvent( &m_searchAndDestroyUnpack ) );
  514. }
  515. // display a message to *all* users
  516. TheInGameUI->message( TheGameText->fetch( data->m_searchAndDestroyMessageLabel ) );
  517. if( m_searchAndDestroyAnnouncement.getEventName().isEmpty() == FALSE )
  518. {
  519. m_searchAndDestroyAnnouncement.setPosition( obj->getPosition() );
  520. TheAudio->addAudioEvent( &m_searchAndDestroyAnnouncement );
  521. }
  522. break;
  523. }
  524. break;
  525. }
  526. case TRANSITIONSTATUS_ACTIVE:
  527. setBattlePlan( m_currentPlan );
  528. switch( m_currentPlan )
  529. {
  530. case PLANSTATUS_BOMBARDMENT:
  531. obj->setModelConditionState( MODELCONDITION_DOOR_1_WAITING_TO_CLOSE );
  532. break;
  533. case PLANSTATUS_HOLDTHELINE:
  534. obj->setModelConditionState( MODELCONDITION_DOOR_2_WAITING_TO_CLOSE );
  535. break;
  536. case PLANSTATUS_SEARCHANDDESTROY:
  537. obj->setModelConditionState( MODELCONDITION_DOOR_3_WAITING_TO_CLOSE );
  538. if( m_searchAndDestroyIdle.getEventName().isNotEmpty() )
  539. {
  540. m_searchAndDestroyIdle.setObjectID( obj->getID() );
  541. m_searchAndDestroyIdle.setPlayingHandle( TheAudio->addAudioEvent( &m_searchAndDestroyIdle ) );
  542. }
  543. break;
  544. }
  545. break;
  546. case TRANSITIONSTATUS_PACKING:
  547. setBattlePlan( PLANSTATUS_NONE );
  548. switch( m_currentPlan )
  549. {
  550. case PLANSTATUS_BOMBARDMENT:
  551. obj->setModelConditionState( MODELCONDITION_DOOR_1_CLOSING );
  552. obj->getDrawable()->setAnimationLoopDuration( data->m_bombardmentPlanAnimationFrames );
  553. m_nextReadyFrame = now + data->m_bombardmentPlanAnimationFrames;
  554. if( m_bombardmentUnpack.getEventName().isNotEmpty() )
  555. {
  556. m_bombardmentPack.setObjectID( obj->getID() );
  557. m_bombardmentPack.setPlayingHandle( TheAudio->addAudioEvent( &m_bombardmentPack ) );
  558. }
  559. break;
  560. case PLANSTATUS_HOLDTHELINE:
  561. obj->setModelConditionState( MODELCONDITION_DOOR_2_CLOSING );
  562. obj->getDrawable()->setAnimationLoopDuration( data->m_holdTheLinePlanAnimationFrames );
  563. m_nextReadyFrame = now + data->m_holdTheLinePlanAnimationFrames;
  564. if( m_holdTheLineUnpack.getEventName().isNotEmpty() )
  565. {
  566. m_holdTheLinePack.setObjectID( obj->getID() );
  567. m_holdTheLinePack.setPlayingHandle( TheAudio->addAudioEvent( &m_holdTheLinePack ) );
  568. }
  569. break;
  570. case PLANSTATUS_SEARCHANDDESTROY:
  571. obj->setModelConditionState( MODELCONDITION_DOOR_3_CLOSING );
  572. obj->getDrawable()->setAnimationLoopDuration( data->m_searchAndDestroyPlanAnimationFrames );
  573. m_nextReadyFrame = now + data->m_searchAndDestroyPlanAnimationFrames;
  574. if( m_searchAndDestroyUnpack.getEventName().isNotEmpty() )
  575. {
  576. m_searchAndDestroyPack.setObjectID( obj->getID() );
  577. m_searchAndDestroyPack.setPlayingHandle( TheAudio->addAudioEvent( &m_searchAndDestroyPack ) );
  578. }
  579. break;
  580. }
  581. break;
  582. }
  583. //Set new status.
  584. m_status = newStatus;
  585. }
  586. //------------------------------------------------------------------------------------------------
  587. void BattlePlanUpdate::enableTurret( Bool enable )
  588. {
  589. AIUpdateInterface *ai = getObject()->getAI();
  590. if( ai )
  591. {
  592. WhichTurretType tur = ai->getWhichTurretForCurWeapon();
  593. if( tur != TURRET_INVALID )
  594. {
  595. ai->setTurretEnabled( tur, enable );
  596. }
  597. }
  598. }
  599. //------------------------------------------------------------------------------------------------
  600. void BattlePlanUpdate::recenterTurret()
  601. {
  602. AIUpdateInterface *ai = getObject()->getAI();
  603. if( ai )
  604. {
  605. WhichTurretType tur = ai->getWhichTurretForCurWeapon();
  606. if( tur != TURRET_INVALID )
  607. {
  608. ai->recenterTurret( tur );
  609. }
  610. }
  611. }
  612. //------------------------------------------------------------------------------------------------
  613. Bool BattlePlanUpdate::isTurretInNaturalPosition()
  614. {
  615. AIUpdateInterface *ai = getObject()->getAI();
  616. if( ai )
  617. {
  618. WhichTurretType tur = ai->getWhichTurretForCurWeapon();
  619. if( tur != TURRET_INVALID )
  620. {
  621. return ai->isTurretInNaturalPosition( tur );
  622. }
  623. }
  624. return false;
  625. }
  626. //------------------------------------------------------------------------------------------------
  627. static void paralyzeTroop( Object *obj, void *userData )
  628. {
  629. const BattlePlanUpdateModuleData *data = (BattlePlanUpdateModuleData*)userData;
  630. if( obj->isAnyKindOf( data->m_validMemberKindOf ) )
  631. {
  632. if( !obj->isAnyKindOf( data->m_invalidMemberKindOf ) )
  633. {
  634. obj->setDisabledUntil( DISABLED_PARALYZED, TheGameLogic->getFrame() + data->m_battlePlanParalyzeFrames );
  635. }
  636. }
  637. }
  638. //------------------------------------------------------------------------------------------------
  639. void BattlePlanUpdate::setBattlePlan( BattlePlanStatus plan )
  640. {
  641. const BattlePlanUpdateModuleData *data = getBattlePlanUpdateModuleData();
  642. Object *obj = getObject();
  643. Player *player = obj->getControllingPlayer();
  644. if( player )
  645. {
  646. switch( m_planAffectingArmy )
  647. {
  648. case PLANSTATUS_BOMBARDMENT:
  649. {
  650. //Remove the previous plan!
  651. player->changeBattlePlan( PLANSTATUS_BOMBARDMENT, -1, m_bonuses );
  652. //The only building bonus is actually the turret, and that's already handled!
  653. break;
  654. }
  655. case PLANSTATUS_HOLDTHELINE:
  656. {
  657. //Remove the previous plan!
  658. player->changeBattlePlan( PLANSTATUS_HOLDTHELINE, -1, m_bonuses );
  659. //Remove building health bonuses
  660. if( data->m_strategyCenterHoldTheLineMaxHealthScalar != 1.0f )
  661. {
  662. BodyModuleInterface *body = obj->getBodyModule();
  663. body->setMaxHealth( body->getMaxHealth() * 1.0f / data->m_strategyCenterHoldTheLineMaxHealthScalar, data->m_strategyCenterHoldTheLineMaxHealthChangeType );
  664. }
  665. break;
  666. }
  667. case PLANSTATUS_SEARCHANDDESTROY:
  668. {
  669. //Remove the previous plan!
  670. player->changeBattlePlan( PLANSTATUS_SEARCHANDDESTROY, -1, m_bonuses );
  671. //Remove sight range bonus
  672. if( data->m_strategyCenterSearchAndDestroySightRangeScalar != 1.0f )
  673. {
  674. obj->setVisionRange( obj->getVisionRange() * 1.0f / data->m_strategyCenterSearchAndDestroySightRangeScalar );
  675. obj->setShroudClearingRange( obj->getShroudClearingRange() * 1.0f / data->m_strategyCenterSearchAndDestroySightRangeScalar );
  676. }
  677. //Remove stealth detection
  678. if( data->m_strategyCenterSearchAndDestroyDetectsStealth )
  679. {
  680. static NameKeyType key_StealthDetectorUpdate = NAMEKEY( "StealthDetectorUpdate" );
  681. StealthDetectorUpdate *update = (StealthDetectorUpdate*)obj->findUpdateModule( key_StealthDetectorUpdate );
  682. if( update )
  683. {
  684. update->setSDEnabled( false );
  685. }
  686. }
  687. break;
  688. }
  689. }
  690. //Revert to default no-bonuses!
  691. m_bonuses->m_armorScalar = 1.0f;
  692. m_bonuses->m_sightRangeScalar = 1.0f;
  693. m_bonuses->m_bombardment = 0;
  694. m_bonuses->m_searchAndDestroy = 0;
  695. m_bonuses->m_holdTheLine = 0;
  696. //Add new bonuses!
  697. switch( plan )
  698. {
  699. case PLANSTATUS_NONE:
  700. //Paralyze troops!
  701. player->iterateObjects( paralyzeTroop, (void*)data );
  702. break;
  703. case PLANSTATUS_BOMBARDMENT:
  704. //Set the bombardment bonuses
  705. m_bonuses->m_bombardment = 1; //for weapon bonuses
  706. //Add the new plan!
  707. player->changeBattlePlan( PLANSTATUS_BOMBARDMENT, 1, m_bonuses );
  708. break;
  709. case PLANSTATUS_HOLDTHELINE:
  710. //Add building health bonuses
  711. if( data->m_strategyCenterHoldTheLineMaxHealthScalar )
  712. {
  713. BodyModuleInterface *body = obj->getBodyModule();
  714. body->setMaxHealth( body->getMaxHealth() * data->m_strategyCenterHoldTheLineMaxHealthScalar, data->m_strategyCenterHoldTheLineMaxHealthChangeType );
  715. }
  716. //Set the hold-the-line bonuses
  717. m_bonuses->m_armorScalar = data->m_holdTheLineArmorDamageScalar;
  718. m_bonuses->m_holdTheLine = 1; //for weapon bonuses
  719. //Add the new plan!
  720. player->changeBattlePlan( PLANSTATUS_HOLDTHELINE, 1, m_bonuses );
  721. break;
  722. case PLANSTATUS_SEARCHANDDESTROY:
  723. //Add sight range bonus
  724. if( data->m_strategyCenterSearchAndDestroySightRangeScalar != 1.0f )
  725. {
  726. obj->setVisionRange( obj->getVisionRange() * data->m_strategyCenterSearchAndDestroySightRangeScalar );
  727. obj->setShroudClearingRange( obj->getShroudClearingRange() * data->m_strategyCenterSearchAndDestroySightRangeScalar );
  728. }
  729. //Enable stealth detection
  730. if( data->m_strategyCenterSearchAndDestroyDetectsStealth )
  731. {
  732. static NameKeyType key_StealthDetectorUpdate = NAMEKEY( "StealthDetectorUpdate" );
  733. StealthDetectorUpdate *update = (StealthDetectorUpdate*)obj->findUpdateModule( key_StealthDetectorUpdate );
  734. if( update )
  735. {
  736. update->setSDEnabled( true );
  737. }
  738. }
  739. //Set the search-and-destroy bonuses
  740. m_bonuses->m_searchAndDestroy = 1; //for weapon bonuses
  741. m_bonuses->m_sightRangeScalar = data->m_searchAndDestroySightRangeScalar;
  742. //Add the new plan!
  743. player->changeBattlePlan( PLANSTATUS_SEARCHANDDESTROY, 1, m_bonuses );
  744. break;
  745. }
  746. m_planAffectingArmy = plan;
  747. }
  748. }
  749. //------------------------------------------------------------------------------------------------
  750. //Returns the currently active battle plan -- unpacked and ready... returns PLANSTATUS_NONE if in
  751. //transition!
  752. //------------------------------------------------------------------------------------------------
  753. BattlePlanStatus BattlePlanUpdate::getActiveBattlePlan() const
  754. {
  755. if( m_status == TRANSITIONSTATUS_ACTIVE )
  756. {
  757. return m_planAffectingArmy;
  758. }
  759. return PLANSTATUS_NONE;
  760. }
  761. //------------------------------------------------------------------------------------------------
  762. void BattlePlanUpdate::crc( Xfer *xfer )
  763. {
  764. // extend base class
  765. UpdateModule::crc( xfer );
  766. } // end crc
  767. //------------------------------------------------------------------------------------------------
  768. // Xfer method
  769. // Version Info:
  770. // 1: Initial version
  771. //------------------------------------------------------------------------------------------------
  772. void BattlePlanUpdate::xfer( Xfer *xfer )
  773. {
  774. // version
  775. XferVersion currentVersion = 1;
  776. XferVersion version = currentVersion;
  777. xfer->xferVersion( &version, currentVersion );
  778. // extend base class
  779. UpdateModule::xfer( xfer );
  780. // current plan
  781. xfer->xferUser( &m_currentPlan, sizeof( BattlePlanStatus ) );
  782. // desired plan
  783. xfer->xferUser( &m_desiredPlan, sizeof( BattlePlanStatus ) );
  784. // plan affecting army
  785. xfer->xferUser( &m_planAffectingArmy, sizeof( BattlePlanStatus ) );
  786. // status
  787. xfer->xferUser( &m_status, sizeof( TransitionStatus ) );
  788. // next ready frame
  789. xfer->xferUnsignedInt( &m_nextReadyFrame );
  790. // don't need to save this interface, it's retrived on object creation
  791. // SpecialPowerModuleInterface *m_specialPowerModule;
  792. // invalid settings
  793. xfer->xferBool( &m_invalidSettings );
  794. // centering turret
  795. xfer->xferBool( &m_centeringTurret );
  796. // bonuses
  797. xfer->xferReal( &m_bonuses->m_armorScalar );
  798. xfer->xferInt( &m_bonuses->m_bombardment );
  799. xfer->xferInt( &m_bonuses->m_searchAndDestroy );
  800. xfer->xferInt( &m_bonuses->m_holdTheLine );
  801. xfer->xferReal( &m_bonuses->m_sightRangeScalar );
  802. m_bonuses->m_validKindOf.xfer( xfer );
  803. m_bonuses->m_invalidKindOf.xfer( xfer );
  804. // vision object data
  805. xfer->xferObjectID( &m_visionObjectID );
  806. } // end xfer
  807. //------------------------------------------------------------------------------------------------
  808. void BattlePlanUpdate::loadPostProcess( void )
  809. {
  810. // extend base class
  811. UpdateModule::loadPostProcess();
  812. } // end loadPostProcess