StickyBombUpdate.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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: StickyBombUpdate.cpp //////////////////////////////////////////////////////////////////////
  24. // Author: Kris Morness, July 2002
  25. // Desc: Updates stickybomb to stay on target while it counts down to detonation (lifetime update)
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "GameLogic/Module/StickyBombUpdate.h"
  30. #include "Common/ThingTemplate.h"
  31. #include "Common/Player.h"
  32. #include "Common/Xfer.h"
  33. #include "GameClient/Drawable.h"
  34. #include "GameClient/FXList.h"
  35. #include "GameClient/InGameUI.h"
  36. #include "GameLogic/Object.h"
  37. #include "GameLogic/ObjectIter.h"
  38. #include "GameLogic/PartitionManager.h"
  39. #include "GameLogic/Weapon.h"
  40. #include "GameLogic/Module/LifetimeUpdate.h"
  41. #include "GameLogic/Module/AIUpdate.h"
  42. #include "GameLogic/Module/BodyModule.h"
  43. #include "GameLogic/GameLogic.h"
  44. // PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
  45. // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
  46. //-------------------------------------------------------------------------------------------------
  47. //-------------------------------------------------------------------------------------------------
  48. StickyBombUpdate::StickyBombUpdate( Thing *thing, const ModuleData *moduleData ) : UpdateModule( thing, moduleData )
  49. {
  50. m_targetID = INVALID_ID;
  51. m_dieFrame = 0;
  52. //Added By Sadullah Nader
  53. //Initialization(s) inserted
  54. m_nextPingFrame = 0;
  55. //
  56. setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
  57. }
  58. //-------------------------------------------------------------------------------------------------
  59. //-------------------------------------------------------------------------------------------------
  60. StickyBombUpdate::~StickyBombUpdate( void )
  61. {
  62. }
  63. //-------------------------------------------------------------------------------------------------
  64. void StickyBombUpdate::onObjectCreated()
  65. {
  66. //This first step is an initialization step. Immediately after the stickybomb is created,
  67. //the producerID is stored in the object to signify the shooter of this weapon. From there,
  68. //we can get the target. The target information will be used to determine where the sticky
  69. //bomb will go.
  70. ObjectID shooterID = getObject()->getProducerID();
  71. Object* shooter = TheGameLogic->findObjectByID( shooterID );
  72. if( shooter )
  73. {
  74. //Find the shooters target!
  75. AIUpdateInterface *ai = shooter->getAIUpdateInterface();
  76. if( ai )
  77. {
  78. Object *target = ai->getGoalObject();
  79. if( target )
  80. {
  81. initStickyBomb( target, NULL);
  82. }
  83. }
  84. }
  85. }
  86. //-------------------------------------------------------------------------------------------------
  87. void StickyBombUpdate::initStickyBomb( Object *target, const Object *bomber, const Coord3D *specificPos )
  88. {
  89. //Store the target.
  90. m_targetID = target ? target->getID() : INVALID_ID;
  91. //Set the target as the producer ID -- so we can access it from other areas.
  92. getObject()->setProducer( target );
  93. UnsignedInt now = TheGameLogic->getFrame();
  94. //Also determine our lifetime.... for countdown purposes.
  95. static NameKeyType key_LifetimeUpdate = NAMEKEY( "LifetimeUpdate" );
  96. LifetimeUpdate *update = (LifetimeUpdate*)getObject()->findUpdateModule( key_LifetimeUpdate );
  97. if( update )
  98. {
  99. //we are a timer bomb
  100. m_dieFrame = update->getDieFrame();
  101. //Calculate the number of seconds (rounded down)
  102. UnsignedInt pings = (m_dieFrame - now) / LOGICFRAMES_PER_SECOND;
  103. //Now determine the next frame we will make a "ping" sound.
  104. m_nextPingFrame = m_dieFrame - (pings * LOGICFRAMES_PER_SECOND);
  105. }
  106. else
  107. {
  108. //we are a remotely triggered bomb.
  109. m_dieFrame = 0;
  110. //Because we don't die -- make our ping in a second.
  111. m_nextPingFrame = now + LOGICFRAMES_PER_SECOND;
  112. }
  113. setWakeFrame( getObject(), UPDATE_SLEEP_NONE );
  114. if( target )
  115. {
  116. const StickyBombUpdateModuleData* d = getStickyBombUpdateModuleData();
  117. Coord3D pos = *target->getPosition();
  118. if( specificPos )
  119. {
  120. pos = *specificPos;
  121. pos.z = TheTerrainLogic->getGroundHeight(pos.x, pos.y);
  122. }
  123. else if(target->isKindOf( KINDOF_IMMOBILE ) && bomber )
  124. {
  125. // make this exception, if bomber has placed bomb on a structure
  126. // let the bomb just stay where it was first put, so a mine clearing unit can get to it later
  127. pos = *bomber->getPosition();
  128. pos.z = TheTerrainLogic->getGroundHeight(pos.x, pos.y);
  129. //keep it at ground height for mine clearing units to reach
  130. }
  131. else
  132. pos.z += d->m_offsetZ; // ride on the roof of the truck/tank
  133. getObject()->setPosition( &pos );
  134. if( getObject()->isKindOf(KINDOF_BOOBY_TRAP) )
  135. {
  136. // This kind of sticky bomb needs to set a status, so the poor victim can trigger us from assorted places
  137. target->setStatus( MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_BOOBY_TRAPPED) );
  138. }
  139. AudioEventRTS soundCreateBomb = *(getObject()->getTemplate()->getPerUnitSound("StickyBombCreated"));
  140. soundCreateBomb.setPosition( getObject()->getPosition() );
  141. TheAudio->addAudioEvent(&soundCreateBomb);
  142. }
  143. }
  144. //-------------------------------------------------------------------------------------------------
  145. UpdateSleepTime StickyBombUpdate::update( void )
  146. {
  147. // Continually reset position of stickybomb to match the position of the target.
  148. const Object *target = getTargetObject();
  149. Object *self = getObject();
  150. if( target )
  151. {
  152. if( target->isEffectivelyDead() )
  153. {
  154. //If the target is dead, then
  155. TheGameLogic->destroyObject( self );
  156. return UPDATE_SLEEP_NONE;
  157. }
  158. if ( target->isKindOf( KINDOF_IMMOBILE) )
  159. {
  160. const Coord3D *pos = self->getPosition();
  161. Coord3D newPos;
  162. newPos.x = pos->x;
  163. newPos.y = pos->y;
  164. newPos.z = TheTerrainLogic->getGroundHeight(newPos.x, newPos.y);
  165. //keep it at ground height for mine clearing units to reach
  166. self->setPosition( &newPos );
  167. }
  168. else // make the bomb follow the target around
  169. {
  170. const StickyBombUpdateModuleData* d = getStickyBombUpdateModuleData();
  171. const Coord3D *pos = target->getPosition();
  172. Coord3D newPos;
  173. newPos.x = pos->x;
  174. newPos.y = pos->y;
  175. newPos.z = pos->z + d->m_offsetZ;
  176. self->setPosition( &newPos );
  177. }
  178. }
  179. UnsignedInt now = TheGameLogic->getFrame();
  180. if( now >= m_nextPingFrame )
  181. {
  182. m_nextPingFrame += LOGICFRAMES_PER_SECOND;
  183. //Play the "ping" sound.
  184. AudioEventRTS sound = *self->getTemplate()->getPerUnitSound( "UnitBombPing" );
  185. sound.setObjectID( self->getID() );
  186. TheAudio->addAudioEvent( &sound );
  187. }
  188. return UPDATE_SLEEP_NONE;
  189. }
  190. //-------------------------------------------------------------------------------------------------
  191. Object* StickyBombUpdate::getTargetObject() const
  192. {
  193. return TheGameLogic->findObjectByID( m_targetID );
  194. }
  195. //-------------------------------------------------------------------------------------------------
  196. void StickyBombUpdate::setTargetObject( Object *obj )
  197. {
  198. m_targetID = obj ? obj->getID() : INVALID_ID;
  199. }
  200. //-------------------------------------------------------------------------------------------------
  201. void StickyBombUpdate::detonate()
  202. {
  203. const StickyBombUpdateModuleData *data = getStickyBombUpdateModuleData();
  204. Object* boobyTrappedObject = getTargetObject();
  205. if( data->m_geometryBasedDamageWeaponTemplate )
  206. {
  207. // We need to hurt people based on the size of the thing we are on. The radius in our weapon
  208. // is the radius beyond our borders
  209. if( boobyTrappedObject )
  210. {
  211. WeaponBonus nullBonus;
  212. Real boundingCircle = boobyTrappedObject->getGeometryInfo().getBoundingCircleRadius();
  213. Real primaryDamage = data->m_geometryBasedDamageWeaponTemplate->getPrimaryDamage(nullBonus);
  214. Real secondaryDamage = data->m_geometryBasedDamageWeaponTemplate->getSecondaryDamage(nullBonus);
  215. Real primaryDamageRange = data->m_geometryBasedDamageWeaponTemplate->getPrimaryDamageRadius(nullBonus);
  216. Real secondaryDamageRange = data->m_geometryBasedDamageWeaponTemplate->getSecondaryDamageRadius(nullBonus);
  217. primaryDamageRange += boundingCircle;
  218. secondaryDamageRange += boundingCircle;
  219. Real primaryDamageRangeSqr = sqr(primaryDamageRange);
  220. Real radius = max(primaryDamageRange, secondaryDamageRange);
  221. SimpleObjectIterator *iter;
  222. iter = ThePartitionManager->iterateObjectsInRange(boobyTrappedObject->getPosition(), radius, FROM_BOUNDINGSPHERE_3D);
  223. MemoryPoolObjectHolder hold(iter);
  224. Real curVictimDistSqr;
  225. Object *curVictim = iter->firstWithNumeric(&curVictimDistSqr);
  226. DamageInfo damageInfo;
  227. damageInfo.in.m_damageType = data->m_geometryBasedDamageWeaponTemplate->getDamageType();
  228. damageInfo.in.m_deathType = data->m_geometryBasedDamageWeaponTemplate->getDeathType();
  229. damageInfo.in.m_sourceID = getObject()->getID();
  230. damageInfo.in.m_sourcePlayerMask = getObject()->getControllingPlayer()->getPlayerMask();
  231. damageInfo.in.m_damageStatusType = data->m_geometryBasedDamageWeaponTemplate->getDamageStatusType();
  232. for (; curVictim != NULL; curVictim = iter ? iter->nextWithNumeric(&curVictimDistSqr) : NULL)
  233. {
  234. damageInfo.in.m_amount = (curVictimDistSqr <= primaryDamageRangeSqr) ? primaryDamage : secondaryDamage;
  235. curVictim->attemptDamage(&damageInfo);
  236. }
  237. if( data->m_geometryBasedDamageFX )
  238. {
  239. // And we make FX based on that size too.
  240. FXList::doFXPos(data->m_geometryBasedDamageFX, boobyTrappedObject->getPosition(), NULL, 0, NULL, secondaryDamageRange);
  241. }
  242. }
  243. }
  244. if( getObject()->isKindOf(KINDOF_BOOBY_TRAP) && boobyTrappedObject )
  245. {
  246. // This kind of sticky bomb needs to set a status, so the poor victim can trigger us from assorted places
  247. boobyTrappedObject->clearStatus( MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_BOOBY_TRAPPED) );
  248. }
  249. getObject()->kill();// Most things just fire weapons in their death modules
  250. }
  251. // ------------------------------------------------------------------------------------------------
  252. /** CRC */
  253. // ------------------------------------------------------------------------------------------------
  254. void StickyBombUpdate::crc( Xfer *xfer )
  255. {
  256. // extend base class
  257. UpdateModule::crc( xfer );
  258. } // end crc
  259. // ------------------------------------------------------------------------------------------------
  260. /** Xfer method
  261. * Version Info:
  262. * 1: Initial version */
  263. // ------------------------------------------------------------------------------------------------
  264. void StickyBombUpdate::xfer( Xfer *xfer )
  265. {
  266. // version
  267. XferVersion currentVersion = 1;
  268. XferVersion version = currentVersion;
  269. xfer->xferVersion( &version, currentVersion );
  270. // extend base class
  271. UpdateModule::xfer( xfer );
  272. // target id
  273. xfer->xferObjectID( &m_targetID );
  274. // die frame
  275. xfer->xferUnsignedInt( &m_dieFrame );
  276. //Next frame that a ping sound will play.
  277. xfer->xferUnsignedInt( &m_nextPingFrame );
  278. } // end xfer
  279. // ------------------------------------------------------------------------------------------------
  280. /** Load post process */
  281. // ------------------------------------------------------------------------------------------------
  282. void StickyBombUpdate::loadPostProcess( void )
  283. {
  284. // extend base class
  285. UpdateModule::loadPostProcess();
  286. } // end loadPostProcess