EMPUpdate.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. ** Command & Conquer Generals(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: EMPUpdate.cpp ///////////////////////////////////////////////////////////////////////
  24. // Author: Mark Lorenzen Sept. 2002
  25. // Desc: Update that makes the electromagnetic pulse field grow, fade and disable junk
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/Thing.h"
  30. #include "Common/ThingTemplate.h"
  31. #include "Common/INI.h"
  32. #include "Common/RandomValue.h"
  33. #include "Common/Player.h"
  34. #include "GameLogic/GameLogic.h"
  35. #include "GameLogic/Module/EMPUpdate.h"
  36. #include "GameLogic/ObjectIter.h"
  37. #include "GameLogic/PartitionManager.h"
  38. #include "GameLogic/Module/AIUpdate.h"
  39. #include "GameLogic/Object.h"
  40. #include "GameClient/Drawable.h"
  41. #include "Common/KindOf.h"
  42. #include "GameClient/ParticleSys.h"
  43. #ifdef _INTERNAL
  44. // for occasional debugging...
  45. //#pragma optimize("", off)
  46. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  47. #endif
  48. ///////////////////////////////////////////////////////////////////////////////////////////
  49. // Static member initialization
  50. ///////////////////////////////////////////////////////////////////////////////////////////
  51. //Bool EMPUpdate::s_lastInstanceSpunPositive = FALSE;
  52. //-------------------------------------------------------------------------------------------------
  53. static void saturateRGB(RGBColor& color, Real factor)
  54. {
  55. color.red *= factor;
  56. color.green *= factor;
  57. color.blue *= factor;
  58. Real halfFactor = factor * 0.5f;
  59. color.red -= halfFactor;
  60. color.green -= halfFactor;
  61. color.blue -= halfFactor;
  62. }
  63. //-------------------------------------------------------------------------------------------------
  64. //-------------------------------------------------------------------------------------------------
  65. EMPUpdate::EMPUpdate( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  66. {
  67. //s_lastInstanceSpunPositive = !s_lastInstanceSpunPositive; //TOGGLES STATIC BOOL
  68. const EMPUpdateModuleData *data = getEMPUpdateModuleData();
  69. if ( data )
  70. {
  71. //SANITY
  72. DEBUG_ASSERTCRASH( TheGameLogic, ("EMPUpdate::EMPUpdate - TheGameLogic is NULL\n" ) );
  73. UnsignedInt now = TheGameLogic->getFrame();
  74. m_currentScale = data->m_startScale;
  75. m_dieFrame = REAL_TO_UNSIGNEDINT( now + data->m_lifeFrames );
  76. m_tintEnvPlayFrame = REAL_TO_UNSIGNEDINT( now + data->m_startFadeFrame );
  77. m_tintEnvFadeFrames = m_dieFrame - m_tintEnvPlayFrame;
  78. //m_spinRate = GameLogicRandomValueReal(data->m_spinRateMax * 0.5f, data->m_spinRateMax);
  79. m_targetScale = GameLogicRandomValueReal(data->m_targetScaleMin, data->m_targetScaleMax);
  80. //if (s_lastInstanceSpunPositive)
  81. //{
  82. // m_spinRate *= -1.0f;
  83. //}
  84. getObject()->setOrientation(GameLogicRandomValueReal(-PI,PI));
  85. DEBUG_ASSERTCRASH( m_tintEnvPlayFrame < m_dieFrame, ("EMPUpdate::EMPUpdate - you cant play fade after death\n" ) );
  86. return;
  87. }
  88. //SANITY
  89. DEBUG_ASSERTCRASH( data, ("EMPUpdate::EMPUpdate - getEMPUpdateModuleData is NULL\n" ) );
  90. m_currentScale = 1.0f;
  91. m_dieFrame = 0;
  92. m_tintEnvFadeFrames = 0;
  93. m_tintEnvPlayFrame = 0;
  94. //m_spinRate = 0;
  95. m_targetScale = 1;
  96. }
  97. //-------------------------------------------------------------------------------------------------
  98. //-------------------------------------------------------------------------------------------------
  99. EMPUpdate::~EMPUpdate( void )
  100. {
  101. }
  102. //-------------------------------------------------------------------------------------------------
  103. //-------------------------------------------------------------------------------------------------
  104. UpdateSleepTime EMPUpdate::update( void )
  105. {
  106. /// @todo srj use SLEEPY_UPDATE here
  107. Object *obj = getObject();
  108. const EMPUpdateModuleData *data = getEMPUpdateModuleData();
  109. Drawable *dr = obj->getDrawable();
  110. UnsignedInt now = TheGameLogic->getFrame();
  111. m_currentScale += ( m_targetScale - m_currentScale ) * 0.05f;
  112. dr->setInstanceScale( m_currentScale );
  113. if ( now < m_tintEnvPlayFrame)
  114. {
  115. RGBColor start = data->m_startColor;
  116. saturateRGB( start, 2 );
  117. dr->colorTint( &start );
  118. }
  119. if ( now == m_tintEnvPlayFrame)
  120. {
  121. RGBColor end = data->m_endColor;
  122. saturateRGB( end, 5 );
  123. dr->colorFlash( &end, 9999, m_tintEnvFadeFrames, TRUE );
  124. doDisableAttack();
  125. }
  126. //Real curSpin = obj->getOrientation();
  127. //curSpin += m_spinRate;
  128. //curSpin = normalizeAngle(curSpin);
  129. //obj->setOrientation( curSpin );
  130. if( now >= m_dieFrame )
  131. obj->kill();
  132. return UPDATE_SLEEP_NONE;
  133. }
  134. //-------------------------------------------------------------------------------------------------
  135. //-------------------------------------------------------------------------------------------------
  136. void EMPUpdate::doDisableAttack( void )
  137. {
  138. Object *object = getObject();
  139. const EMPUpdateModuleData *data = getEMPUpdateModuleData();
  140. if( !object || !data )
  141. return; //sanity
  142. Real radius = 200.0f; ///@todo kluge
  143. Real curVictimDistSqr;
  144. const Coord3D *pos = object->getPosition();
  145. SimpleObjectIterator *iter;
  146. Object *curVictim;
  147. if (radius > 0.0f)
  148. {
  149. iter = ThePartitionManager->iterateObjectsInRange(pos,
  150. radius, FROM_BOUNDINGSPHERE_3D);
  151. curVictim = iter->firstWithNumeric(&curVictimDistSqr);
  152. }
  153. MemoryPoolObjectHolder hold(iter);
  154. for ( ; curVictim != NULL; curVictim = iter ? iter->nextWithNumeric(&curVictimDistSqr) : NULL)
  155. {
  156. if ( curVictim != object)
  157. {
  158. if ( !curVictim->isKindOf( KINDOF_VEHICLE ) && !curVictim->isKindOf(KINDOF_STRUCTURE) && !curVictim->isKindOf(KINDOF_SPAWNS_ARE_THE_WEAPONS) )
  159. {
  160. //DONT DISABLE PEOPLE, EXCEPT FOR STINGER SOLDIERS
  161. continue;
  162. }
  163. else if ( curVictim->isKindOf( KINDOF_AIRCRAFT ) && curVictim->isAirborneTarget() )
  164. {
  165. if ( curVictim->isKindOf( KINDOF_TRANSPORT ) && curVictim->getRelationship( object ) == ALLIES)
  166. continue;//DONT DISABLE YOUR OWN TRANSPORT PLANES
  167. curVictim->kill();// @todo this should use some sort of DEADSTICK DIE or something...
  168. Drawable *drw = curVictim->getDrawable();
  169. if ( drw )
  170. {
  171. drw->setTintStatus( TINT_STATUS_DISABLED );// paint it black
  172. }
  173. continue;
  174. }
  175. else if ( curVictim->isKindOf( KINDOF_STRUCTURE ) )
  176. {
  177. if ( ! curVictim->isFactionStructure() )
  178. continue;
  179. }
  180. //Disable the target for a specified amount of time.
  181. curVictim->setDisabledUntil( DISABLED_EMP, TheGameLogic->getFrame() + data->m_disabledDuration );
  182. Drawable *drw = curVictim->getDrawable();
  183. if ( drw )
  184. {
  185. const ParticleSystemTemplate *tmp = data->m_disableFXParticleSystem;
  186. if (tmp)
  187. {
  188. Real victimHeight = curVictim->getGeometryInfo().getMaxHeightAbovePosition();
  189. Real victimFootprintArea = curVictim->getGeometryInfo().getFootprintArea();
  190. Real victimVolume = victimFootprintArea * MIN(victimHeight, 10.0f);
  191. UnsignedInt emitterCount = MAX(15, REAL_TO_INT_CEIL(data->m_sparksPerCubicFoot * victimVolume));
  192. for (Int e = 0 ; e < emitterCount; ++e)
  193. {
  194. ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(tmp);
  195. if (sys)
  196. {
  197. Coord3D offs = {0,0,0};
  198. curVictim->getGeometryInfo().makeRandomOffsetWithinFootprint( offs );
  199. offs.z = GameLogicRandomValue(3, victimHeight);
  200. //This puts all the sparks within a quadrahemicycloid (rectangular dome) volume
  201. //The same shape as a four cornered camping dome tent, for those with less Greek
  202. if (offs.length() > victimHeight)
  203. {
  204. Real resoreX = offs.x;
  205. Real resoreY = offs.y;
  206. offs.normalize();
  207. offs.z *= victimHeight;
  208. offs.x = resoreX;
  209. offs.y = resoreY;
  210. }
  211. sys->attachToObject(curVictim);
  212. sys->setPosition( &offs );
  213. sys->setSystemLifetime(MAX(0, data->m_disabledDuration - 30));
  214. sys->setInitialDelay(GameLogicRandomValue(1,100));
  215. }
  216. }
  217. }
  218. }
  219. }
  220. }
  221. }
  222. // ------------------------------------------------------------------------------------------------
  223. /** CRC */
  224. // ------------------------------------------------------------------------------------------------
  225. void EMPUpdate::crc( Xfer *xfer )
  226. {
  227. } // end crc
  228. // ------------------------------------------------------------------------------------------------
  229. /** Xfer method
  230. * Version Info:
  231. * 1: Initial version */
  232. // ------------------------------------------------------------------------------------------------
  233. void EMPUpdate::xfer( Xfer *xfer )
  234. {
  235. // version
  236. XferVersion currentVersion = 1;
  237. XferVersion version = currentVersion;
  238. xfer->xferVersion( &version, currentVersion );
  239. } // end xfer
  240. // ------------------------------------------------------------------------------------------------
  241. /** Load post process */
  242. // ------------------------------------------------------------------------------------------------
  243. void EMPUpdate::loadPostProcess( void )
  244. {
  245. } // end loadPostProcess