TransitionDamageFX.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  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: TransitionDamageFX.cpp ///////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, March 2002
  25. // Desc: Damage module capable of launching various effects on damage transitions
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/Xfer.h"
  30. #include "GameClient/Drawable.h"
  31. #include "GameClient/FXList.h"
  32. #include "GameLogic/GameLogic.h"
  33. #include "GameLogic/Object.h"
  34. #include "GameLogic/ObjectCreationList.h"
  35. #include "GameLogic/Module/TransitionDamageFX.h"
  36. //-------------------------------------------------------------------------------------------------
  37. //-------------------------------------------------------------------------------------------------
  38. TransitionDamageFXModuleData::TransitionDamageFXModuleData( void )
  39. {
  40. Int i, j;
  41. for( i = 0; i < BODYDAMAGETYPE_COUNT; i++ )
  42. {
  43. for( j = 0; j < DAMAGE_MODULE_MAX_FX; j++ )
  44. {
  45. m_fxList[ i ][ j ].fx = NULL;
  46. m_fxList[ i ][ j ].locInfo.loc.x = 0.0f;
  47. m_fxList[ i ][ j ].locInfo.loc.y = 0.0f;
  48. m_fxList[ i ][ j ].locInfo.loc.z = 0.0f;
  49. m_fxList[ i ][ j ].locInfo.locType = FX_DAMAGE_LOC_TYPE_COORD;
  50. m_fxList[ i ][ j ].locInfo.randomBone = FALSE;
  51. m_OCL[ i ][ j ].ocl = NULL;
  52. m_OCL[ i ][ j ].locInfo.loc.x = 0.0f;
  53. m_OCL[ i ][ j ].locInfo.loc.y = 0.0f;
  54. m_OCL[ i ][ j ].locInfo.loc.z = 0.0f;
  55. m_OCL[ i ][ j ].locInfo.locType = FX_DAMAGE_LOC_TYPE_COORD;
  56. m_OCL[ i ][ j ].locInfo.randomBone = FALSE;
  57. m_particleSystem[ i ][ j ].particleSysTemplate = NULL;
  58. m_particleSystem[ i ][ j ].locInfo.loc.x = 0.0f;
  59. m_particleSystem[ i ][ j ].locInfo.loc.y = 0.0f;
  60. m_particleSystem[ i ][ j ].locInfo.loc.z = 0.0f;
  61. m_particleSystem[ i ][ j ].locInfo.locType = FX_DAMAGE_LOC_TYPE_COORD;
  62. m_particleSystem[ i ][ j ].locInfo.randomBone = FALSE;
  63. } // end for j
  64. } // end for i
  65. m_damageFXTypes = DAMAGE_TYPE_FLAGS_NONE;
  66. m_damageFXTypes.flip();
  67. m_damageOCLTypes = DAMAGE_TYPE_FLAGS_NONE;
  68. m_damageOCLTypes.flip();
  69. m_damageParticleTypes = DAMAGE_TYPE_FLAGS_NONE;
  70. m_damageParticleTypes.flip();
  71. } // end TransitionDamageFXModuleData
  72. //-------------------------------------------------------------------------------------------------
  73. /** Parse fx location info ... that is a named bone or a coord3d position */
  74. //-------------------------------------------------------------------------------------------------
  75. static void parseFXLocInfo( INI *ini, void *instance, FXLocInfo *locInfo )
  76. {
  77. const char *token = ini->getNextToken( ini->getSepsColon() );
  78. if( stricmp( token, "bone" ) == 0 )
  79. {
  80. // save bone name and location type
  81. locInfo->boneName = AsciiString( ini->getNextToken() );
  82. locInfo->locType = FX_DAMAGE_LOC_TYPE_BONE;
  83. //
  84. // bones are followed by RandomBone:<Yes|No>, if random bone is yes, the bone name is
  85. // assumed to be a "base bone name" and we will find all the bones with that prefix
  86. // when picking an effect position. If it's no, the bone name is assumed to be explicit
  87. //
  88. token = ini->getNextToken( ini->getSepsColon() );
  89. if( stricmp( token, "randombone" ) != 0 )
  90. {
  91. DEBUG_CRASH(( "parseFXLocInfo: Bone name not followed by RandomBone specifier\nPress IGNORE to see which INI file and line # is incorrect." ));
  92. throw INI_INVALID_DATA;
  93. } // end if
  94. // parse the Bool definition
  95. ini->parseBool( ini, instance, &locInfo->randomBone, NULL );
  96. } // end if
  97. else if( stricmp( token, "loc" ) == 0 )
  98. {
  99. // save location and location type
  100. locInfo->loc.x = ini->scanReal( ini->getNextSubToken("X") );
  101. locInfo->loc.y = ini->scanReal( ini->getNextSubToken("Y") );
  102. locInfo->loc.z = ini->scanReal( ini->getNextSubToken("Z") );
  103. locInfo->locType = FX_DAMAGE_LOC_TYPE_COORD;
  104. } // end else
  105. else
  106. {
  107. // error
  108. throw INI_INVALID_DATA;
  109. } // end else
  110. } // end parseFXLocInfo
  111. //-------------------------------------------------------------------------------------------------
  112. /** In the form of:
  113. * FXListSlot = <<Bone:BoneName BoneRandom:<Yes|No>> | <Loc: X:x Y:y Z:z>> FXList:FXListName */
  114. //-------------------------------------------------------------------------------------------------
  115. void TransitionDamageFXModuleData::parseFXList( INI *ini, void *instance,
  116. void *store, const void *userData )
  117. {
  118. const char *token;
  119. FXDamageFXListInfo *info = (FXDamageFXListInfo *)store;
  120. // parse the location bone or location
  121. parseFXLocInfo( ini, instance, &info->locInfo );
  122. // make sure we have an "FXList:" token
  123. token = ini->getNextToken( ini->getSepsColon() );
  124. if( stricmp( token, "fxlist" ) != 0 )
  125. {
  126. // error
  127. throw INI_INVALID_DATA;
  128. } // end if
  129. // parse the fx list name
  130. ini->parseFXList( ini, instance, &info->fx, NULL );
  131. } // end parseFXList
  132. //-------------------------------------------------------------------------------------------------
  133. /** In the form of:
  134. * OCLSlot = <<Bone:BoneName BoneRandom:<Yes|No>> | <Loc: X:x Y:y Z:z>> OCL:OCLName */
  135. //-------------------------------------------------------------------------------------------------
  136. void TransitionDamageFXModuleData::parseObjectCreationList( INI *ini, void *instance,
  137. void *store, const void *userData )
  138. {
  139. const char *token;
  140. FXDamageOCLInfo *info = (FXDamageOCLInfo *)store;
  141. // parse the location bone or location
  142. parseFXLocInfo( ini, instance, &info->locInfo );
  143. // make sure we have an "OCL:" token
  144. token = ini->getNextToken( ini->getSepsColon() );
  145. if( stricmp( token, "ocl" ) != 0 )
  146. {
  147. // error
  148. throw INI_INVALID_DATA;
  149. } // end if
  150. // parse the ocl name
  151. ini->parseObjectCreationList( ini, instance, store, &info->ocl );
  152. } // end parseObjectCreationList
  153. //-------------------------------------------------------------------------------------------------
  154. /** In the form of:
  155. * ParticleSlot = <<Bone:BoneName BoneRandom:<Yes|No>> | <Loc: X:x Y:y Z:z>> PSys:PSysName */
  156. //-------------------------------------------------------------------------------------------------
  157. void TransitionDamageFXModuleData::parseParticleSystem( INI *ini, void *instance,
  158. void *store, const void *userData )
  159. {
  160. const char *token;
  161. FXDamageParticleSystemInfo *info = (FXDamageParticleSystemInfo *)store;
  162. // parse the location bone or location
  163. parseFXLocInfo( ini, instance, &info->locInfo );
  164. // make sure we have an "PSys:" token
  165. token = ini->getNextToken( ini->getSepsColon() );
  166. if( stricmp( token, "psys" ) != 0 )
  167. {
  168. // error
  169. throw INI_INVALID_DATA;
  170. } // end if
  171. // parse the particle system name
  172. ini->parseParticleSystemTemplate( ini, instance, store, &info->particleSysTemplate );
  173. } // end parseParticleSystem
  174. //-------------------------------------------------------------------------------------------------
  175. //-------------------------------------------------------------------------------------------------
  176. TransitionDamageFX::TransitionDamageFX( Thing *thing, const ModuleData* moduleData )
  177. : DamageModule( thing, moduleData )
  178. {
  179. Int i, j;
  180. for( i = 0; i < BODYDAMAGETYPE_COUNT; i++ )
  181. for( j = 0; j < DAMAGE_MODULE_MAX_FX; j++ )
  182. m_particleSystemID[ i ][ j ] = INVALID_PARTICLE_SYSTEM_ID;
  183. } // end TransitionDamageFX
  184. //-------------------------------------------------------------------------------------------------
  185. //-------------------------------------------------------------------------------------------------
  186. TransitionDamageFX::~TransitionDamageFX( void )
  187. {
  188. } // end ~TransitionDamageFX
  189. /*
  190. //-------------------------------------------------------------------------------------------------
  191. //-------------------------------------------------------------------------------------------------
  192. void TransitionDamageFX::onDelete( void )
  193. {
  194. //
  195. // we would in theory delete any particle systems we have created and attached, but the
  196. // particle system will automatically delete itself when the object is destroyed
  197. //
  198. } // end onDelete
  199. */
  200. //-------------------------------------------------------------------------------------------------
  201. /** Given an FXLoc info struct, return the effect position that we are supposed to use.
  202. * The position is local to to the object */
  203. //-------------------------------------------------------------------------------------------------
  204. static Coord3D getLocalEffectPos( const FXLocInfo *locInfo, Drawable *draw )
  205. {
  206. DEBUG_ASSERTCRASH( locInfo, ("getLocalEffectPos: locInfo is NULL\n") );
  207. if( locInfo->locType == FX_DAMAGE_LOC_TYPE_BONE && draw )
  208. {
  209. if( locInfo->randomBone == FALSE )
  210. {
  211. Coord3D pos;
  212. // get the bone position
  213. Int count = draw->getPristineBonePositions( locInfo->boneName.str(), 0, &pos, NULL, 1 );
  214. // sanity, if bone not found revert back to location defined in struct (which is 0,0,0)
  215. if( count == 0 )
  216. return locInfo->loc;
  217. // return the position retrieved
  218. return pos;
  219. } // end if
  220. else
  221. {
  222. const Int MAX_BONES = 32;
  223. Coord3D positions[ MAX_BONES ];
  224. // get the bone positions
  225. Int boneCount;
  226. boneCount = draw->getPristineBonePositions( locInfo->boneName.str(), 1, positions, NULL, MAX_BONES );
  227. // sanity, if bone not found revert back to location defined in struct (which is 0,0,0)
  228. if( boneCount == 0 )
  229. return locInfo->loc;
  230. // pick one of the bone positions
  231. Int pick = GameLogicRandomValue( 0, boneCount - 1 );
  232. return positions[ pick ];
  233. } // end else
  234. } // end if
  235. else
  236. return locInfo->loc;
  237. } // end getLocalEffectPos
  238. //-------------------------------------------------------------------------------------------------
  239. /** Switching damage states */
  240. //-------------------------------------------------------------------------------------------------
  241. void TransitionDamageFX::onBodyDamageStateChange( const DamageInfo* damageInfo,
  242. BodyDamageType oldState,
  243. BodyDamageType newState )
  244. {
  245. Object *damageSource = NULL;
  246. Int i;
  247. Drawable *draw = getObject()->getDrawable();
  248. const TransitionDamageFXModuleData *modData = getTransitionDamageFXModuleData();
  249. // get the source of the damage if present
  250. if( damageInfo )
  251. damageSource = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
  252. // remove any particle systems that might be emitting from our old state
  253. for( i = 0; i < DAMAGE_MODULE_MAX_FX; i++ )
  254. {
  255. if( m_particleSystemID[ oldState ][ i ] != INVALID_PARTICLE_SYSTEM_ID )
  256. {
  257. TheParticleSystemManager->destroyParticleSystemByID( m_particleSystemID[ oldState ][ i ] );
  258. m_particleSystemID[ oldState ][ i ] = INVALID_PARTICLE_SYSTEM_ID;
  259. } // end if
  260. } // end for i
  261. //
  262. // when we are transitioning to a "worse" state we will play a set of effects for that
  263. // new state to make the transition
  264. //
  265. if( IS_CONDITION_WORSE( newState, oldState ) )
  266. {
  267. const ParticleSystemTemplate *pSystemT;
  268. Coord3D pos;
  269. // if we are restricted by the damage type executing effect, bail out of here
  270. const DamageInfo *lastDamageInfo = getObject()->getBodyModule()->getLastDamageInfo();
  271. for( i = 0; i < DAMAGE_MODULE_MAX_FX; i++ )
  272. {
  273. // play fx list for our new state
  274. if( modData->m_fxList[ newState ][ i ].fx )
  275. {
  276. if( lastDamageInfo == NULL ||
  277. getDamageTypeFlag( modData->m_damageFXTypes, lastDamageInfo->in.m_damageType ) )
  278. {
  279. pos = getLocalEffectPos( &modData->m_fxList[ newState ][ i ].locInfo, draw );
  280. getObject()->convertBonePosToWorldPos( &pos, NULL, &pos, NULL );
  281. FXList::doFXPos( modData->m_fxList[ newState ][ i ].fx, &pos );
  282. } // end if
  283. } // end if
  284. // do any object creation list for our new state
  285. if( modData->m_OCL[ newState ][ i ].ocl )
  286. {
  287. if( lastDamageInfo == NULL ||
  288. getDamageTypeFlag( modData->m_damageOCLTypes, lastDamageInfo->in.m_damageType ) )
  289. {
  290. pos = getLocalEffectPos( &modData->m_OCL[ newState ][ i ].locInfo, draw );
  291. getObject()->convertBonePosToWorldPos( &pos, NULL, &pos, NULL );
  292. ObjectCreationList::create( modData->m_OCL[ newState ][ i ].ocl,
  293. getObject(), &pos, damageSource->getPosition(), INVALID_ANGLE );
  294. } // end if
  295. } // end if
  296. // get the template of the system to create
  297. pSystemT = modData->m_particleSystem[ newState ][ i ].particleSysTemplate;
  298. if( pSystemT )
  299. {
  300. if( lastDamageInfo == NULL ||
  301. getDamageTypeFlag( modData->m_damageParticleTypes, lastDamageInfo->in.m_damageType ) )
  302. {
  303. // create a new particle system based on the template provided
  304. ParticleSystem* pSystem = TheParticleSystemManager->createParticleSystem( pSystemT );
  305. if( pSystem )
  306. {
  307. // get the what is the position we're going to playe the effect at
  308. pos = getLocalEffectPos( &modData->m_particleSystem[ newState ][ i ].locInfo, draw );
  309. //
  310. // set position on system given any bone position provided, the bone position is
  311. // local to the object and that's what we want for the particle system ... the
  312. // transormation into world space using the object position is taken care of in
  313. // the particle system attachToObject method
  314. //
  315. pSystem->setPosition( &pos );
  316. // attach to object
  317. pSystem->attachToObject( getObject() );
  318. // save the id of this particle system so we can remove it later if it still exists
  319. m_particleSystemID[ newState ][ i ] = pSystem->getSystemID();
  320. } // end if
  321. } // end if
  322. } // end if
  323. } // end for i
  324. } // end if
  325. } // end onBodyDamageStateChange
  326. // ------------------------------------------------------------------------------------------------
  327. /** CRC */
  328. // ------------------------------------------------------------------------------------------------
  329. void TransitionDamageFX::crc( Xfer *xfer )
  330. {
  331. // extend base class
  332. DamageModule::crc( xfer );
  333. } // end crc
  334. // ------------------------------------------------------------------------------------------------
  335. /** Xfer method
  336. * Version Info:
  337. * 1: Initial version */
  338. // ------------------------------------------------------------------------------------------------
  339. void TransitionDamageFX::xfer( Xfer *xfer )
  340. {
  341. // version
  342. XferVersion currentVersion = 1;
  343. XferVersion version = currentVersion;
  344. xfer->xferVersion( &version, currentVersion );
  345. // extend base class
  346. DamageModule::xfer( xfer );
  347. // particle systems ids
  348. xfer->xferUser( m_particleSystemID, sizeof( ParticleSystemID ) * BODYDAMAGETYPE_COUNT * DAMAGE_MODULE_MAX_FX );
  349. } // end xfer
  350. // ------------------------------------------------------------------------------------------------
  351. /** Load post process */
  352. // ------------------------------------------------------------------------------------------------
  353. void TransitionDamageFX::loadPostProcess( void )
  354. {
  355. // extend base class
  356. DamageModule::loadPostProcess();
  357. } // end loadPostProcess