HackInternetAIUpdate.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  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. // HackInternetAIUpdate.cpp ////////////
  24. // Author: Kris Morness, June 2002
  25. // Desc: State machine that handles internet hacking (free cash)
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "Common/Player.h"
  28. #include "Common/ThingFactory.h"
  29. #include "Common/ThingTemplate.h"
  30. #include "GameClient/Drawable.h"
  31. #include "GameClient/InGameUI.h"
  32. #include "GameClient/GameText.h"
  33. #include "GameLogic/ExperienceTracker.h"
  34. #include "GameLogic/Module/BodyModule.h"
  35. #include "GameLogic/Module/ContainModule.h"
  36. #include "GameLogic/Module/HackInternetAIUpdate.h"
  37. #include "GameLogic/Module/PhysicsUpdate.h"
  38. #include "GameLogic/Object.h"
  39. //#include "GameLogic/PartitionManager.h"
  40. #ifdef _INTERNAL
  41. // for occasional debugging...
  42. //#pragma optimize("", off)
  43. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  44. #endif
  45. //-------------------------------------------------------------------------------------------------
  46. //-------------------------------------------------------------------------------------------------
  47. //-------------------------------------------------------------------------------------------------
  48. //-------------------------------------------------------------------------------------------------
  49. AIStateMachine* HackInternetAIUpdate::makeStateMachine()
  50. {
  51. return newInstance(HackInternetStateMachine)( getObject(), "HackInternetBasicAI");
  52. }
  53. //-------------------------------------------------------------------------------------------------
  54. HackInternetAIUpdate::HackInternetAIUpdate( Thing *thing, const ModuleData* moduleData ) : AIUpdateInterface( thing, moduleData )
  55. {
  56. m_hasPendingCommand = false;
  57. }
  58. //-------------------------------------------------------------------------------------------------
  59. HackInternetAIUpdate::~HackInternetAIUpdate( void )
  60. {
  61. }
  62. //-------------------------------------------------------------------------------------------------
  63. Bool HackInternetAIUpdate::isIdle() const
  64. {
  65. // we need to do this because we enter an idle state briefly between takeoff/landing in these cases,
  66. // but scripting relies on us never claiming to be "idle"...
  67. if (m_hasPendingCommand)
  68. return false;
  69. return AIUpdateInterface::isIdle();
  70. }
  71. //-------------------------------------------------------------------------------------------------
  72. Bool HackInternetAIUpdate::isHacking() const
  73. {
  74. if( getStateMachine()->getCurrentStateID() == HACK_INTERNET )
  75. {
  76. return true;
  77. }
  78. return false;
  79. }
  80. //-------------------------------------------------------------------------------------------------
  81. Bool HackInternetAIUpdate::isHackingPackingOrUnpacking() const
  82. {
  83. if( getStateMachine()->getCurrentStateID() == HACK_INTERNET ||
  84. getStateMachine()->getCurrentStateID() == PACKING ||
  85. getStateMachine()->getCurrentStateID() == UNPACKING )
  86. {
  87. return true;
  88. }
  89. return false;
  90. }
  91. //-------------------------------------------------------------------------------------------------
  92. UpdateSleepTime HackInternetAIUpdate::update( void )
  93. {
  94. // have to call our parent's isIdle, because we override it to never return true
  95. // when we have a pending command...
  96. if( AIUpdateInterface::isIdle() )
  97. {
  98. if( m_hasPendingCommand )
  99. {
  100. AICommandParms parms( AICMD_MOVE_TO_POSITION, CMD_FROM_AI ); // values don't matter, will be wiped by next line
  101. m_pendingCommand.reconstitute( parms );
  102. m_hasPendingCommand = false;
  103. aiDoCommand(&parms);
  104. }
  105. }
  106. /*UpdateSleepTime ret =*/ AIUpdateInterface::update();
  107. //return (mine < ret) ? mine : ret;
  108. /// @todo srj -- someday, make sleepy. for now, must not sleep.
  109. return UPDATE_SLEEP_NONE;
  110. }
  111. //-------------------------------------------------------------------------------------------------
  112. void HackInternetAIUpdate::aiDoCommand(const AICommandParms* parms)
  113. {
  114. if (!isAllowedToRespondToAiCommands(parms))
  115. return;
  116. //If our hacker is currently packing up his gear, we need to prevent him
  117. //from moving until completed. In order to accomplish this, we'll detect,
  118. //then
  119. if( getStateMachine()->getCurrentStateID() == HACK_INTERNET || getStateMachine()->getCurrentStateID() == PACKING )
  120. {
  121. // nuke any existing pending cmd
  122. m_pendingCommand.store(*parms);
  123. m_hasPendingCommand = true;
  124. if( getStateMachine()->getCurrentStateID() == HACK_INTERNET )
  125. {
  126. getStateMachine()->clear();
  127. setLastCommandSource( CMD_FROM_AI );
  128. getStateMachine()->setState( PACKING );
  129. }
  130. return;
  131. }
  132. m_hasPendingCommand = false;
  133. AIUpdateInterface::aiDoCommand(parms);
  134. }
  135. //-------------------------------------------------------------------------------------------------
  136. void HackInternetAIUpdate::hackInternet()
  137. {
  138. //if (m_hackInternetStateMachine)
  139. // m_hackInternetStateMachine->deleteInstance();
  140. //m_hackInternetStateMachine = NULL;
  141. // must make the state machine AFTER initing the other stuff, since it may inquire of its values...
  142. //m_hackInternetStateMachine = newInstance(HackInternetStateMachine)( getObject() );
  143. //m_hackInternetStateMachine->initDefaultState();
  144. #ifdef _DEBUG
  145. //m_hackInternetStateMachine->setName("HackInternetSpecificAI");
  146. #endif
  147. getStateMachine()->setState(UNPACKING);
  148. }
  149. // ------------------------------------------------------------------------------------------------
  150. UnsignedInt HackInternetAIUpdate::getUnpackTime() const
  151. {
  152. // Not yet contained at the time this is queried
  153. return getHackInternetAIUpdateModuleData()->m_unpackTime;
  154. }
  155. // ------------------------------------------------------------------------------------------------
  156. UnsignedInt HackInternetAIUpdate::getPackTime() const
  157. {
  158. if( getObject()->getContainedBy() != NULL )
  159. return 0; //We don't need to pack if exiting a building
  160. return getHackInternetAIUpdateModuleData()->m_packTime;
  161. }
  162. // ------------------------------------------------------------------------------------------------
  163. UnsignedInt HackInternetAIUpdate::getCashUpdateDelay() const
  164. {
  165. if( getObject()->getContainedBy() != NULL )
  166. return getHackInternetAIUpdateModuleData()->m_cashUpdateDelayFast;
  167. else
  168. return getHackInternetAIUpdateModuleData()->m_cashUpdateDelay;
  169. }
  170. // ------------------------------------------------------------------------------------------------
  171. /** CRC */
  172. // ------------------------------------------------------------------------------------------------
  173. void HackInternetAIUpdate::crc( Xfer *xfer )
  174. {
  175. // extend base class
  176. AIUpdateInterface::crc(xfer);
  177. } // end crc
  178. // ------------------------------------------------------------------------------------------------
  179. /** Xfer method
  180. * Version Info:
  181. * 1: Initial version */
  182. // ------------------------------------------------------------------------------------------------
  183. void HackInternetAIUpdate::xfer( Xfer *xfer )
  184. {
  185. // version
  186. XferVersion currentVersion = 1;
  187. XferVersion version = currentVersion;
  188. xfer->xferVersion( &version, currentVersion );
  189. // extend base class
  190. AIUpdateInterface::xfer(xfer);
  191. xfer->xferBool(&m_hasPendingCommand);
  192. if (m_hasPendingCommand) {
  193. m_pendingCommand.doXfer(xfer);
  194. }
  195. } // end xfer
  196. // ------------------------------------------------------------------------------------------------
  197. /** Load post process */
  198. // ------------------------------------------------------------------------------------------------
  199. void HackInternetAIUpdate::loadPostProcess( void )
  200. {
  201. // extend base class
  202. AIUpdateInterface::loadPostProcess();
  203. } // end loadPostProcess
  204. //-------------------------------------------------------------------------------------------------
  205. //-------------------------------------------------------------------------------------------------
  206. //-------------------------------------------------------------------------------------------------
  207. //-------------------------------------------------------------------------------------------------
  208. HackInternetStateMachine::HackInternetStateMachine( Object *owner, AsciiString name ) : AIStateMachine( owner, "HackInternetStateMachine" )
  209. {
  210. //HackInternetAIUpdate *ai = (HackInternetAIUpdate*)owner->getAIUpdateInterface();
  211. // order matters: first state is the default state.
  212. defineState( UNPACKING, newInstance(UnpackingState)( this ), HACK_INTERNET, HACK_INTERNET );
  213. defineState( HACK_INTERNET, newInstance(HackInternetState)( this ), PACKING, PACKING );
  214. defineState( PACKING, newInstance(PackingState)( this ), AI_IDLE, AI_IDLE );
  215. }
  216. //-------------------------------------------------------------------------------------------------
  217. HackInternetStateMachine::~HackInternetStateMachine()
  218. {
  219. }
  220. // ------------------------------------------------------------------------------------------------
  221. /** CRC */
  222. // ------------------------------------------------------------------------------------------------
  223. void UnpackingState::crc( Xfer *xfer )
  224. {
  225. } // end crc
  226. // ------------------------------------------------------------------------------------------------
  227. /** Xfer Method */
  228. // ------------------------------------------------------------------------------------------------
  229. void UnpackingState::xfer( Xfer *xfer )
  230. {
  231. // version
  232. XferVersion currentVersion = 1;
  233. XferVersion version = currentVersion;
  234. xfer->xferVersion( &version, currentVersion );
  235. xfer->xferUnsignedInt(&m_framesRemaining);
  236. } // end xfer
  237. // ------------------------------------------------------------------------------------------------
  238. /** Load post process */
  239. // ------------------------------------------------------------------------------------------------
  240. void UnpackingState::loadPostProcess( void )
  241. {
  242. } // end loadPostProcess
  243. //-------------------------------------------------------------------------------------------------
  244. StateReturnType UnpackingState::onEnter()
  245. {
  246. Object *owner = getMachineOwner();
  247. HackInternetAIUpdate *ai = (HackInternetAIUpdate*)owner->getAIUpdateInterface();
  248. if( !ai )
  249. {
  250. return STATE_FAILURE;
  251. }
  252. owner->clearModelConditionFlags( MAKE_MODELCONDITION_MASK3( MODELCONDITION_PACKING, MODELCONDITION_FIRING_A, MODELCONDITION_UNPACKING ) );
  253. owner->setModelConditionState( MODELCONDITION_UNPACKING );
  254. AudioEventRTS sound = *owner->getTemplate()->getPerUnitSound( "UnitUnpack" );
  255. sound.setObjectID( owner->getID() );
  256. TheAudio->addAudioEvent( &sound );
  257. Real variationFactor = ai->getPackUnpackVariationFactor();
  258. Real variation = GameLogicRandomValueReal( 1.0f - variationFactor, 1.0f + variationFactor );
  259. m_framesRemaining = ai->getUnpackTime() * variation; //In frames
  260. owner->getDrawable()->setAnimationLoopDuration( m_framesRemaining );
  261. return STATE_CONTINUE;
  262. }
  263. //-------------------------------------------------------------------------------------------------
  264. StateReturnType UnpackingState::update()
  265. {
  266. Object *owner = getMachineOwner();
  267. // HackInternetAIUpdate *ai = (HackInternetAIUpdate*)owner->getAIUpdateInterface();
  268. // This is a bit hacky, no pun intended, but if this Update is engeged specialability (disablebuilding)
  269. // The unpacking modelconditionflag gets cleared by specialability::cleanup() after my onEnter() sets it!
  270. // Why HackInterent wasn't included within specialability I can't figure out, but... too late to change now.
  271. owner->setModelConditionState( MODELCONDITION_UNPACKING );
  272. if( m_framesRemaining > 0 )
  273. {
  274. m_framesRemaining--;
  275. }
  276. else
  277. {
  278. return STATE_SUCCESS;
  279. }
  280. return STATE_CONTINUE;
  281. }
  282. //-------------------------------------------------------------------------------------------------
  283. void UnpackingState::onExit( StateExitType status )
  284. {
  285. Object *owner = getMachineOwner();
  286. owner->clearModelConditionState( MODELCONDITION_UNPACKING );
  287. }
  288. //-------------------------------------------------------------------------------------------------
  289. //-------------------------------------------------------------------------------------------------
  290. //-------------------------------------------------------------------------------------------------
  291. // ------------------------------------------------------------------------------------------------
  292. /** CRC */
  293. // ------------------------------------------------------------------------------------------------
  294. void PackingState::crc( Xfer *xfer )
  295. {
  296. } // end crc
  297. // ------------------------------------------------------------------------------------------------
  298. /** Xfer Method */
  299. // ------------------------------------------------------------------------------------------------
  300. void PackingState::xfer( Xfer *xfer )
  301. {
  302. // version
  303. XferVersion currentVersion = 1;
  304. XferVersion version = currentVersion;
  305. xfer->xferVersion( &version, currentVersion );
  306. xfer->xferUnsignedInt(&m_framesRemaining);
  307. } // end xfer
  308. // ------------------------------------------------------------------------------------------------
  309. /** Load post process */
  310. // ------------------------------------------------------------------------------------------------
  311. void PackingState::loadPostProcess( void )
  312. {
  313. } // end loadPostProcess
  314. //-------------------------------------------------------------------------------------------------
  315. StateReturnType PackingState::onEnter()
  316. {
  317. Object *owner = getMachineOwner();
  318. HackInternetAIUpdate *ai = (HackInternetAIUpdate*)owner->getAIUpdateInterface();
  319. if( !ai )
  320. {
  321. return STATE_FAILURE;
  322. }
  323. owner->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK( MODELCONDITION_FIRING_A ),
  324. MAKE_MODELCONDITION_MASK( MODELCONDITION_PACKING ) );
  325. AudioEventRTS sound = *owner->getTemplate()->getPerUnitSound( "UnitPack" );
  326. sound.setObjectID( owner->getID() );
  327. TheAudio->addAudioEvent( &sound );
  328. Real variationFactor = ai->getPackUnpackVariationFactor();
  329. Real variation = GameLogicRandomValueReal( 1.0f - variationFactor, 1.0f + variationFactor );
  330. m_framesRemaining = ai->getPackTime() * variation; //In frames
  331. owner->getDrawable()->setAnimationLoopDuration( m_framesRemaining );
  332. return STATE_CONTINUE;
  333. }
  334. //-------------------------------------------------------------------------------------------------
  335. StateReturnType PackingState::update()
  336. {
  337. // Object *owner = getMachineOwner();
  338. // HackInternetAIUpdate *ai = (HackInternetAIUpdate*)owner->getAIUpdateInterface();
  339. if( m_framesRemaining > 0 )
  340. {
  341. m_framesRemaining--;
  342. }
  343. else
  344. {
  345. return STATE_SUCCESS;
  346. }
  347. return STATE_CONTINUE;
  348. }
  349. //-------------------------------------------------------------------------------------------------
  350. void PackingState::onExit( StateExitType status )
  351. {
  352. Object *owner = getMachineOwner();
  353. owner->clearModelConditionState( MODELCONDITION_PACKING );
  354. }
  355. // ------------------------------------------------------------------------------------------------
  356. /** CRC */
  357. // ------------------------------------------------------------------------------------------------
  358. void HackInternetState::crc( Xfer *xfer )
  359. {
  360. } // end crc
  361. // ------------------------------------------------------------------------------------------------
  362. /** Xfer Method */
  363. // ------------------------------------------------------------------------------------------------
  364. void HackInternetState::xfer( Xfer *xfer )
  365. {
  366. // version
  367. XferVersion currentVersion = 1;
  368. XferVersion version = currentVersion;
  369. xfer->xferVersion( &version, currentVersion );
  370. xfer->xferUnsignedInt(&m_framesRemaining);
  371. } // end xfer
  372. // ------------------------------------------------------------------------------------------------
  373. /** Load post process */
  374. // ------------------------------------------------------------------------------------------------
  375. void HackInternetState::loadPostProcess( void )
  376. {
  377. } // end loadPostProcess
  378. //-------------------------------------------------------------------------------------------------
  379. StateReturnType HackInternetState::onEnter()
  380. {
  381. //Go into the hack internet stance.
  382. Object *owner = getMachineOwner();
  383. HackInternetAIUpdate *ai = (HackInternetAIUpdate*)owner->getAIUpdateInterface();
  384. if( !ai )
  385. {
  386. return STATE_FAILURE;
  387. }
  388. owner->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK( MODELCONDITION_UNPACKING ),
  389. MAKE_MODELCONDITION_MASK( MODELCONDITION_FIRING_A ) );
  390. m_framesRemaining = ai->getCashUpdateDelay();
  391. return STATE_CONTINUE;
  392. }
  393. //-------------------------------------------------------------------------------------------------
  394. StateReturnType HackInternetState::update()
  395. {
  396. Object *owner = getMachineOwner();
  397. HackInternetAIUpdate *ai = (HackInternetAIUpdate*)owner->getAIUpdateInterface();
  398. if( !ai )
  399. {
  400. return STATE_FAILURE;
  401. }
  402. if( owner->isDisabledByType( DISABLED_HACKED ) )
  403. {
  404. //Don't hack while hacked, hehe.
  405. return STATE_CONTINUE;
  406. }
  407. if( m_framesRemaining > 0 )
  408. {
  409. //Decrement frame counter.
  410. m_framesRemaining--;
  411. }
  412. else
  413. {
  414. //We have waited the full amount of the delay, so hack some cash from the heavens!
  415. //Add cash
  416. Money *money = owner->getControllingPlayer()->getMoney();
  417. if( money )
  418. {
  419. ExperienceTracker *xp = owner->getExperienceTracker();
  420. if( xp )
  421. {
  422. UnsignedInt amount = 0;
  423. switch( xp->getVeterancyLevel() )
  424. {
  425. case LEVEL_HEROIC:
  426. amount = ai->getHeroicCashAmount();
  427. if( amount )
  428. {
  429. break;
  430. }
  431. //If entry missing, fall through!
  432. case LEVEL_ELITE:
  433. amount = ai->getEliteCashAmount();
  434. if( amount )
  435. {
  436. break;
  437. }
  438. //If entry missing, fall through!
  439. case LEVEL_VETERAN:
  440. amount = ai->getVeteranCashAmount();
  441. if( amount )
  442. {
  443. break;
  444. }
  445. //If entry missing, fall through!
  446. case LEVEL_REGULAR:
  447. amount = ai->getRegularCashAmount();
  448. if( amount )
  449. {
  450. break;
  451. }
  452. //If entry missing, fall through!
  453. default:
  454. amount = 1;
  455. break;
  456. }
  457. money->deposit( amount );
  458. owner->getControllingPlayer()->getScoreKeeper()->addMoneyEarned( amount );
  459. //Grant the unit some experience for a successful hack.
  460. xp->addExperiencePoints( ai->getXpPerCashUpdate() );
  461. Bool displayMoney = TRUE;
  462. if( owner->testStatus(OBJECT_STATUS_STEALTHED) )
  463. {
  464. // OY LOOK! I AM USING LOCAL PLAYER. Do not put anything other than TheInGameUI->addFloatingText in the block this controls!!!
  465. if( !owner->isLocallyControlled() && !owner->testStatus(OBJECT_STATUS_DETECTED) )
  466. {
  467. displayMoney = FALSE;
  468. }
  469. }
  470. if( owner->getContainedBy() && owner->getContainedBy()->testStatus(OBJECT_STATUS_STEALTHED) )
  471. {
  472. // OY LOOK! I AM USING LOCAL PLAYER. Do not put anything other than TheInGameUI->addFloatingText in the block this controls!!!
  473. if( !owner->getContainedBy()->isLocallyControlled() && !owner->getContainedBy()->testStatus(OBJECT_STATUS_DETECTED) )
  474. {
  475. displayMoney = FALSE;
  476. }
  477. }
  478. if( displayMoney )
  479. {
  480. // OY LOOK! I AM USING LOCAL PLAYER. Do not put anything other than TheInGameUI->addFloatingText in the block this controls!!!
  481. //Display cash income floating over the hacker.
  482. UnicodeString moneyString;
  483. moneyString.format( TheGameText->fetch( "GUI:AddCash" ), amount );
  484. Coord3D pos;
  485. pos.zero();
  486. pos.add( owner->getPosition() );
  487. pos.z += 20.0f; //add a little z to make it show up above the unit.
  488. Object *internetCenter = owner->getContainedBy();
  489. if ( internetCenter )
  490. {
  491. Real width = internetCenter->getGeometryInfo().getMajorRadius() * 0.3f;
  492. Real depth = internetCenter->getGeometryInfo().getMinorRadius() * 0.3f;
  493. pos.x += GameClientRandomValue(-width,width);
  494. pos.y += GameClientRandomValue(-depth,depth);
  495. }
  496. TheInGameUI->addFloatingText( moneyString, &pos, GameMakeColor( 0, 255, 0, 255 ) );
  497. }
  498. AudioEventRTS sound = *(owner->getTemplate()->getPerUnitSound( "UnitCashPing" ));
  499. sound.setObjectID( owner->getID() );
  500. TheAudio->addAudioEvent( &sound );
  501. }
  502. }
  503. //Reset timer and start a new cycle.
  504. m_framesRemaining = ai->getCashUpdateDelay();
  505. }
  506. //This is a persistent state until told otherwise.
  507. return STATE_CONTINUE;
  508. }
  509. //-------------------------------------------------------------------------------------------------
  510. void HackInternetState::onExit( StateExitType status )
  511. {
  512. Object *owner = getMachineOwner();
  513. owner->clearModelConditionState( MODELCONDITION_FIRING_A );
  514. }