TransitionDamageFX.cpp 16 KB

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