LaserUpdate.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  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: LaserUpdate.cpp //////////////////////////////////////////////////////////////////////////
  24. // Author: Kris Morness, July 2002
  25. // Desc: Handles laser update processing for render purposes and game control.
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common\Player.h"
  30. #include "Common\PlayerList.h"
  31. #include "Common/Xfer.h"
  32. #include "Common/DrawModule.h"
  33. #include "Common/ThingTemplate.h"
  34. #include "GameClient/Drawable.h"
  35. #include "GameClient/GameClient.h"
  36. #include "GameClient/ParticleSys.h"
  37. #include "GameLogic/Object.h"
  38. #include "GameLogic/GameLogic.h" // For frame number
  39. #include "GameLogic/Module/LaserUpdate.h"
  40. #include "WWMath/Vector3.h"
  41. #ifdef _INTERNAL
  42. // for occasional debugging...
  43. //#pragma optimize("", off)
  44. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  45. #endif
  46. //-------------------------------------------------------------------------------------------------
  47. //-------------------------------------------------------------------------------------------------
  48. LaserUpdateModuleData::LaserUpdateModuleData()
  49. {
  50. m_punchThroughScalar = 0.0f;
  51. }
  52. //-------------------------------------------------------------------------------------------------
  53. /*static*/ void LaserUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  54. {
  55. ModuleData::buildFieldParse(p);
  56. static const FieldParse dataFieldParse[] =
  57. {
  58. { "MuzzleParticleSystem", INI::parseAsciiString, NULL, offsetof( LaserUpdateModuleData, m_particleSystemName ) },
  59. { "TargetParticleSystem", INI::parseAsciiString, NULL, offsetof( LaserUpdateModuleData, m_targetParticleSystemName ) },
  60. { "PunchThroughScalar", INI::parseReal, NULL, offsetof( LaserUpdateModuleData, m_punchThroughScalar ) },
  61. { 0, 0, 0, 0 }
  62. };
  63. p.add(dataFieldParse);
  64. }
  65. //-------------------------------------------------------------------------------------------------
  66. //-------------------------------------------------------------------------------------------------
  67. LaserUpdate::LaserUpdate( Thing *thing, const ModuleData* moduleData ) : ClientUpdateModule( thing, moduleData )
  68. {
  69. //Added By Sadullah Nader
  70. //Initialization missing and needed
  71. m_dirty = FALSE;
  72. m_endPos.zero();
  73. m_startPos.zero();
  74. //
  75. m_particleSystemID = INVALID_PARTICLE_SYSTEM_ID;
  76. m_targetParticleSystemID = INVALID_PARTICLE_SYSTEM_ID;
  77. m_widening = false;
  78. m_widenStartFrame = 0;
  79. m_widenFinishFrame = 0;
  80. m_currentWidthScalar = 1.0f;
  81. m_decaying = false;
  82. m_decayStartFrame = 0;
  83. m_decayFinishFrame = 0;
  84. m_parentID = INVALID_DRAWABLE_ID;
  85. m_targetID = INVALID_DRAWABLE_ID;
  86. m_parentBoneName.clear();
  87. }
  88. //-------------------------------------------------------------------------------------------------
  89. //-------------------------------------------------------------------------------------------------
  90. LaserUpdate::~LaserUpdate( void )
  91. {
  92. if( m_particleSystemID )
  93. TheParticleSystemManager->destroyParticleSystemByID( m_particleSystemID );
  94. if( m_targetParticleSystemID )
  95. TheParticleSystemManager->destroyParticleSystemByID( m_targetParticleSystemID );
  96. }
  97. //-------------------------------------------------------------------------------------------------
  98. void LaserUpdate::updateStartPos()
  99. {
  100. Coord3D oldStartPos = m_startPos;
  101. if( m_parentID == INVALID_DRAWABLE_ID )
  102. return;// Can't update if not told to update
  103. const Drawable *parentDrawable = TheGameClient->findDrawableByID(m_parentID);
  104. if( parentDrawable == NULL )
  105. return;// Can't update if no one to ask
  106. if( m_parentBoneName.isNotEmpty() )
  107. {
  108. Matrix3D startPosMatrix;
  109. if( !parentDrawable->getCurrentWorldspaceClientBonePositions( m_parentBoneName.str(), startPosMatrix ) )
  110. {
  111. // failed to find the required bone, so just die
  112. //Kris: Doing this CRASHES THE GAME LATER!!!! Instead, let's set the position to the drawable, then
  113. // create a nasty assert.
  114. //TheGameClient->destroyDrawable( getDrawable() );
  115. m_startPos.set( parentDrawable->getPosition() );
  116. DEBUG_CRASH( ("LaserUpdate::updateStartPos() -- Drawable %s is expecting to find a bone %s but can't. Defaulting to position of drawable.",
  117. parentDrawable->getTemplate()->getName().str(), m_parentBoneName.str() ) );
  118. return;
  119. }
  120. m_startPos.x = startPosMatrix.Get_X_Translation();
  121. m_startPos.y = startPosMatrix.Get_Y_Translation();
  122. m_startPos.z = startPosMatrix.Get_Z_Translation();
  123. }
  124. else
  125. {
  126. // Just use parent position
  127. m_startPos = *parentDrawable->getPosition();
  128. }
  129. if( !(m_startPos == oldStartPos) ) // No != operator. Heh.
  130. m_dirty = TRUE;
  131. }
  132. //-------------------------------------------------------------------------------------------------
  133. void LaserUpdate::updateEndPos()
  134. {
  135. const LaserUpdateModuleData *data = getLaserUpdateModuleData();
  136. Coord3D oldEndPos = m_endPos;
  137. if( m_targetID == INVALID_DRAWABLE_ID )
  138. return;// Can't update if not told to update
  139. const Drawable *targetDrawable = TheGameClient->findDrawableByID(m_targetID);
  140. Bool targetDead = (targetDrawable && targetDrawable->getObject())
  141. ? targetDrawable->getObject()->isEffectivelyDead()
  142. : FALSE;
  143. if( targetDrawable == NULL || targetDead )
  144. {
  145. // If here, we used to track something, but now it is gone. So make our end point pierce through
  146. // the old spot, and then stop trying to find a target Drawable
  147. if( data->m_punchThroughScalar > 0 )
  148. {
  149. Vector3 laserVector;
  150. laserVector.Set(m_endPos.x, m_endPos.y, m_endPos.z);
  151. laserVector = laserVector - Vector3(m_startPos.x, m_startPos.y, m_startPos.z);
  152. laserVector *= data->m_punchThroughScalar;
  153. laserVector = laserVector + Vector3(m_startPos.x, m_startPos.y, m_startPos.z);
  154. m_endPos.x = laserVector.X;
  155. m_endPos.y = laserVector.Y;
  156. m_endPos.z = laserVector.Z;
  157. }
  158. m_targetID = INVALID_DRAWABLE_ID;
  159. }
  160. else
  161. {
  162. m_endPos = *targetDrawable->getPosition();
  163. }
  164. if( !(m_endPos == oldEndPos) ) // No != operator. Heh.
  165. m_dirty = TRUE;
  166. }
  167. //-------------------------------------------------------------------------------------------------
  168. /** The update callback. */
  169. //-------------------------------------------------------------------------------------------------
  170. void LaserUpdate::clientUpdate( void )
  171. {
  172. updateStartPos();
  173. updateEndPos();
  174. if( m_decaying )
  175. {
  176. UnsignedInt now = TheGameLogic->getFrame();
  177. m_currentWidthScalar = 1.0f - (Real)(now - m_decayStartFrame) / (Real)(m_decayFinishFrame - m_decayStartFrame);
  178. m_dirty = true;
  179. if( m_currentWidthScalar <= 0.0f )
  180. {
  181. m_currentWidthScalar = 0.0f;
  182. //When decay is finished... delete the laser.
  183. //TheGameLogic->destroyObject( getObject() );
  184. return;
  185. }
  186. }
  187. else if( m_widening )
  188. {
  189. //We need to resize our laser width based on the growth ratio completed.
  190. UnsignedInt now = TheGameLogic->getFrame();
  191. m_currentWidthScalar = (Real)(now - m_widenStartFrame) / (Real)(m_widenFinishFrame - m_widenStartFrame);
  192. m_dirty = true;
  193. if( m_currentWidthScalar >= 1.0f )
  194. {
  195. m_currentWidthScalar = 1.0f;
  196. m_widening = false;
  197. }
  198. }
  199. return;
  200. }
  201. void LaserUpdate::setDecayFrames( UnsignedInt decayFrames )
  202. {
  203. if( decayFrames > 0 )
  204. {
  205. m_decaying = true;
  206. m_decayStartFrame = TheGameLogic->getFrame();
  207. m_decayFinishFrame = m_decayStartFrame + decayFrames;
  208. m_currentWidthScalar = 1.0f;
  209. }
  210. }
  211. //-------------------------------------------------------------------------------------------------
  212. void LaserUpdate::initLaser( const Object *parent, const Object *target, const Coord3D *startPos, const Coord3D *endPos, AsciiString parentBoneName, Int sizeDeltaFrames )
  213. {
  214. const LaserUpdateModuleData *data = getLaserUpdateModuleData();
  215. ParticleSystem *system;
  216. if( sizeDeltaFrames > 0 )
  217. {
  218. m_widening = true;
  219. m_widenStartFrame = TheGameLogic->getFrame();
  220. m_widenFinishFrame = m_widenStartFrame + sizeDeltaFrames;
  221. m_currentWidthScalar = 0.0f;
  222. }
  223. else if( sizeDeltaFrames < 0 )
  224. {
  225. m_decaying = true;
  226. m_decayStartFrame = TheGameLogic->getFrame();
  227. m_decayFinishFrame = m_decayStartFrame - sizeDeltaFrames;
  228. m_currentWidthScalar = 1.0f;
  229. }
  230. // Write down the bone name override
  231. m_parentBoneName = parentBoneName;
  232. //Record IDs if we have them, then figure out starting points
  233. if( parent )
  234. {
  235. // If a source object, use it
  236. if( parent->getDrawable() )
  237. m_parentID = parent->getDrawable()->getID();
  238. updateStartPos();
  239. }
  240. else if( startPos )
  241. {
  242. // or just use what they gave
  243. m_startPos = *startPos;
  244. }
  245. else
  246. {
  247. // if they gave nothing, then we are screwed
  248. TheGameClient->destroyDrawable( getDrawable() );
  249. return;
  250. }
  251. if( target && !endPos )
  252. {
  253. // If a target object, use it (unless we override it!)
  254. if( target->getDrawable() )
  255. m_targetID = target->getDrawable()->getID();
  256. m_endPos = *target->getPosition();
  257. }
  258. else if( endPos )
  259. {
  260. // just use what they gave, no override here
  261. m_endPos = *endPos;
  262. }
  263. else
  264. {
  265. // if they gave nothing, then we are screwed
  266. TheGameClient->destroyDrawable( getDrawable() );
  267. return;
  268. }
  269. // Create special particle systems
  270. //PLEASE NOTE You cannot check an ID for NULL. This should be a check against INVALID_PARTICLE_SYSTEM_ID. Can't change it on the last day without a bug though.
  271. if( !m_particleSystemID )
  272. {
  273. const Player *localPlayer = ThePlayerList->getLocalPlayer();
  274. //Make sure the laser flare is visible to the player. If no parent, assume laser owner will handle it.
  275. if (!parent || parent->getShroudedStatus( localPlayer->getPlayerIndex() ) <= OBJECTSHROUD_PARTIAL_CLEAR )
  276. {
  277. //If we don't have a particle system for the lense flare (muzzle flare), create it.
  278. if( data->m_particleSystemName.isNotEmpty() )
  279. {
  280. const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate( data->m_particleSystemName );
  281. if( tmp )
  282. {
  283. system = TheParticleSystemManager->createParticleSystem( tmp );
  284. if( system )
  285. {
  286. m_particleSystemID = system->getSystemID();
  287. }
  288. }
  289. }
  290. //If we don't have a particle system for the target effect, create it.
  291. if( data->m_targetParticleSystemName.isNotEmpty() )
  292. {
  293. const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate( data->m_targetParticleSystemName );
  294. if( tmp )
  295. {
  296. system = TheParticleSystemManager->createParticleSystem( tmp );
  297. if( system )
  298. {
  299. m_targetParticleSystemID = system->getSystemID();
  300. }
  301. }
  302. }
  303. }
  304. }
  305. //Adjust the position of any existing particle system.
  306. //PLEASE NOTE You cannot check an ID for NULL. This should be a check against INVALID_PARTICLE_SYSTEM_ID. Can't change it on the last day without a bug though.
  307. if( m_particleSystemID )
  308. {
  309. system = TheParticleSystemManager->findParticleSystem( m_particleSystemID );
  310. if( system )
  311. {
  312. system->setPosition( &m_startPos );
  313. }
  314. }
  315. //PLEASE NOTE You cannot check an ID for NULL. This should be a check against INVALID_PARTICLE_SYSTEM_ID. Can't change it on the last day without a bug though.
  316. if( m_targetParticleSystemID )
  317. {
  318. system = TheParticleSystemManager->findParticleSystem( m_targetParticleSystemID );
  319. if( system )
  320. {
  321. system->setPosition( &m_endPos );
  322. }
  323. }
  324. //Important! Set the laser position to the average of both points or else
  325. //it probably won't get rendered!!!
  326. // And as a client update, we cannot set the logic position.
  327. Coord3D posToUse;
  328. if( parent == NULL )
  329. {
  330. posToUse.set( startPos );
  331. posToUse.add( endPos );
  332. posToUse.scale( 0.5 );
  333. }
  334. else
  335. {
  336. posToUse = *parent->getPosition();
  337. }
  338. Drawable *draw = getDrawable();
  339. if( draw )
  340. {
  341. draw->setPosition( &posToUse );
  342. }
  343. m_dirty = true;
  344. }
  345. //-------------------------------------------------------------------------------------------------
  346. Real LaserUpdate::getCurrentLaserRadius() const
  347. {
  348. const Drawable *draw = getDrawable();
  349. const LaserDrawInterface* ldi = NULL;
  350. for( const DrawModule** d = draw->getDrawModules(); *d; ++d )
  351. {
  352. ldi = (*d)->getLaserDrawInterface();
  353. if( ldi )
  354. {
  355. //***NOTE***
  356. //While it appears the logic is accessing client data, it is actually accessing template module
  357. //data from the client. This value is INI constant thus can't change. It's grouped with other
  358. //laser defining attributes and having it there makes it easier for artists.
  359. return ldi->getLaserTemplateWidth() * m_currentWidthScalar;
  360. }
  361. }
  362. return 0.0f;
  363. }
  364. // ------------------------------------------------------------------------------------------------
  365. /** CRC */
  366. // ------------------------------------------------------------------------------------------------
  367. void LaserUpdate::crc( Xfer *xfer )
  368. {
  369. // extend base class
  370. ClientUpdateModule::crc( xfer );
  371. } // end crc
  372. // ------------------------------------------------------------------------------------------------
  373. /** Xfer method
  374. * Version Info:
  375. * 1: Initial version */
  376. // ------------------------------------------------------------------------------------------------
  377. void LaserUpdate::xfer( Xfer *xfer )
  378. {
  379. // version
  380. XferVersion currentVersion = 1;
  381. XferVersion version = currentVersion;
  382. xfer->xferVersion( &version, currentVersion );
  383. // extend base class
  384. ClientUpdateModule::xfer( xfer );
  385. // start pos
  386. xfer->xferCoord3D( &m_startPos );
  387. // end pos
  388. xfer->xferCoord3D( &m_endPos );
  389. // dirty
  390. xfer->xferBool( &m_dirty );
  391. // particle system ID
  392. xfer->xferUser( &m_particleSystemID, sizeof( ParticleSystemID ) );
  393. // target particle system id
  394. xfer->xferUser( &m_targetParticleSystemID, sizeof( ParticleSystemID ) );
  395. // widening
  396. xfer->xferBool( &m_widening );
  397. // decaying
  398. xfer->xferBool( &m_decaying );
  399. // widen start frame
  400. xfer->xferUnsignedInt( &m_widenStartFrame );
  401. // widen finish frame
  402. xfer->xferUnsignedInt( &m_widenFinishFrame );
  403. // current width scalar
  404. xfer->xferReal( &m_currentWidthScalar );
  405. // decay start frame
  406. xfer->xferUnsignedInt( &m_decayStartFrame );
  407. // decay finish frame
  408. xfer->xferUnsignedInt( &m_decayFinishFrame );
  409. xfer->xferDrawableID(&m_parentID);
  410. xfer->xferDrawableID(&m_targetID);
  411. xfer->xferAsciiString(&m_parentBoneName);
  412. } // end xfer
  413. // ------------------------------------------------------------------------------------------------
  414. /** Load post process */
  415. // ------------------------------------------------------------------------------------------------
  416. void LaserUpdate::loadPostProcess( void )
  417. {
  418. // extend base class
  419. ClientUpdateModule::loadPostProcess();
  420. } // end loadPostProcess