WaveGuideUpdate.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  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: WaveGuideUpdate.cpp //////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, April 2002
  25. // Desc: Update module for the waver wave objects to create flooding
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/Radar.h"
  30. #include "Common/ThingFactory.h"
  31. #include "Common/ThingTemplate.h"
  32. #include "Common/Xfer.h"
  33. #include "GameClient/Drawable.h"
  34. #include "GameClient/ParticleSys.h"
  35. #include "GameClient/TerrainVisual.h"
  36. #include "GameLogic/GameLogic.h"
  37. #include "GameLogic/Object.h"
  38. #include "GameLogic/PartitionManager.h"
  39. #include "GameLogic/TerrainLogic.h"
  40. #include "GameLogic/AIPathfind.h"
  41. #include "GameLogic/Module/AIUpdate.h"
  42. #include "GameLogic/Module/PhysicsUpdate.h"
  43. #include "GameLogic/Module/WaveGuideUpdate.h"
  44. #include "GameLogic/Module/ToppleUpdate.h"
  45. // DEFINES ////////////////////////////////////////////////////////////////////////////////////////
  46. #define PATH_EXTRA_DISTANCE (10 * PATHFIND_CELL_SIZE_F)
  47. ///////////////////////////////////////////////////////////////////////////////////////////////////
  48. ///////////////////////////////////////////////////////////////////////////////////////////////////
  49. ///////////////////////////////////////////////////////////////////////////////////////////////////
  50. // ------------------------------------------------------------------------------------------------
  51. // ------------------------------------------------------------------------------------------------
  52. WaveGuideUpdateModuleData::WaveGuideUpdateModuleData( void )
  53. {
  54. //Added By Sadullah Nader
  55. //Initialization(s) inserted
  56. m_bridgeParticleAngleFudge = 0.0f;
  57. m_randomSplashSoundFrequency = 0;
  58. m_waveDelay = 0.0f;
  59. //
  60. m_ySize = 0.0f;
  61. m_linearWaveSpacing = 0.0f;
  62. m_waveBendMagnitude = 0.0f;
  63. m_waterVelocity = 0.0f;
  64. m_preferredHeight = 0.0f;
  65. m_shorelineEffectDistance = 0.0f;
  66. m_damageRadius = 0.0f;
  67. m_damageAmount = 0.0f;
  68. m_toppleForce = 0.0f;
  69. } // end WaveGuideUpdateModuleData
  70. // ------------------------------------------------------------------------------------------------
  71. // ------------------------------------------------------------------------------------------------
  72. /*static*/ void WaveGuideUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  73. {
  74. UpdateModuleData::buildFieldParse( p );
  75. static const FieldParse dataFieldParse[] =
  76. {
  77. { "WaveDelay", INI::parseDurationReal, NULL, offsetof( WaveGuideUpdateModuleData, m_waveDelay ) },
  78. { "YSize", INI::parseReal, NULL, offsetof( WaveGuideUpdateModuleData, m_ySize ) },
  79. { "LinearWaveSpacing", INI::parseReal, NULL, offsetof( WaveGuideUpdateModuleData, m_linearWaveSpacing ) },
  80. { "WaveBendMagnitude", INI::parseReal, NULL, offsetof( WaveGuideUpdateModuleData, m_waveBendMagnitude ) },
  81. { "WaterVelocity", INI::parseVelocityReal, NULL, offsetof( WaveGuideUpdateModuleData, m_waterVelocity ) },
  82. { "PreferredHeight", INI::parseReal, NULL, offsetof( WaveGuideUpdateModuleData, m_preferredHeight ) },
  83. { "ShorelineEffectDistance", INI::parseReal, NULL, offsetof( WaveGuideUpdateModuleData, m_shorelineEffectDistance ) },
  84. { "DamageRadius", INI::parseReal, NULL, offsetof( WaveGuideUpdateModuleData, m_damageRadius ) },
  85. { "DamageAmount", INI::parseReal, NULL, offsetof( WaveGuideUpdateModuleData, m_damageAmount ) },
  86. { "ToppleForce", INI::parseReal, NULL, offsetof( WaveGuideUpdateModuleData, m_toppleForce ) },
  87. { "RandomSplashSound", INI::parseAudioEventRTS, NULL, offsetof( WaveGuideUpdateModuleData, m_randomSplashSound ) },
  88. { "RandomSplashSoundFrequency", INI::parseInt, NULL, offsetof( WaveGuideUpdateModuleData, m_randomSplashSoundFrequency ) },
  89. { "BridgeParticle", INI::parseParticleSystemTemplate, NULL, offsetof( WaveGuideUpdateModuleData, m_bridgeParticle ) },
  90. { "BridgeParticleAngleFudge", INI::parseAngleReal, NULL, offsetof( WaveGuideUpdateModuleData, m_bridgeParticleAngleFudge ) },
  91. { "LoopingSound", INI::parseAudioEventRTS, NULL, offsetof( WaveGuideUpdateModuleData, m_loopingSound ) },
  92. { 0, 0, 0, 0 }
  93. };
  94. p.add(dataFieldParse);
  95. } // end buildFieldParse
  96. ///////////////////////////////////////////////////////////////////////////////////////////////////
  97. ///////////////////////////////////////////////////////////////////////////////////////////////////
  98. ///////////////////////////////////////////////////////////////////////////////////////////////////
  99. // ------------------------------------------------------------------------------------------------
  100. // ------------------------------------------------------------------------------------------------
  101. WaveGuideUpdate::WaveGuideUpdate( Thing *thing, const ModuleData *moduleData )
  102. :UpdateModule( thing, moduleData )
  103. {
  104. // Object *waveGuide = getObject();
  105. // water waves objects are disabled by default when they are created
  106. // doh, except that (for now) you can't disable something in its ctor
  107. //waveGuide->setDisabled();
  108. m_needDisable = true;
  109. m_activeFrame = 0;
  110. m_initialized = FALSE;
  111. m_shapePointCount = 0;
  112. m_splashSoundFrame = 0;
  113. m_finalDestination.zero();
  114. for( Int i = 0; i < MAX_WAVEGUIDE_SHAPE_POINTS; i++ )
  115. {
  116. m_shapePoints[ i ].zero();
  117. m_transformedShapePoints[ i ].zero();
  118. for( Int j = 0; j < MAX_SHAPE_EFFECTS; j++ )
  119. m_shapeEffects[ i ][ j ] = INVALID_PARTICLE_SYSTEM_ID;
  120. } // end for i
  121. } // end WaveGuideUpdate
  122. // ------------------------------------------------------------------------------------------------
  123. // ------------------------------------------------------------------------------------------------
  124. WaveGuideUpdate::~WaveGuideUpdate( void )
  125. {
  126. } // end ~WaveGuideUpdate
  127. // ------------------------------------------------------------------------------------------------
  128. /** Start the waveguide moving along its waypoint path, bringing water destruction and havok
  129. * upon the land */
  130. // ------------------------------------------------------------------------------------------------
  131. Bool WaveGuideUpdate::startMoving( void )
  132. {
  133. Object *waveGuide = getObject();
  134. Waypoint *waypoint;
  135. const WaveGuideUpdateModuleData *data = getWaveGuideUpdateModuleData();
  136. AudioEventRTS loopingSound = data->m_loopingSound;
  137. loopingSound.setObjectID( getObject()->getID() );
  138. TheAudio->addAudioEvent( &loopingSound );
  139. waypoint = TheTerrainLogic->getWaypointByName( "WaveGuide1" );
  140. if( waypoint )
  141. {
  142. //
  143. // traverse the entire waypoint path, there must be one link in the path, and
  144. // at any of the links there cannot be multiple choices of where to go next
  145. //
  146. Waypoint *verify = waypoint;
  147. while( verify )
  148. {
  149. // can't have more than one link
  150. if( verify->getNumLinks() > 1 )
  151. {
  152. DEBUG_CRASH(( "WaveGuideUpdate::startMoving - The waypoint path cannot have multiple link choices at any node\n" ));
  153. return FALSE;
  154. } // end if
  155. // set our destination location to this waypoint position
  156. m_finalDestination = *verify->getLocation();
  157. // on to the next
  158. verify = verify->getLink( 0 );
  159. } // end while
  160. // there must be at least one link
  161. Waypoint *next = waypoint->getLink( 0 );
  162. if( next == NULL )
  163. {
  164. DEBUG_CRASH(( "WaveGuideUpdate:startMoving - There must be a linked waypoint path to follow\n" ));
  165. return FALSE;
  166. } // end if
  167. // get vector from next waypoint to first waypoint
  168. Coord2D v;
  169. v.x = next->getLocation()->x - waypoint->getLocation()->x;
  170. v.y = next->getLocation()->y - waypoint->getLocation()->y;
  171. // turn vector into angle
  172. Real angle = v.toAngle();
  173. // orient the waveguide the same direction
  174. waveGuide->setOrientation( angle );
  175. // get the ai update interface for the waveguide
  176. AIUpdateInterface *ai = waveGuide->getAIUpdateInterface();
  177. if( ai )
  178. {
  179. // instantly move to waypoint location on the terrain
  180. Coord3D pos;
  181. pos.x = waypoint->getLocation()->x;
  182. pos.y = waypoint->getLocation()->y;
  183. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y );
  184. waveGuide->setPosition( &pos );
  185. // follow the waypoint path here
  186. ai->aiFollowWaypointPath( waypoint, CMD_FROM_AI );
  187. //
  188. // make sure it doesn't stop or slow down at each waypoint along the path if the path
  189. // we're following consists of multiple nodes
  190. //
  191. ai->setPathExtraDistance( PATH_EXTRA_DISTANCE );
  192. } // end if
  193. } // endif
  194. return TRUE; // all is well
  195. } // end startMoving
  196. // ------------------------------------------------------------------------------------------------
  197. /** The wave guide has started moving ... this is called once */
  198. // ------------------------------------------------------------------------------------------------
  199. Bool WaveGuideUpdate::initWaveGuide( void )
  200. {
  201. // start the waveguide movement
  202. if( startMoving() == FALSE )
  203. return FALSE; // don't begin moving
  204. // compute the wave shape points
  205. computeWaveShapePoints();
  206. static const ParticleSystemTemplate *wave1 = TheParticleSystemManager->findTemplate( "WaveSpray01" );
  207. static const ParticleSystemTemplate *wave2 = TheParticleSystemManager->findTemplate( "WaveSpray02" );
  208. static const ParticleSystemTemplate *wave3 = TheParticleSystemManager->findTemplate( "WaveSpray03" );
  209. ParticleSystem *particleSys;
  210. // create wavespray particle system and attach to object
  211. for( Int i = 0; i < m_shapePointCount; i++ )
  212. {
  213. // create spray 1 effect
  214. particleSys = TheParticleSystemManager->createParticleSystem( wave1 );
  215. if( particleSys )
  216. {
  217. particleSys->setPosition( &m_shapePoints[ i ] );
  218. particleSys->attachToObject( getObject() );
  219. m_shapeEffects[ i ][ 0 ] = particleSys->getSystemID();
  220. } // end if
  221. // create spray 2 effect
  222. particleSys = TheParticleSystemManager->createParticleSystem( wave2 );
  223. if( particleSys )
  224. {
  225. particleSys->setPosition( &m_shapePoints[ i ] );
  226. particleSys->attachToObject( getObject() );
  227. m_shapeEffects[ i ][ 1 ] = particleSys->getSystemID();
  228. } // end if
  229. // create spray 3 every few points across the wave
  230. if( i % 5 == 0 )
  231. {
  232. particleSys = TheParticleSystemManager->createParticleSystem( wave3 );
  233. if( particleSys )
  234. {
  235. particleSys->setPosition( &m_shapePoints[ i ] );
  236. particleSys->attachToObject( getObject() );
  237. m_shapeEffects[ i ][ 2 ] = particleSys->getSystemID();
  238. } // end if
  239. } // end if
  240. } // end if
  241. return TRUE; // all is well
  242. } // end initWaveGuide
  243. // ------------------------------------------------------------------------------------------------
  244. // ------------------------------------------------------------------------------------------------
  245. void WaveGuideUpdate::computeWaveShapePoints( void )
  246. {
  247. // get the module data
  248. const WaveGuideUpdateModuleData *modData = getWaveGuideUpdateModuleData();
  249. //
  250. // build an array of points that will represent the wavefront. these points are
  251. // created in local object space and can be put through the transform matrix of the
  252. // object to get the world position given the waveguides' current position and orientation
  253. //
  254. Int halfY = modData->m_ySize / 2.0f;
  255. for( Int y = -halfY; y < halfY; y += modData->m_linearWaveSpacing )
  256. {
  257. // sanity
  258. if( m_shapePointCount >= MAX_WAVEGUIDE_SHAPE_POINTS )
  259. break; // exit for
  260. //
  261. // if there is a bend magnitude we will use a form of "y = x^2" to make a parabola
  262. // that forms the wavefront ... otherwise we just make a straight line
  263. //
  264. if( modData->m_waveBendMagnitude )
  265. m_shapePoints[ m_shapePointCount ].x = -(y * y) / modData->m_waveBendMagnitude;
  266. else
  267. m_shapePoints[ m_shapePointCount ].x = 0.0f;
  268. m_shapePoints[ m_shapePointCount ].y = y;
  269. m_shapePoints[ m_shapePointCount ].z = 0.0f;
  270. m_shapePointCount++;
  271. } // end for width
  272. } // end computeWaveShapePoints
  273. // ------------------------------------------------------------------------------------------------
  274. /** Given the current position and orientation of the wave guide, transform all the wave
  275. * shape points so we can quickly access them for mutliple reasons */
  276. // ------------------------------------------------------------------------------------------------
  277. void WaveGuideUpdate::transformWaveShape( void )
  278. {
  279. Int i;
  280. Object *waveGuide = getObject();
  281. for( i = 0; i < m_shapePointCount; i++ )
  282. {
  283. // transform the point
  284. waveGuide->transformPoint( &m_shapePoints[ i ], &m_transformedShapePoints[ i ] );
  285. // the Z of the transformed point will be on the terrain
  286. m_transformedShapePoints[ i ].z = TheTerrainLogic->getGroundHeight( m_transformedShapePoints[ i ].x,
  287. m_transformedShapePoints[ i ].y );
  288. } // end for i
  289. } // end transformWaveShape
  290. // ------------------------------------------------------------------------------------------------
  291. /** Update phase for the effects that make up the front shape of the wave */
  292. // ------------------------------------------------------------------------------------------------
  293. void WaveGuideUpdate::doShapeEffects( void )
  294. {
  295. const WaveGuideUpdateModuleData *modData = getWaveGuideUpdateModuleData();
  296. //
  297. // the particle systems that make up the wave shape need to maintain a position that is
  298. // just above the ground
  299. //
  300. Int i, j;
  301. ParticleSystem *particleSys;
  302. Coord3D pos;
  303. for( i = 0; i < m_shapePointCount; i++ )
  304. {
  305. for( j = 0; j < MAX_SHAPE_EFFECTS; j++ )
  306. {
  307. // get the particle system here
  308. particleSys = TheParticleSystemManager->findParticleSystem( m_shapeEffects[ i ][ j ] );
  309. if( particleSys )
  310. {
  311. //
  312. // we have the height of the terrain at this shape point in the transformed shape
  313. // point array ... just set the LOCAL position for the particle system to reflect
  314. // this Z *IF* that Z will not take us above the target water height
  315. //
  316. if( m_transformedShapePoints[ i ].z < modData->m_preferredHeight )
  317. {
  318. particleSys->getPosition( &pos );
  319. pos.z = m_transformedShapePoints[ i ].z;
  320. particleSys->setPosition( &pos );
  321. } // end if
  322. } // end if
  323. } // end for j
  324. } // end for i
  325. } // end doShapeEffects
  326. // ------------------------------------------------------------------------------------------------
  327. /** Given all our sample points, make the wave go */
  328. // ------------------------------------------------------------------------------------------------
  329. void WaveGuideUpdate::doWaterMotion( void )
  330. {
  331. // Object *waveGuide = getObject();
  332. // get the module data for the wave guide
  333. const WaveGuideUpdateModuleData *modData = getWaveGuideUpdateModuleData();
  334. // iterate the waveguide shape points ... at certain points we will create water "up force"
  335. Int i;
  336. // const Matrix3D *transform = waveGuide->getTransformMatrix();
  337. for( i = 0; i < m_shapePointCount; i++ )
  338. {
  339. // push up the water here
  340. TheTerrainVisual->addWaterVelocity( m_transformedShapePoints[ i ].x,
  341. m_transformedShapePoints[ i ].y,
  342. modData->m_waterVelocity,
  343. modData->m_preferredHeight );
  344. } // end for i
  345. } // end doWaterMotion
  346. // ------------------------------------------------------------------------------------------------
  347. /** Any points in our wave that are on the shoreline that are close enough to the effect
  348. * points for the shore will do those effects */
  349. // ------------------------------------------------------------------------------------------------
  350. void WaveGuideUpdate::doShoreEffects( void )
  351. {
  352. // this is expensive to do *all* the time
  353. if( TheGameLogic->getFrame() & 0x1 )
  354. return;
  355. Object *waveGuide = getObject();
  356. Int i;
  357. // get module data
  358. const WaveGuideUpdateModuleData *modData = getWaveGuideUpdateModuleData();
  359. //
  360. // setup an array of points just behind the front of the wave where the first wave
  361. // "crest" is rising up ... we will use these points to make collide with shore effects
  362. //
  363. Coord3D effectPoints[ MAX_WAVEGUIDE_SHAPE_POINTS ];
  364. for( i = 0; i < m_shapePointCount; i++ )
  365. {
  366. // setup point to be a distance "behind" the wave shape points
  367. effectPoints[ i ].x = m_shapePoints[ i ].x - modData->m_shorelineEffectDistance;
  368. effectPoints[ i ].y = m_shapePoints[ i ].y;
  369. effectPoints[ i ].z = m_shapePoints[ i ].z;
  370. // transform the point
  371. waveGuide->transformPoint( &effectPoints[ i ], &effectPoints[ i ] );
  372. } // end for i
  373. //
  374. // go across the shape of our wave ... when we detect a transition from underground to
  375. // above ground or vice verse, that is a shoreline ... play an effect between those points
  376. //
  377. static const ParticleSystemTemplate *left = TheParticleSystemManager->findTemplate( "WaveSplashLeft01" );
  378. static const ParticleSystemTemplate *right = TheParticleSystemManager->findTemplate( "WaveSplashRight01" );
  379. ParticleSystem *particleSystem;
  380. Real terrainZ;
  381. Bool underWater = TRUE;
  382. Coord3D *point;
  383. for( i = 0; i < m_shapePointCount; i++ )
  384. {
  385. // get the point in question
  386. point = &effectPoints[ i ];
  387. // get terrain height at this point
  388. terrainZ = TheTerrainLogic->getGroundHeight( point->x, point->y );
  389. // is the terrain at this point below or above the preferred water height
  390. if( terrainZ > modData->m_preferredHeight )
  391. {
  392. //
  393. // if we were previously 'under' we are not any longer, this is a shore
  394. // note that we're ignoring the first point i = 0
  395. //
  396. if( underWater == TRUE && i != 0 )
  397. {
  398. Coord3D *prevPoint = &effectPoints[ i - 1 ]; // the prev point is actuall on the water so we'll use it
  399. particleSystem = TheParticleSystemManager->createParticleSystem( right );
  400. if( particleSystem )
  401. particleSystem->setPosition( prevPoint );
  402. } // end if
  403. // we are now 'above'
  404. underWater = FALSE;
  405. } // end if
  406. else
  407. {
  408. //
  409. // if we were previously 'above' we are not any longer, this is a shore
  410. // note that we're ignoring the first point i = 0
  411. //
  412. if( underWater == FALSE && i != 0 )
  413. {
  414. particleSystem = TheParticleSystemManager->createParticleSystem( left );
  415. if( particleSystem )
  416. particleSystem->setPosition( point );
  417. } // end if
  418. // we are now 'under'
  419. underWater = TRUE;
  420. } // end else
  421. } // end for i
  422. } // end doShoreEffects
  423. // ------------------------------------------------------------------------------------------------
  424. /** Do damage to things that have fallen victim in the path of this enourmous wave */
  425. // ------------------------------------------------------------------------------------------------
  426. void WaveGuideUpdate::doDamage( void )
  427. {
  428. Object *waveGuide = getObject();
  429. const WaveGuideUpdateModuleData *modData = getWaveGuideUpdateModuleData();
  430. // get our position forward unit direction vector
  431. // const Coord3D *unitForward = waveGuide->getUnitDirectionVector2D();
  432. // iterate over all our sample points and kill stuff around us
  433. for( Int i = 0; i < m_shapePointCount; i++ )
  434. {
  435. // scan objects around us and do damage to objects we have "passed over" and are behind us
  436. ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( &m_transformedShapePoints[ i ],
  437. modData->m_damageRadius,
  438. FROM_CENTER_2D,
  439. NULL );
  440. MemoryPoolObjectHolder hold( iter );
  441. Object *obj;
  442. const Coord3D *objPos;
  443. Coord3D v;
  444. Real angle;
  445. for( obj = iter->first(); obj; obj = iter->next() )
  446. {
  447. // ignore all waveguide types
  448. if( obj->isKindOf( KINDOF_WAVEGUIDE ) )
  449. continue;
  450. //
  451. // ignore bridge towers ... water waves will destroy bridges by colliding with
  452. // the actual bridge object in the *center* of the bridge, not the towers
  453. //
  454. if( obj->isKindOf( KINDOF_BRIDGE_TOWER ) )
  455. continue;
  456. // get other object position
  457. objPos = obj->getPosition();
  458. //
  459. // only damage objects that are below the preferred height of the wave doing the damage,
  460. // bridges are an exception as their object is raised above the ground, but we'll
  461. // say the water is destroying the foundation of it
  462. //
  463. if( objPos->z > modData->m_preferredHeight && obj->isKindOf( KINDOF_BRIDGE ) == FALSE )
  464. continue;
  465. // get the vector from us to the object
  466. v.x = objPos->x - m_transformedShapePoints[ i ].x;
  467. v.y = objPos->y - m_transformedShapePoints[ i ].y;
  468. v.z = 0.0f; // forget Z, this is really a top down 2D calculation
  469. v.normalize();
  470. //
  471. // get the cosine of the angle between the our forward direction and the vector to the obj
  472. // otherwise known as a dot product
  473. //
  474. angle = v.x * m_transformedShapePoints[ i ].x +
  475. v.y * m_transformedShapePoints[ i ].y +
  476. v.z * m_transformedShapePoints[ i ].z;
  477. //
  478. // we will only do damage if the object is "behind" the wave ... that is, behind
  479. // the 180 degrees mark behind us which we figure out by seeing if the cosine of
  480. // the angle between us (dot product) is less than zero
  481. //
  482. if( angle < 0 )
  483. {
  484. // if object was not wet before we kill it and play effects
  485. if( !obj->getStatusBits().test( OBJECT_STATUS_WET ) )
  486. {
  487. static const ParticleSystemTemplate *splash = TheParticleSystemManager->findTemplate( "WaveHit01" );
  488. ParticleSystem *particleSystem;
  489. // create particle system at position
  490. particleSystem = TheParticleSystemManager->createParticleSystem( splash );
  491. if( particleSystem )
  492. {
  493. Coord3D pos;
  494. //
  495. // we will use the position of the shape point (in local space) with the
  496. // Z of the object we're killin
  497. //
  498. pos.x = m_shapePoints[ i ].x;
  499. pos.y = m_shapePoints[ i ].y;
  500. pos.z = obj->getPosition()->z;
  501. particleSystem->setPosition( &pos );
  502. particleSystem->attachToObject( waveGuide );
  503. } // end if
  504. // this object is now wet
  505. obj->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_WET ) );
  506. // some things can be toppled ... ooo, xtra special of us!
  507. Coord3D toppleVector;
  508. toppleVector.x = obj->getPosition()->x - m_transformedShapePoints[ i ].x;
  509. toppleVector.y = obj->getPosition()->y - m_transformedShapePoints[ i ].y;
  510. toppleVector.z = 0;
  511. obj->topple( &toppleVector, modData->m_toppleForce, TOPPLE_OPTIONS_NO_BOUNCE |
  512. TOPPLE_OPTIONS_NO_FX );
  513. // do a lot of water damage
  514. DamageInfo damageInfo;
  515. damageInfo.in.m_damageType = DAMAGE_WATER;
  516. damageInfo.in.m_deathType = DEATH_FLOODED;
  517. damageInfo.in.m_sourceID = waveGuide->getID();
  518. damageInfo.in.m_amount = modData->m_damageAmount;
  519. obj->attemptDamage( &damageInfo );
  520. //
  521. // set flooded condition for model and turn off shadows cause we're inside water,
  522. // note that we want to do this after we damage the object so that we switch
  523. // off the shadows on the new model data loaded after damage is dealt
  524. //
  525. Drawable *draw = obj->getDrawable();
  526. if( draw )
  527. {
  528. draw->setModelConditionState( MODELCONDITION_FLOODED );
  529. draw->setShadowsEnabled( FALSE );
  530. } // end if
  531. //
  532. // Temp demo hack, replace bridges destroyed with the special destroyed bridge art
  533. //
  534. if( obj->isKindOf( KINDOF_BRIDGE ) )
  535. {
  536. const ThingTemplate* ttn = TheThingFactory->findTemplate("WaterWaveBridge");
  537. Object *newBridge = TheThingFactory->newObject( ttn, NULL );
  538. if( newBridge )
  539. {
  540. Real angle = 0.0f;
  541. // get the bridge represented by the object we're killing
  542. Bridge *oldBridge = TheTerrainLogic->findBridgeAt( obj->getPosition() );
  543. if( oldBridge )
  544. {
  545. BridgeInfo bridgeInfo;
  546. // get the bridge info
  547. oldBridge->getBridgeInfo( &bridgeInfo );
  548. // compute the angle of the object based on the angle of the bridge
  549. Coord2D v;
  550. v.x = bridgeInfo.to.x - bridgeInfo.from.x;
  551. v.y = bridgeInfo.to.y - bridgeInfo.from.y;
  552. angle = v.toAngle();
  553. } // end if
  554. // put new bridge looking object in the world
  555. newBridge->setPosition( obj->getPosition() );
  556. newBridge->setOrientation( angle );
  557. // create and attach a wave hit bridge particle system to the new object
  558. ParticleSystem *particleSystem = TheParticleSystemManager->createParticleSystem( modData->m_bridgeParticle );
  559. if( particleSystem )
  560. {
  561. Coord3D u, x, y, z;
  562. Matrix3D transform;
  563. z.x = 0.0f;
  564. z.y = 0.0f;
  565. z.z = 1.0f;
  566. //
  567. // angle is rotated, becuase we computed from 'from' and 'to' points of
  568. // the bridge going *across* the valley, not pointing *down* it
  569. //
  570. u.x = Cos( angle + modData->m_bridgeParticleAngleFudge );
  571. u.y = Sin( angle + modData->m_bridgeParticleAngleFudge );
  572. u.z = 0.0f;
  573. y.crossProduct( &z, &u, &y );
  574. x.crossProduct( &y, &z, &x );
  575. transform.Set( x.x, y.x, z.x, obj->getPosition()->x,
  576. x.y, y.y, z.y, obj->getPosition()->y,
  577. x.z, y.z, z.z, obj->getPosition()->z );
  578. particleSystem->setLocalTransform( &transform );
  579. } // end if
  580. // destroy the old bridge and bridge object
  581. TheTerrainLogic->deleteBridge( oldBridge );
  582. } // end if
  583. } // end if
  584. } // end if
  585. } // end if
  586. } // end for obj
  587. } // end for i
  588. } // end doDamage
  589. // ------------------------------------------------------------------------------------------------
  590. // ------------------------------------------------------------------------------------------------
  591. UpdateSleepTime WaveGuideUpdate::update( void )
  592. {
  593. /// @todo srj use SLEEPY_UPDATE here
  594. Object *waveGuide = getObject();
  595. if (m_needDisable)
  596. {
  597. m_needDisable = false;
  598. getObject()->setDisabled( DISABLED_DEFAULT );
  599. return UPDATE_SLEEP_NONE;
  600. }
  601. // do nothing if we're disabled
  602. if( waveGuide->isDisabled() )
  603. return UPDATE_SLEEP_NONE;
  604. // get module data
  605. const WaveGuideUpdateModuleData *modData = getWaveGuideUpdateModuleData();
  606. // set the frame we became active on if not set
  607. if( m_activeFrame == 0 )
  608. m_activeFrame = TheGameLogic->getFrame();
  609. // if we're waiting for a delay check it and get out of here
  610. if( TheGameLogic->getFrame() - m_activeFrame < modData->m_waveDelay )
  611. return UPDATE_SLEEP_NONE;
  612. // get our position
  613. // const Coord3D *waveGuidePos = waveGuide->getPosition();
  614. // when we become enabled we will start particle effects for our wave
  615. if( m_initialized == FALSE )
  616. {
  617. // start the effects we need
  618. if( initWaveGuide() == FALSE )
  619. {
  620. // initialization failed, destroy object and get out of here
  621. TheGameLogic->destroyObject( getObject() );
  622. return UPDATE_SLEEP_NONE;
  623. } // end if
  624. // we are now in motion
  625. m_initialized = TRUE;
  626. } // end if
  627. // every half second we try to play a random spash sound
  628. if( TheGameLogic->getFrame() - m_splashSoundFrame > LOGICFRAMES_PER_SECOND / 2.0f )
  629. {
  630. // mark that we've had the opportunity to play
  631. m_splashSoundFrame = TheGameLogic->getFrame();
  632. // pick a random number and play according to frequency
  633. if( GameLogicRandomValue( 1, 100 ) > modData->m_randomSplashSoundFrequency )
  634. {
  635. AudioEventRTS randomSplash(modData->m_randomSplashSound);
  636. randomSplash.setObjectID(waveGuide->getID());
  637. TheAudio->addAudioEvent(&randomSplash);
  638. } // end if
  639. } // end if
  640. //
  641. // transform the wave shape points once for the current position ... we have this array
  642. // because we access this data several times and don't want to recompute it every time
  643. //
  644. transformWaveShape();
  645. // see if we are close enough to the end of our journey on the waypoint path
  646. const Coord3D *currentPos = waveGuide->getPosition();
  647. Real distSquared = PATH_EXTRA_DISTANCE * PATH_EXTRA_DISTANCE;
  648. Coord2D v;
  649. v.x = m_finalDestination.x - currentPos->x;
  650. v.y = m_finalDestination.y - currentPos->y;
  651. if( v.x * v.x + v.y * v.y <= distSquared )
  652. {
  653. static const ParticleSystemTemplate *waveSplash = TheParticleSystemManager->findTemplate( "WaveSplash01" );
  654. ParticleSystem *particleSys;
  655. // create spash effect
  656. particleSys = TheParticleSystemManager->createParticleSystem( waveSplash );
  657. if( particleSys )
  658. particleSys->setLocalTransform( waveGuide->getTransformMatrix() );
  659. // destroy object
  660. TheGameLogic->destroyObject( waveGuide );
  661. // update the radar with the final new water levels
  662. TheRadar->refreshTerrain( TheTerrainLogic );
  663. return UPDATE_SLEEP_NONE;
  664. } // end if
  665. // do wavefront effects
  666. doShapeEffects();
  667. // do water effects
  668. doWaterMotion();
  669. // do shore effects
  670. doShoreEffects();
  671. // do damage to things in our way
  672. doDamage();
  673. return UPDATE_SLEEP_NONE;
  674. } // end update
  675. // ------------------------------------------------------------------------------------------------
  676. /** CRC */
  677. // ------------------------------------------------------------------------------------------------
  678. void WaveGuideUpdate::crc( Xfer *xfer )
  679. {
  680. // extend base class
  681. UpdateModule::crc( xfer );
  682. } // end crc
  683. // ------------------------------------------------------------------------------------------------
  684. /** Xfer method
  685. * Version Info:
  686. * 1: Initial version */
  687. // ------------------------------------------------------------------------------------------------
  688. void WaveGuideUpdate::xfer( Xfer *xfer )
  689. {
  690. // version
  691. XferVersion currentVersion = 1;
  692. XferVersion version = currentVersion;
  693. xfer->xferVersion( &version, currentVersion );
  694. // extend base class
  695. UpdateModule::xfer( xfer );
  696. // active frame
  697. xfer->xferUnsignedInt( &m_activeFrame );
  698. // need disable
  699. xfer->xferBool( &m_needDisable );
  700. // initialized
  701. xfer->xferBool( &m_initialized );
  702. // shape points
  703. xfer->xferUser( m_shapePoints, sizeof( Coord3D ) * MAX_WAVEGUIDE_SHAPE_POINTS );
  704. // transformed shape points
  705. xfer->xferUser( &m_transformedShapePoints, sizeof( Coord3D ) * MAX_WAVEGUIDE_SHAPE_POINTS );
  706. // particle shape effects
  707. xfer->xferUser( m_shapeEffects, sizeof( ParticleSystemID ) * MAX_WAVEGUIDE_SHAPE_POINTS * MAX_SHAPE_EFFECTS );
  708. // shape point count
  709. xfer->xferInt( &m_shapePointCount );
  710. // splash sound frame
  711. xfer->xferUnsignedInt( &m_splashSoundFrame );
  712. // final destination
  713. xfer->xferCoord3D( &m_finalDestination );
  714. } // end xfer
  715. // ------------------------------------------------------------------------------------------------
  716. /** Load post process */
  717. // ------------------------------------------------------------------------------------------------
  718. void WaveGuideUpdate::loadPostProcess( void )
  719. {
  720. // extend base class
  721. UpdateModule::loadPostProcess();
  722. } // end loadPostProcess