TensileFormationUpdate.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  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: TensileFormationUpdate.cpp //////////////////////////////////////////////////////////////////////////
  24. // Author: Mark Lorenzen, November 2002
  25. // Desc: Springy formation movement like that of say, an avalanche
  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 "Common/QuickTrig.h"
  31. #include "Common/AudioEventRTS.h"
  32. #include "GameLogic/Object.h"
  33. #include "GameLogic/TerrainLogic.h"
  34. #include "GameLogic/Module/TensileFormationUpdate.h"
  35. #include "GameLogic/Module/BodyModule.h"
  36. #include "GameLogic/PartitionManager.h"
  37. #include "GameLogic/GameLogic.h"
  38. #include "GameLogic/AI.h"
  39. #include "GameLogic/AIPathfind.h"
  40. #include "GameClient/TerrainVisual.h"
  41. #include "GameClient/Drawable.h"
  42. #ifdef _INTERNAL
  43. // for occasional debugging...
  44. //#pragma optimize("", off)
  45. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  46. #endif
  47. #define MAP_XY_FACTOR (10.0f) //How wide and tall each height map square is in world space.
  48. #define MAP_HEIGHT_SCALE (MAP_XY_FACTOR/16.0f) //divide all map heights by 8.
  49. //---------------------------------------------------------------------------------
  50. TensileFormationUpdate* getTFU( Object *obj )
  51. {
  52. static NameKeyType theTFUKey = TheNameKeyGenerator->nameToKey( "TensileFormationUpdate" );
  53. TensileFormationUpdate* tfu = (TensileFormationUpdate *)obj->findUpdateModule( theTFUKey );
  54. return tfu;
  55. }
  56. class PartitionFilterTensileFormationMember : public PartitionFilter
  57. {
  58. private:
  59. Object* m_obj;
  60. public:
  61. PartitionFilterTensileFormationMember( Object* obj ) : m_obj( obj ) { }
  62. #if defined(_DEBUG) || defined(_INTERNAL)
  63. virtual const char* debugGetName() { return "PartitionFilterTensileFormationMember"; }
  64. #endif
  65. virtual Bool allow( Object *objOther )
  66. {
  67. return ( getTFU( objOther ) != NULL );
  68. }
  69. };
  70. // ------------------------------------------------------------------------------------------------
  71. // ------------------------------------------------------------------------------------------------
  72. TensileFormationUpdateModuleData::TensileFormationUpdateModuleData( void )
  73. {
  74. m_enabled = FALSE;
  75. } // end TensileFormationUpdateModuleData
  76. // ------------------------------------------------------------------------------------------------
  77. // ------------------------------------------------------------------------------------------------
  78. /*static*/ void TensileFormationUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  79. {
  80. UpdateModuleData::buildFieldParse( p );
  81. static const FieldParse dataFieldParse[] =
  82. {
  83. { "Enabled", INI::parseBool, NULL, offsetof( TensileFormationUpdateModuleData, m_enabled ) },
  84. { "CrackSound", INI::parseAudioEventRTS, NULL, offsetof( TensileFormationUpdateModuleData, m_crackSound) },
  85. { 0, 0, 0, 0 }
  86. };
  87. p.add(dataFieldParse);
  88. } // end buildFieldParse
  89. ///////////////////////////////////////////////////////////////////////////////////////////////////
  90. ///////////////////////////////////////////////////////////////////////////////////////////////////
  91. ///////////////////////////////////////////////////////////////////////////////////////////////////
  92. // ------------------------------------------------------------------------------------------------
  93. // ------------------------------------------------------------------------------------------------
  94. TensileFormationUpdate::TensileFormationUpdate( Thing *thing, const ModuleData *moduleData )
  95. :UpdateModule( thing, moduleData )
  96. {
  97. const TensileFormationUpdateModuleData *modData = getTensileFormationUpdateModuleData();
  98. // save our initial enabled status based on INI settings
  99. m_enabled = ((TensileFormationUpdateModuleData *)moduleData)->m_enabled;
  100. m_inertia.set(0,0,0);
  101. m_linksInited = FALSE;
  102. m_motionlessCounter = 0;
  103. m_life = 0;
  104. m_lowestSlideElevation = 255.0f;
  105. for ( int t = 0; t < 4; ++t)
  106. {
  107. m_links[ t ].id = INVALID_ID;
  108. m_links[ t ].tensor.set(0,0,0);
  109. }
  110. m_crackSound = modData->m_crackSound;
  111. m_crackSound.setObjectID( getObject()->getID() ) ;
  112. TheAI->pathfinder()->createAWallFromMyFootprint( getObject() ); // Temporarily treat this object as an obstacle.
  113. //Coord3D pos = *getObject()->getPosition();
  114. //if ( pos.z > 80)
  115. //{
  116. // pos.z = 80 + ( ( pos.z - 80 ) * 2);
  117. // getObject()->setPosition( &pos );
  118. //}
  119. } // end TensileFormationUpdate
  120. // ------------------------------------------------------------------------------------------------
  121. // ------------------------------------------------------------------------------------------------
  122. TensileFormationUpdate::~TensileFormationUpdate( void )
  123. {
  124. } // end ~TensileFormationUpdate
  125. void TensileFormationUpdate::initLinks( void )
  126. {
  127. m_linksInited = TRUE;
  128. Object *obj = getObject();
  129. if ( !obj )
  130. return;
  131. PartitionFilterTensileFormationMember tfmFilter( getObject() );
  132. PartitionFilter *filters[] = { &tfmFilter, NULL };
  133. SimpleObjectIterator *iter = NULL;
  134. iter = ThePartitionManager->iterateObjectsInRange(getObject(), 1000.0f, FROM_BOUNDINGSPHERE_3D, filters);
  135. MemoryPoolObjectHolder hold(iter);
  136. Real closestDistance = 99999.9f;
  137. Real thisDistance = 99999.9f;
  138. const Coord3D *myPos = obj->getPosition();
  139. for (Object* other = iter->first(); other; other = iter->next())
  140. {
  141. const Coord3D *theirPos = other->getPosition();
  142. Coord3D delta = { theirPos->x - myPos->x, theirPos->y - myPos->y, theirPos->z - myPos->z };
  143. thisDistance = delta.length();
  144. if ( closestDistance > thisDistance )
  145. {
  146. closestDistance = thisDistance;
  147. //push a new link
  148. for ( int t = 3; t > 0; --t )
  149. m_links[ t ] = m_links[ t-1 ];
  150. m_links[ 0 ].id = other->getID();
  151. m_links[ 0 ].tensor = delta;
  152. }
  153. }
  154. //ICoord2D gridPos;
  155. //gridPos.x = REAL_TO_INT_FLOOR(getObject()->getPosition()->x/MAP_XY_FACTOR);
  156. //gridPos.y = REAL_TO_INT_FLOOR(getObject()->getPosition()->y/MAP_XY_FACTOR);
  157. //TheTerrainVisual->setRawMapHeight(&gridPos, 500);
  158. getObject()->setOrientation(GameLogicRandomValueReal(-PI,PI));
  159. }
  160. // ------------------------------------------------------------------------------------------------
  161. // ------------------------------------------------------------------------------------------------
  162. UpdateSleepTime TensileFormationUpdate::update( void )
  163. {
  164. if ( ! m_linksInited )
  165. initLinks();
  166. if( m_enabled == FALSE )
  167. {
  168. //We are all going to sit here idle and do not much of anything until one of us gets hurt
  169. BodyModuleInterface *body = getObject()->getBodyModule();
  170. BodyDamageType bdt = body->getDamageState();
  171. if ( bdt >= BODY_DAMAGED )
  172. {
  173. //THen the hurt one (maybe me) will tell every other member of the formation to enable
  174. //So we will start moving
  175. setEnabled( TRUE );
  176. TheAI->pathfinder()->removeWallFromMyFootprint( getObject() ); // Undo createAWallFromMyFootprint.
  177. //ALert players of disaster happening
  178. if ( ! m_crackSound.isCurrentlyPlaying())
  179. m_crackSound.setPlayingHandle(TheAudio->addAudioEvent( &m_crackSound ));
  180. }
  181. else
  182. return UPDATE_SLEEP(30);
  183. }
  184. Drawable *draw = getObject()->getDrawable();
  185. if ( ! draw )
  186. return UPDATE_SLEEP_NONE;
  187. if (++m_life > 300)
  188. {
  189. draw->clearModelConditionFlags(MAKE_MODELCONDITION_MASK(MODELCONDITION_MOVING));
  190. draw->clearModelConditionFlags(MAKE_MODELCONDITION_MASK(MODELCONDITION_FREEFALL));
  191. draw->clearModelConditionFlags(MAKE_MODELCONDITION_MASK(MODELCONDITION_POST_COLLAPSE));
  192. BodyModuleInterface *body = getObject()->getBodyModule();
  193. body->setDamageState( BODY_RUBBLE );
  194. TheAI->pathfinder()->createAWallFromMyFootprint( getObject() ); // Temporarily treat this object as an obstacle.
  195. return UPDATE_SLEEP_FOREVER;
  196. }
  197. if ( m_life%30 == 29 )
  198. propagateDislodgement( TRUE );
  199. //APPLY PHYSICS===========================
  200. const Coord3D *pos = getObject()->getPosition();
  201. Coord3D normal = { 0.0f, 0.0f, 1.0f };
  202. TheTerrainLogic->getGroundHeight(pos->x, pos->y, &normal); // which way does the ground slope?
  203. Coord3D slope = { normal.x, normal.y, 0.0f};
  204. Real steepness = 1.0f - normal.z;
  205. slope.scale( 0.3f + steepness);
  206. m_inertia.add( &slope );
  207. Real friction = 0.95f;
  208. m_inertia.scale( friction );
  209. Coord3D newPos;
  210. newPos.x = pos->x + m_inertia.x;//flow down the slope
  211. newPos.y = pos->y + m_inertia.y;
  212. newPos.z = TheTerrainLogic->getGroundHeight(newPos.x, newPos.y);//rest on surface here
  213. Object *tree = ThePartitionManager->getClosestObject( &newPos, getObject()->getGeometryInfo().getMajorRadius(), FROM_CENTER_2D );
  214. if (tree->isKindOf( KINDOF_SHRUBBERY ))
  215. tree->topple( &m_inertia, m_inertia.length(), 1 );//No Bounce
  216. //APPLY TENSORS===========================
  217. Coord3D tensorSum = { 0, 0, 0 };
  218. for ( int t = 0; t < 4; ++t )
  219. {
  220. Object *other = TheGameLogic->findObjectByID( m_links[ t ].id );
  221. if ( other )
  222. {
  223. Coord3D desiredPos = *other->getPosition();
  224. Coord3D tensor = m_links[ t ].tensor;
  225. desiredPos.sub( &tensor );
  226. //Coord3D desiredPos = { theirPos->x - m_links[ t ].tensor.x, theirPos->y - m_links[ t ].tensor.y, theirPos->z - m_links[ t ].tensor.z };
  227. newPos.x = newPos.x*0.93f + desiredPos.x*0.07f;
  228. newPos.y = newPos.y*0.93f + desiredPos.y*0.07f;
  229. newPos.z = MIN( m_lowestSlideElevation, TheTerrainLogic->getGroundHeight(newPos.x, newPos.y) );//rest on surface here
  230. tensor.normalize();
  231. tensorSum.add( &tensor );
  232. }
  233. }
  234. tensorSum.normalize();
  235. Coord3D inertiaNormal = m_inertia;
  236. inertiaNormal.normalize();
  237. draw->setModelConditionFlags(MAKE_MODELCONDITION_MASK(MODELCONDITION_POST_COLLAPSE));
  238. if ( m_life < 200 )
  239. draw->setModelConditionFlags(MAKE_MODELCONDITION_MASK(MODELCONDITION_MOVING));
  240. else
  241. draw->clearModelConditionFlags(MAKE_MODELCONDITION_MASK(MODELCONDITION_MOVING));
  242. if ( fabs( pos->z - newPos.z ) > 0.2f && m_life < 100)
  243. draw->setModelConditionFlags(MAKE_MODELCONDITION_MASK(MODELCONDITION_FREEFALL));
  244. else
  245. draw->clearModelConditionFlags(MAKE_MODELCONDITION_MASK(MODELCONDITION_FREEFALL));
  246. // {
  247. // RGBColor cyan = {0,1,1};
  248. // draw->flashAsSelected(&cyan);
  249. // }
  250. //else
  251. //m_motionlessCounter = 0;
  252. //if ( newPos.z > 80)
  253. // newPos.z = 80 + ( ( newPos.z - 80 ) * 2);
  254. m_lowestSlideElevation = newPos.z;
  255. getObject()->setPosition( &newPos );
  256. return UPDATE_SLEEP_NONE;
  257. } // end update
  258. //-------------------------------------------------------------------------------------
  259. //-------------------------------------------------------------------------------
  260. void TensileFormationUpdate::propagateDislodgement ( Bool enabled )
  261. {
  262. PartitionFilterTensileFormationMember tfmFilter( getObject() );
  263. PartitionFilter *filters[] = { &tfmFilter, NULL };
  264. SimpleObjectIterator *iter = NULL;
  265. iter = ThePartitionManager->iterateObjectsInRange(getObject(), 100.0f, FROM_BOUNDINGSPHERE_3D, filters);
  266. MemoryPoolObjectHolder hold(iter);
  267. for (Object* other = iter->first(); other; other = iter->next())
  268. {
  269. //Object *other = TheGameLogic->findObjectByID( m_links[ t ].id );
  270. if ( other )
  271. {
  272. BodyModuleInterface *body = other->getBodyModule();
  273. if ( body )
  274. {
  275. body->setDamageState( BODY_DAMAGED );
  276. }
  277. }
  278. //TensileFormationUpdate* tfu = getTFU(other);
  279. //if ( tfu != NULL )
  280. //{
  281. // tfu->setEnabled( enabled );
  282. //}
  283. }
  284. for ( int t = 0; t < 4; ++t )
  285. {
  286. Object *other = TheGameLogic->findObjectByID( m_links[ t ].id );
  287. if ( other )
  288. {
  289. BodyModuleInterface *body = other->getBodyModule();
  290. if ( body )
  291. {
  292. body->setDamageState( BODY_DAMAGED );
  293. }
  294. }
  295. }
  296. }
  297. // ------------------------------------------------------------------------------------------------
  298. /** CRC */
  299. // ------------------------------------------------------------------------------------------------
  300. void TensileFormationUpdate::crc( Xfer *xfer )
  301. {
  302. // extend base class
  303. UpdateModule::crc( xfer );
  304. } // end crc
  305. // ------------------------------------------------------------------------------------------------
  306. /** Xfer method
  307. * Version Info:
  308. * 1: Initial version */
  309. // ------------------------------------------------------------------------------------------------
  310. void TensileFormationUpdate::xfer( Xfer *xfer )
  311. {
  312. // version
  313. XferVersion currentVersion = 1;
  314. XferVersion version = currentVersion;
  315. xfer->xferVersion( &version, currentVersion );
  316. // extend base class
  317. UpdateModule::xfer( xfer );
  318. // enabled
  319. xfer->xferBool( &m_enabled );
  320. } // end xfer
  321. // ------------------------------------------------------------------------------------------------
  322. /** Load post process */
  323. // ------------------------------------------------------------------------------------------------
  324. void TensileFormationUpdate::loadPostProcess( void )
  325. {
  326. // extend base class
  327. UpdateModule::loadPostProcess();
  328. } // end loadPostProcess