ObjectCreationList.cpp 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494
  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: ObjectCreationList.cpp ///////////////////////////////////////////////////////////////////////////////
  24. // Author: Steven Johnson, December 2001
  25. // Desc: ObjectCreationList descriptions
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #define DEFINE_SHADOW_NAMES // for TheShadowNames[]
  30. #define DEFINE_WEAPONSLOTTYPE_NAMES
  31. #define NO_DEBUG_CRC
  32. #include "Common/AudioEventRTS.h"
  33. #include "Common/DrawModule.h"
  34. #include "Common/GlobalData.h"
  35. #include "Common/INI.h"
  36. #include "Common/Player.h"
  37. #include "Common/PlayerList.h"
  38. #include "Common/ThingTemplate.h"
  39. #include "Common/ThingFactory.h"
  40. #include "Common/GameLOD.h"
  41. #include "GameClient/Drawable.h"
  42. #include "GameClient/FXList.h"
  43. #include "GameClient/ParticleSys.h"
  44. #include "GameClient/Shadow.h"
  45. #include "GameLogic/ExperienceTracker.h"
  46. #include "GameLogic/GameLogic.h"
  47. #include "GameLogic/Locomotor.h"
  48. #include "GameLogic/Module/BodyModule.h"
  49. #include "GameLogic/Module/ContainModule.h"
  50. #include "GameLogic/Module/DeliverPayloadAIUpdate.h"
  51. #include "GameLogic/Module/FloatUpdate.h"
  52. #include "GameLogic/Module/PhysicsUpdate.h"
  53. #include "GameLogic/Module/SpecialPowerCompletionDie.h"
  54. #include "GameLogic/Module/LifetimeUpdate.h"
  55. #include "GameLogic/Module/RadiusDecalUpdate.h"
  56. #include "GameLogic/Object.h"
  57. #include "GameLogic/ObjectCreationList.h"
  58. #include "GameLogic/PartitionManager.h"
  59. #include "GameLogic/ScriptEngine.h"
  60. #include "GameLogic/Weapon.h"
  61. #include "GameLogic/WeaponSet.h"
  62. #include "Common/CRCDebug.h"
  63. #ifdef _INTERNAL
  64. // for occasional debugging...
  65. //#pragma optimize("", off)
  66. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  67. #endif
  68. ///////////////////////////////////////////////////////////////////////////////////////////////////
  69. // PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
  70. ///////////////////////////////////////////////////////////////////////////////////////////////////
  71. ObjectCreationListStore *TheObjectCreationListStore = NULL; ///< the ObjectCreationList store definition
  72. //-------------------------------------------------------------------------------------------------
  73. static void adjustVector(Coord3D *vec, const Matrix3D* mtx)
  74. {
  75. if (mtx)
  76. {
  77. Vector3 vectmp;
  78. vectmp.X = vec->x;
  79. vectmp.Y = vec->y;
  80. vectmp.Z = vec->z;
  81. vectmp = mtx->Rotate_Vector(vectmp);
  82. vec->x = vectmp.X;
  83. vec->y = vectmp.Y;
  84. vec->z = vectmp.Z;
  85. }
  86. }
  87. ///////////////////////////////////////////////////////////////////////////////////////////////////
  88. // PRIVATE CLASSES ///////////////////////////////////////////////////////////////////////////////////
  89. ///////////////////////////////////////////////////////////////////////////////////////////////////
  90. //-------------------------------------------------------------------------------------------------
  91. void ObjectCreationNugget::create( const Object* primary, const Object* secondary, UnsignedInt lifetimeFrames ) const
  92. {
  93. create( primary, primary ? primary->getPosition() : NULL, secondary ? secondary->getPosition() : NULL, lifetimeFrames );
  94. }
  95. //-------------------------------------------------------------------------------------------------
  96. //This one is called only when we have a nugget that doesn't care about createOwner.
  97. void ObjectCreationNugget::create( const Object *primaryObj, const Coord3D *primary, const Coord3D *secondary, Bool createOwner, UnsignedInt lifetimeFrames ) const
  98. {
  99. create( primaryObj, primary, secondary, lifetimeFrames );
  100. }
  101. //-------------------------------------------------------------------------------------------------
  102. //-------------------------------------------------------------------------------------------------
  103. class FireWeaponNugget : public ObjectCreationNugget
  104. {
  105. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(FireWeaponNugget, "FireWeaponNugget")
  106. public:
  107. FireWeaponNugget() :
  108. m_weapon(NULL)
  109. {
  110. }
  111. virtual void create( const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, UnsignedInt lifetimeFrames = 0 ) const
  112. {
  113. if (!primaryObj || !primary || !secondary)
  114. {
  115. DEBUG_CRASH(("You must have a primary and secondary source for this effect"));
  116. return;
  117. }
  118. if (m_weapon)
  119. {
  120. TheWeaponStore->createAndFireTempWeapon( m_weapon, primaryObj, secondary );
  121. }
  122. }
  123. static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
  124. {
  125. static const FieldParse myFieldParse[] =
  126. {
  127. { "Weapon", INI::parseWeaponTemplate, NULL, offsetof( FireWeaponNugget, m_weapon ) },
  128. { 0, 0, 0, 0 }
  129. };
  130. FireWeaponNugget* nugget = newInstance(FireWeaponNugget);
  131. ini->initFromINI(nugget, myFieldParse);
  132. ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
  133. }
  134. private:
  135. const WeaponTemplate* m_weapon;
  136. };
  137. EMPTY_DTOR(FireWeaponNugget)
  138. //-------------------------------------------------------------------------------------------------
  139. //-------------------------------------------------------------------------------------------------
  140. class AttackNugget : public ObjectCreationNugget
  141. {
  142. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AttackNugget, "AttackNugget")
  143. public:
  144. AttackNugget() :
  145. m_numberOfShots(1),
  146. m_weaponSlot(PRIMARY_WEAPON),
  147. m_deliveryDecalRadius(0)
  148. {
  149. }
  150. virtual void create( const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, UnsignedInt lifetimeFrames = 0 ) const
  151. {
  152. if (!primaryObj || !primary || !secondary)
  153. {
  154. DEBUG_CRASH(("You must have a primary and secondary source for this effect"));
  155. return;
  156. }
  157. // Star trekkin, across the universe.
  158. // Boldly goin forward now, cause we can't find reverse!
  159. // 1:30 left on the clock, Demo looming, should I de-const all of OCL since this one effect needs the
  160. // Primary to help make the objects? Should I rewrite superweapons to completely subsume them
  161. // into normal special powers, so I can have a spell attack with a global timer and a spinny clock?
  162. // Should I const cast? Should I eat them? No! I'd rather hurt them stomp them crush them hurt them
  163. // stomp them while I dance! Down. Those insects make me dance! Down. I'll hurt them while I dance.
  164. Object *primaryObject = const_cast<Object *>(primaryObj);
  165. AIUpdateInterface *ai = primaryObject->getAIUpdateInterface();
  166. if( ai )
  167. {
  168. // lock merely fires till the weapon is empty or the attack is "done"
  169. primaryObject->setWeaponLock( m_weaponSlot, LOCKED_TEMPORARILY );
  170. ai->aiAttackPosition( secondary, m_numberOfShots, CMD_FROM_AI );
  171. }
  172. static NameKeyType key_RadiusDecalUpdate = NAMEKEY("RadiusDecalUpdate");
  173. RadiusDecalUpdate *rd = (RadiusDecalUpdate*)primaryObject->findUpdateModule(key_RadiusDecalUpdate);
  174. if (rd)
  175. {
  176. rd->createRadiusDecal(m_deliveryDecalTemplate, m_deliveryDecalRadius, *secondary);
  177. rd->killWhenNoLongerAttacking(true);
  178. }
  179. }
  180. static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
  181. {
  182. static const FieldParse myFieldParse[] =
  183. {
  184. { "NumberOfShots", INI::parseInt, NULL, offsetof( AttackNugget, m_numberOfShots ) },
  185. { "WeaponSlot", INI::parseLookupList, TheWeaponSlotTypeNamesLookupList, offsetof( AttackNugget, m_weaponSlot ) },
  186. { "DeliveryDecal", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( AttackNugget, m_deliveryDecalTemplate ) },
  187. { "DeliveryDecalRadius", INI::parseReal, NULL, offsetof(AttackNugget, m_deliveryDecalRadius) },
  188. { 0, 0, 0, 0 }
  189. };
  190. AttackNugget* nugget = newInstance(AttackNugget);
  191. ini->initFromINI(nugget, myFieldParse);
  192. ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
  193. }
  194. private:
  195. RadiusDecalTemplate m_deliveryDecalTemplate;
  196. Real m_deliveryDecalRadius;
  197. Int m_numberOfShots;
  198. WeaponSlotType m_weaponSlot;
  199. };
  200. EMPTY_DTOR(AttackNugget)
  201. //-------------------------------------------------------------------------------------------------
  202. //-------------------------------------------------------------------------------------------------
  203. class DeliverPayloadNugget : public ObjectCreationNugget
  204. {
  205. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DeliverPayloadNugget, "DeliverPayloadNugget")
  206. public:
  207. DeliverPayloadNugget() :
  208. m_startAtPreferredHeight(true),
  209. m_startAtMaxSpeed(false),
  210. m_formationSize(1),
  211. m_formationSpacing(25.0f),
  212. m_errorRadius(0.0f),
  213. m_delayDeliveryFramesMax(0),
  214. m_convergenceFactor( 0.0f )
  215. {
  216. //Note: m_data is constructed with default values.
  217. // Added by Sadullah Nader
  218. // Initialization missing and needed
  219. m_payload.clear();
  220. m_putInContainerName.clear();
  221. m_transportName.clear();
  222. // End Add
  223. }
  224. virtual void create(const Object *primaryObj, const Coord3D *primary, const Coord3D *secondary, UnsignedInt lifetimeFrames = 0 ) const
  225. {
  226. create( primaryObj, primary, secondary, true, lifetimeFrames );
  227. }
  228. virtual void create(const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, Bool createOwner, UnsignedInt lifetimeFrames = 0 ) const
  229. {
  230. if (!primaryObj || !primary || !secondary)
  231. {
  232. DEBUG_CRASH(("You must have a primary and secondary source for this effect"));
  233. return;
  234. }
  235. Team* owner = primaryObj ? primaryObj->getControllingPlayer()->getDefaultTeam() : NULL;
  236. //What I'm doing for the purposes of the formations is to calculate the relative positions of
  237. //each member of the formation. To do so, we take the vector from the target location to the
  238. //lead plane location, normalize it, then rotate it 90 degrees (CW and CCW). When we add the
  239. //resultant vectors to the initial vectors, we can calculate the delta positions for each plane.
  240. Real CCWx = 0.0f, CCWy = 0.0f, CWx = 0.0f, CWy = 0.0f;
  241. if( m_formationSize > 1 )
  242. {
  243. //Get the delta x and y values from the target to the origin.
  244. Real dx = primary->x - secondary->x;
  245. Real dy = primary->y - secondary->y;
  246. //Calc length
  247. Real length = sqrt( dx*dx + dy*dy );
  248. //Normalize length
  249. dx /= length;
  250. dy /= length;
  251. //Rotate 90 degrees CCW.
  252. Real radians = 90.0f * PI / 180.0f;
  253. Real s = Sin( radians );
  254. Real c = Cos( radians );
  255. CCWx = dx * c + dy * -s + dx;
  256. CCWy = dx * s + dy * c + dy;
  257. //Rotate 90 degrees CW
  258. s = Sin( -radians );
  259. c = Cos( -radians );
  260. CWx = dx * c + dy * -s + dx;
  261. CWy = dx * s + dy * c + dy;
  262. }
  263. for( Int formationIndex = 0; formationIndex < m_formationSize; formationIndex++ )
  264. {
  265. Coord3D offset;
  266. offset.zero();
  267. Int offsetMultiplier = ( formationIndex + 1 ) / 2 * m_formationSpacing;
  268. if( formationIndex % 2 )
  269. {
  270. //Formation index is odd -- use the CCW deltas
  271. offset.x = CCWx * offsetMultiplier;
  272. offset.y = CCWy * offsetMultiplier;
  273. }
  274. else
  275. {
  276. //Formation index is even -- use the CW deltas
  277. offset.x = CWx * offsetMultiplier;
  278. offset.y = CWy * offsetMultiplier;
  279. }
  280. Coord3D startPos = *primary;
  281. Coord3D moveToPos = *secondary;
  282. startPos.add( &offset );
  283. //Also give our moveToPos the same offset to maintain perfect formation.
  284. moveToPos.add( &offset );
  285. Coord3D targetPos = *secondary;
  286. //Our target position only applies when using fireweapon and when we have multiple planes,
  287. //as is the case with the napalm strike. The target position either be somewhere between the
  288. //moveToPos of the lead plane and that of the relative offset -- determined by the convergenceFactor.
  289. targetPos.x += offset.x * (1.0f - m_convergenceFactor);
  290. targetPos.y += offset.y * (1.0f - m_convergenceFactor);
  291. // first guy in each formation is always spot-on (to keep targeting cursor well-matched)
  292. if ( m_errorRadius > 1.0f && formationIndex > 0 )
  293. {
  294. Real randomRadius = GameLogicRandomValueReal(0, m_errorRadius );
  295. Real randomAngle = GameLogicRandomValueReal(0, PI*2 );
  296. targetPos.x += randomRadius * Cos( randomAngle );
  297. targetPos.y += randomRadius * Sin( randomAngle );
  298. }
  299. Real orient = atan2( moveToPos.y - startPos.y, moveToPos.x - startPos.x);
  300. if( m_data.m_distToTarget > 0 )
  301. {
  302. const Real SLOP = 1.5f;
  303. startPos.x -= Cos(orient) * m_data.m_distToTarget * SLOP;
  304. startPos.y -= Sin(orient) * m_data.m_distToTarget * SLOP;
  305. }
  306. Object *transport;
  307. if( createOwner )
  308. {
  309. const ThingTemplate* ttn = TheThingFactory->findTemplate(m_transportName);
  310. transport = TheThingFactory->newObject( ttn, owner );
  311. if( !transport )
  312. {
  313. return;
  314. }
  315. transport->setPosition(&startPos);
  316. transport->setOrientation(orient);
  317. transport->setProducer(primaryObj);
  318. //Adding this nifty flag allows enemy players to target it manually with weapons :)
  319. transport->setScriptStatus( OBJECT_STATUS_SCRIPT_TARGETABLE );
  320. if ( m_delayDeliveryFramesMax > 0 )
  321. {
  322. transport->setDisabledUntil( DISABLED_DEFAULT, TheGameLogic->getFrame() + GameLogicRandomValue(0, m_delayDeliveryFramesMax) );
  323. }
  324. }
  325. else
  326. {
  327. transport = (Object*)primaryObj;
  328. }
  329. // Notify special power tracking
  330. SpecialPowerCompletionDie *die = transport->findSpecialPowerCompletionDie();
  331. if (die)
  332. {
  333. if (formationIndex == 0)
  334. die->setCreator(primaryObj->getID());
  335. else
  336. die->setCreator(INVALID_ID);
  337. }
  338. static NameKeyType key_DeliverPayloadAIUpdate = NAMEKEY("DeliverPayloadAIUpdate");
  339. DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)transport->findUpdateModule(key_DeliverPayloadAIUpdate);
  340. if( ai )
  341. {
  342. if( m_startAtMaxSpeed && createOwner )
  343. {
  344. PhysicsBehavior* physics = transport->getPhysics();
  345. if (physics)
  346. {
  347. Coord3D startingForce = *transport->getUnitDirectionVector2D();
  348. Real maxSpeed = ai->getCurLocomotor()->getMaxSpeedForCondition(transport->getBodyModule()->getDamageState());
  349. Real factor = maxSpeed * physics->getMass();
  350. startingForce.x *= factor;
  351. startingForce.y *= factor;
  352. startingForce.z *= factor;
  353. physics->applyMotiveForce( &startingForce );
  354. }
  355. }
  356. // only the first guy in each formation gets a delivery decal
  357. DeliverPayloadData data = m_data;
  358. if (formationIndex != 0)
  359. data.m_deliveryDecalRadius = 0;
  360. ai->deliverPayload( &moveToPos, &targetPos, &data );
  361. if( m_startAtPreferredHeight && createOwner )
  362. {
  363. startPos.z = TheTerrainLogic->getGroundHeight(startPos.x, startPos.y) + ai->getCurLocomotor()->getPreferredHeight();
  364. transport->setPosition(&startPos);
  365. }
  366. const ThingTemplate* putInContainerTmpl = m_putInContainerName.isEmpty() ? NULL : TheThingFactory->findTemplate(m_putInContainerName);
  367. for (std::vector<Payload>::const_iterator it = m_payload.begin(); it != m_payload.end(); ++it)
  368. {
  369. const ThingTemplate* payloadTmpl = TheThingFactory->findTemplate(it->m_payloadName);
  370. for (int i = 0; i < it->m_payloadCount; ++i)
  371. {
  372. Object* payload = TheThingFactory->newObject( payloadTmpl, owner );
  373. payload->setPosition(&startPos);
  374. payload->setProducer(transport);
  375. // Notify special power tracking
  376. SpecialPowerCompletionDie *die = payload->findSpecialPowerCompletionDie();
  377. if (die)
  378. {
  379. if (formationIndex == 0 && i == 0)
  380. die->setCreator(primaryObj->getID());
  381. else
  382. die->setCreator(INVALID_ID);
  383. }
  384. if (putInContainerTmpl)
  385. {
  386. Object* container = TheThingFactory->newObject( putInContainerTmpl, owner );
  387. container->setPosition(&startPos);
  388. container->setProducer(transport);
  389. // Notify special power tracking
  390. SpecialPowerCompletionDie *die = container->findSpecialPowerCompletionDie();
  391. if (die)
  392. {
  393. if (formationIndex == 0 && i == 0)
  394. die->setCreator(primaryObj->getID());
  395. else
  396. die->setCreator(INVALID_ID);
  397. }
  398. if (container->getContain() && container->getContain()->isValidContainerFor(payload, true))
  399. {
  400. container->getContain()->addToContain(payload);
  401. payload = container;
  402. }
  403. else
  404. {
  405. DEBUG_CRASH(("DeliverPayload: PutInContainer %s is full, or not valid for the payload %s!",m_putInContainerName.str(),it->m_payloadName.str()));
  406. }
  407. }
  408. if (transport->getContain() && transport->getContain()->isValidContainerFor(payload, true))
  409. {
  410. transport->getContain()->addToContain(payload);
  411. }
  412. else
  413. {
  414. DEBUG_CRASH(("DeliverPayload: transport %s is full, or not valid for the payload %s!",m_transportName.str(),it->m_payloadName.str()));
  415. }
  416. }
  417. }
  418. }
  419. else
  420. {
  421. DEBUG_CRASH(("You should really have a DeliverPayloadAIUpdate here"));
  422. }
  423. }
  424. }
  425. static void parsePayload( INI* ini, void *instance, void *store, const void* /*userData*/ )
  426. {
  427. DeliverPayloadNugget* self = (DeliverPayloadNugget*)instance;
  428. const char* name = ini->getNextToken();
  429. const char* countStr = ini->getNextTokenOrNull();
  430. Int count = countStr ? INI::scanInt(countStr) : 1;
  431. Payload p;
  432. p.m_payloadName.set(name);
  433. p.m_payloadCount = count;
  434. self->m_payload.push_back(p);
  435. }
  436. static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
  437. {
  438. static const FieldParse myFieldParse[] =
  439. {
  440. //***************************************************************
  441. //OBJECT CREATION LIST SPECIFIC DATA -- once created data no longer needed
  442. //The transport(s) that carry all the payload items (and initial physics information)
  443. { "Transport", INI::parseAsciiString, NULL, offsetof(DeliverPayloadNugget, m_transportName) },
  444. { "StartAtPreferredHeight", INI::parseBool, NULL, offsetof(DeliverPayloadNugget, m_startAtPreferredHeight) },
  445. { "StartAtMaxSpeed", INI::parseBool, NULL, offsetof(DeliverPayloadNugget, m_startAtMaxSpeed) },
  446. //For multiple transports, this defines the formation (and convergence if all weapons will hit same target)
  447. { "FormationSize", INI::parseUnsignedInt, NULL, offsetof( DeliverPayloadNugget, m_formationSize) },
  448. { "FormationSpacing", INI::parseReal, NULL, offsetof( DeliverPayloadNugget, m_formationSpacing) },
  449. { "WeaponConvergenceFactor", INI::parseReal, NULL, offsetof( DeliverPayloadNugget, m_convergenceFactor ) },
  450. { "WeaponErrorRadius", INI::parseReal, NULL, offsetof( DeliverPayloadNugget, m_errorRadius ) },
  451. { "DelayDeliveryMax", INI::parseDurationUnsignedInt, NULL, offsetof( DeliverPayloadNugget, m_delayDeliveryFramesMax ) },
  452. //Payload information (it's all created now and stored inside)
  453. { "Payload", parsePayload, NULL, 0 },
  454. { "PutInContainer", INI::parseAsciiString, NULL, offsetof( DeliverPayloadNugget, m_putInContainerName) },
  455. //END OBJECT CREATION LIST SPECIFIC DATA
  456. //***************************************************************
  457. //***************************************************************
  458. //DELIVERPAYLOADDATA contains the rest (and most) of the parsed data.
  459. //***************************************************************
  460. { 0, 0, 0, 0 }
  461. };
  462. DeliverPayloadNugget* nugget = newInstance(DeliverPayloadNugget);
  463. MultiIniFieldParse p;
  464. p.add(myFieldParse);
  465. p.add(DeliverPayloadData::getFieldParse(), offsetof( DeliverPayloadNugget, m_data ));
  466. ini->initFromINIMulti(nugget, p);
  467. ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
  468. }
  469. private:
  470. struct Payload
  471. {
  472. AsciiString m_payloadName;
  473. Int m_payloadCount;
  474. };
  475. //Specific data needed to create the transport(s), internal payload, and initial physics.
  476. AsciiString m_transportName;
  477. AsciiString m_putInContainerName;
  478. std::vector<Payload> m_payload;
  479. Real m_formationSpacing;
  480. Real m_convergenceFactor;
  481. Real m_errorRadius;
  482. UnsignedInt m_delayDeliveryFramesMax;
  483. UnsignedInt m_formationSize;
  484. Bool m_startAtPreferredHeight;
  485. Bool m_startAtMaxSpeed;
  486. //AI specific data passed over to DeliverPayloadAIUpdate::deliver()
  487. DeliverPayloadData m_data;
  488. };
  489. EMPTY_DTOR(DeliverPayloadNugget)
  490. //-------------------------------------------------------------------------------------------------
  491. static void calcRandomForce(Real minMag, Real maxMag, Real minPitch, Real maxPitch, Coord3D* force)
  492. {
  493. Real angle = GameLogicRandomValueReal(0, 2*PI);
  494. Real pitch = GameLogicRandomValueReal(minPitch, maxPitch);
  495. Real mag = GameLogicRandomValueReal(minMag, maxMag);
  496. Matrix3D mtx(1);
  497. mtx.Scale(mag);
  498. mtx.Rotate_Z(angle);
  499. mtx.Rotate_Y(-pitch);
  500. Vector3 v = mtx.Get_X_Vector();
  501. force->x = v.X;
  502. force->y = v.Y;
  503. force->z = v.Z;
  504. }
  505. //-------------------------------------------------------------------------------------------------
  506. class ApplyRandomForceNugget : public ObjectCreationNugget
  507. {
  508. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ApplyRandomForceNugget, "ApplyRandomForceNugget")
  509. public:
  510. ApplyRandomForceNugget() :
  511. m_spinRate(0.0f),
  512. m_minMag(0.0f),
  513. m_maxMag(0.0f),
  514. m_minPitch(0.0f),
  515. m_maxPitch(0.0f)
  516. {
  517. }
  518. virtual void create( const Object* primary, const Object* secondary, UnsignedInt lifetimeFrames = 0 ) const
  519. {
  520. if (primary)
  521. {
  522. /// @todo srj -- ack. const_cast is evil.
  523. PhysicsBehavior* p = const_cast<Object*>(primary)->getPhysics();
  524. if (p)
  525. {
  526. Coord3D force;
  527. calcRandomForce(m_minMag, m_maxMag, m_minPitch, m_maxPitch, &force);
  528. p->applyForce(&force);
  529. Real yaw = GameLogicRandomValueReal( -m_spinRate, m_spinRate );
  530. Real roll = GameLogicRandomValueReal( -m_spinRate, m_spinRate );
  531. Real pitch = GameLogicRandomValueReal( -m_spinRate, m_spinRate );
  532. p->setYawRate(yaw);
  533. p->setRollRate(roll);
  534. p->setPitchRate(pitch);
  535. }
  536. else
  537. {
  538. DEBUG_CRASH(("You must have a Physics module source for this effect"));
  539. }
  540. }
  541. else
  542. {
  543. DEBUG_CRASH(("You must have a primary source for this effect"));
  544. }
  545. }
  546. virtual void create(const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, UnsignedInt lifetimeFrames = 0 ) const
  547. {
  548. DEBUG_CRASH(("You must call this effect with an object, not a location"));
  549. }
  550. static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
  551. {
  552. static const FieldParse myFieldParse[] =
  553. {
  554. { "SpinRate", INI::parseAngularVelocityReal, NULL, offsetof(ApplyRandomForceNugget, m_spinRate) },
  555. { "MinForceMagnitude", INI::parseReal, NULL, offsetof(ApplyRandomForceNugget, m_minMag) },
  556. { "MaxForceMagnitude", INI::parseReal, NULL, offsetof(ApplyRandomForceNugget, m_maxMag) },
  557. { "MinForcePitch", INI::parseAngleReal, NULL, offsetof(ApplyRandomForceNugget, m_minPitch) },
  558. { "MaxForcePitch", INI::parseAngleReal, NULL, offsetof(ApplyRandomForceNugget, m_maxPitch) },
  559. { 0, 0, 0, 0 }
  560. };
  561. ApplyRandomForceNugget* nugget = newInstance(ApplyRandomForceNugget);
  562. ini->initFromINI(nugget, myFieldParse);
  563. ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
  564. }
  565. protected:
  566. private:
  567. Real m_spinRate;
  568. Real m_minMag, m_maxMag;
  569. Real m_minPitch, m_maxPitch;
  570. };
  571. EMPTY_DTOR(ApplyRandomForceNugget)
  572. //-------------------------------------------------------------------------------------------------
  573. enum DebrisDisposition
  574. {
  575. LIKE_EXISTING = 0x00000001,
  576. ON_GROUND_ALIGNED = 0x00000002,
  577. SEND_IT_FLYING = 0x00000004,
  578. SEND_IT_UP = 0x00000008,
  579. SEND_IT_OUT = 0x00000010,
  580. RANDOM_FORCE = 0x00000020,
  581. FLOATING = 0x00000040,
  582. INHERIT_VELOCITY = 0x00000080,
  583. WHIRLING = 0x00000100
  584. };
  585. static const char* DebrisDispositionNames[] =
  586. {
  587. "LIKE_EXISTING",
  588. "ON_GROUND_ALIGNED",
  589. "SEND_IT_FLYING",
  590. "SEND_IT_UP",
  591. "SEND_IT_OUT",
  592. "RANDOM_FORCE",
  593. "FLOATING",
  594. "INHERIT_VELOCITY",
  595. "WHIRLING",
  596. };
  597. std::vector<AsciiString> debrisModelNamesGlobalHack;
  598. //-------------------------------------------------------------------------------------------------
  599. //-------------------------------------------------------------------------------------------------
  600. static void parseFrictionPerSec( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
  601. {
  602. Real fricPerSec = INI::scanReal(ini->getNextToken());
  603. Real fricPerFrame = fricPerSec * SECONDS_PER_LOGICFRAME_REAL;
  604. *(Real *)store = fricPerFrame;
  605. }
  606. //-------------------------------------------------------------------------------------------------
  607. class GenericObjectCreationNugget : public ObjectCreationNugget
  608. {
  609. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(GenericObjectCreationNugget, "GenericObjectCreationNugget")
  610. public:
  611. GenericObjectCreationNugget() :
  612. m_requiresLivePlayer(FALSE),
  613. m_debrisToGenerate(1),
  614. m_mass(0),
  615. m_extraBounciness(0),
  616. m_extraFriction(0),
  617. m_disposition(ON_GROUND_ALIGNED),
  618. m_dispositionIntensity(0.0f),
  619. m_spinRate(-1.0f),
  620. m_yawRate(-1.0f),
  621. m_rollRate(-1.0f),
  622. m_pitchRate(-1.0f),
  623. m_nameAreObjects(true),
  624. m_okToChangeModelColor(false),
  625. m_minLODRequired(STATIC_GAME_LOD_LOW),
  626. m_ignorePrimaryObstacle(false),
  627. m_inheritsVeterancy(false),
  628. m_skipIfSignificantlyAirborne(false),
  629. m_invulnerableTime(0),
  630. m_containInsideSourceObject(FALSE),
  631. m_minHealth(1.0f),
  632. m_maxHealth(1.0f),
  633. m_orientInForceDirection(false),
  634. m_spreadFormation(false),
  635. m_minDistanceAFormation(0.0f),
  636. m_minDistanceBFormation(0.0f),
  637. m_maxDistanceFormation(0.0f),
  638. m_fadeIn(false),
  639. m_fadeOut(false),
  640. m_fadeFrames(0),
  641. m_fadeSoundName(AsciiString::TheEmptyString), // Added By Sadullah Nader
  642. m_particleSysName(AsciiString::TheEmptyString), // Added By Sadullah Nader
  643. m_putInContainer(AsciiString::TheEmptyString), // Added By Sadullah Nader
  644. m_minMag(0.0f),
  645. m_maxMag(0.0f),
  646. m_minPitch(0.0f),
  647. m_maxPitch(0.0f),
  648. m_minFrames(0),
  649. m_maxFrames(0),
  650. m_shadowType(SHADOW_NONE),
  651. m_fxFinal(NULL),
  652. m_preserveLayer(true),
  653. m_objectCount(0) // Added By Sadullah Nader
  654. {
  655. // Change Made by Sadullah Nader
  656. // for init purposes, easier to read
  657. m_offset.zero();
  658. }
  659. virtual void create(const Object* primary, const Object* secondary, UnsignedInt lifetimeFrames = 0 ) const
  660. {
  661. if (primary)
  662. {
  663. if (m_skipIfSignificantlyAirborne && primary->isSignificantlyAboveTerrain())
  664. return;
  665. reallyCreate( primary->getPosition(), primary->getTransformMatrix(), primary->getOrientation(), primary, lifetimeFrames );
  666. }
  667. else
  668. {
  669. DEBUG_CRASH(("You must have a primary source for this effect"));
  670. }
  671. }
  672. virtual void create(const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, UnsignedInt lifetimeFrames = 0 ) const
  673. {
  674. if (primary)
  675. {
  676. const Matrix3D *xfrm = NULL;
  677. const Real orientation = 0.0;
  678. reallyCreate( primary, xfrm, orientation, primaryObj, lifetimeFrames );
  679. }
  680. else
  681. {
  682. DEBUG_CRASH(("You must have a primary source for this effect"));
  683. }
  684. }
  685. static const FieldParse* getCommonFieldParse()
  686. {
  687. static const FieldParse commonFieldParse[] =
  688. {
  689. { "PutInContainer", INI::parseAsciiString, NULL, offsetof( GenericObjectCreationNugget, m_putInContainer) },
  690. { "ParticleSystem", INI::parseAsciiString, NULL, offsetof( GenericObjectCreationNugget, m_particleSysName) },
  691. { "Count", INI::parseInt, NULL, offsetof( GenericObjectCreationNugget, m_debrisToGenerate ) },
  692. { "IgnorePrimaryObstacle", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_ignorePrimaryObstacle) },
  693. { "OrientInForceDirection", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_orientInForceDirection) },
  694. { "ExtraBounciness", INI::parseReal, NULL, offsetof( GenericObjectCreationNugget, m_extraBounciness ) },
  695. { "ExtraFriction", parseFrictionPerSec, NULL, offsetof( GenericObjectCreationNugget, m_extraFriction ) },
  696. { "Offset", INI::parseCoord3D, NULL, offsetof( GenericObjectCreationNugget, m_offset ) },
  697. { "Disposition", INI::parseBitString32, DebrisDispositionNames, offsetof( GenericObjectCreationNugget, m_disposition ) },
  698. { "DispositionIntensity", INI::parseReal, NULL, offsetof( GenericObjectCreationNugget, m_dispositionIntensity ) },
  699. { "SpinRate", INI::parseAngularVelocityReal, NULL, offsetof(GenericObjectCreationNugget, m_spinRate) },
  700. { "YawRate", INI::parseAngularVelocityReal, NULL, offsetof(GenericObjectCreationNugget, m_yawRate) },
  701. { "RollRate", INI::parseAngularVelocityReal, NULL, offsetof(GenericObjectCreationNugget, m_rollRate) },
  702. { "PitchRate", INI::parseAngularVelocityReal, NULL, offsetof(GenericObjectCreationNugget, m_pitchRate) },
  703. { "MinForceMagnitude", INI::parseReal, NULL, offsetof(GenericObjectCreationNugget, m_minMag) },
  704. { "MaxForceMagnitude", INI::parseReal, NULL, offsetof(GenericObjectCreationNugget, m_maxMag) },
  705. { "MinForcePitch", INI::parseAngleReal, NULL, offsetof(GenericObjectCreationNugget, m_minPitch) },
  706. { "MaxForcePitch", INI::parseAngleReal, NULL, offsetof(GenericObjectCreationNugget, m_maxPitch) },
  707. { "MinLifetime", INI::parseDurationUnsignedInt, NULL, offsetof( GenericObjectCreationNugget, m_minFrames ) },
  708. { "MaxLifetime", INI::parseDurationUnsignedInt, NULL, offsetof( GenericObjectCreationNugget, m_maxFrames ) },
  709. { "SpreadFormation", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_spreadFormation) },
  710. { "MinDistanceAFormation", INI::parseReal, NULL, offsetof(GenericObjectCreationNugget, m_minDistanceAFormation) },
  711. { "MinDistanceBFormation", INI::parseReal, NULL, offsetof(GenericObjectCreationNugget, m_minDistanceBFormation) },
  712. { "MaxDistanceFormation", INI::parseReal, NULL, offsetof(GenericObjectCreationNugget, m_maxDistanceFormation) },
  713. { "FadeIn", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_fadeIn) },
  714. { "FadeOut", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_fadeOut) },
  715. { "FadeTime", INI::parseDurationUnsignedInt, NULL, offsetof(GenericObjectCreationNugget, m_fadeFrames) },
  716. { "FadeSound", INI::parseAsciiString, NULL, offsetof( GenericObjectCreationNugget, m_fadeSoundName) },
  717. { "PreserveLayer", INI::parseBool, NULL, offsetof( GenericObjectCreationNugget, m_preserveLayer) },
  718. { 0, 0, 0, 0 }
  719. };
  720. return commonFieldParse;
  721. }
  722. static void parseObject(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
  723. {
  724. static const FieldParse myFieldParse[] =
  725. {
  726. { "ContainInsideSourceObject", INI::parseBool, NULL, offsetof( GenericObjectCreationNugget, m_containInsideSourceObject) },
  727. { "ObjectNames", parseDebrisObjectNames, NULL, 0 },
  728. { "ObjectCount", INI::parseInt, NULL, offsetof(GenericObjectCreationNugget, m_objectCount) },
  729. { "InheritsVeterancy", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_inheritsVeterancy) },
  730. { "SkipIfSignificantlyAirborne", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_skipIfSignificantlyAirborne) },
  731. { "InvulnerableTime", INI::parseDurationUnsignedInt, NULL, offsetof(GenericObjectCreationNugget, m_invulnerableTime) },
  732. { "MinHealth", INI::parsePercentToReal, NULL, offsetof(GenericObjectCreationNugget, m_minHealth) },
  733. { "MaxHealth", INI::parsePercentToReal, NULL, offsetof(GenericObjectCreationNugget, m_maxHealth) },
  734. { "RequiresLivePlayer", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_requiresLivePlayer) },
  735. { 0, 0, 0, 0 }
  736. };
  737. MultiIniFieldParse p;
  738. p.add(getCommonFieldParse());
  739. p.add(myFieldParse);
  740. GenericObjectCreationNugget* nugget = newInstance(GenericObjectCreationNugget);
  741. nugget->m_nameAreObjects = true;
  742. ini->initFromINIMulti(nugget, p);
  743. ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
  744. }
  745. static void parseDebris(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
  746. {
  747. static const FieldParse myFieldParse[] =
  748. {
  749. { "ModelNames", parseDebrisObjectNames, NULL, 0 },
  750. { "Mass", INI::parsePositiveNonZeroReal, NULL, offsetof( GenericObjectCreationNugget, m_mass ) },
  751. { "AnimationSet", parseAnimSet, NULL, offsetof( GenericObjectCreationNugget, m_animSets) },
  752. { "FXFinal", INI::parseFXList, NULL, offsetof( GenericObjectCreationNugget, m_fxFinal) },
  753. { "OkToChangeModelColor", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_okToChangeModelColor) },
  754. { "MinLODRequired", INI::parseStaticGameLODLevel, NULL, offsetof(GenericObjectCreationNugget, m_minLODRequired) },
  755. { "Shadow", INI::parseBitString32, TheShadowNames, offsetof( GenericObjectCreationNugget, m_shadowType ) },
  756. { "BounceSound", INI::parseAudioEventRTS, NULL, offsetof( GenericObjectCreationNugget, m_bounceSound) },
  757. { 0, 0, 0, 0 }
  758. };
  759. MultiIniFieldParse p;
  760. p.add(getCommonFieldParse());
  761. p.add(myFieldParse);
  762. GenericObjectCreationNugget* nugget = newInstance(GenericObjectCreationNugget);
  763. nugget->m_nameAreObjects = false;
  764. ini->initFromINIMulti(nugget, p);
  765. DEBUG_ASSERTCRASH(nugget->m_mass > 0.0f, ("Zero masses are not allowed for debris!\n"));
  766. ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
  767. }
  768. static void parseAnimSet(INI *ini, void * /*instance*/, void* store, const void* /*userData*/)
  769. {
  770. AnimSet anim;
  771. anim.m_animInitial = ini->getNextAsciiString();
  772. anim.m_animFlying = ini->getNextAsciiString();
  773. anim.m_animFinal = ini->getNextAsciiString();
  774. ((std::vector<AnimSet>*)store)->push_back(anim);
  775. }
  776. protected:
  777. void doStuffToObj(
  778. Object* obj,
  779. const AsciiString& modelName,
  780. const Coord3D *pos,
  781. const Matrix3D *mtx,
  782. Real orientation,
  783. const Object *sourceObj,
  784. UnsignedInt lifetimeFrames
  785. ) const
  786. {
  787. obj->setProducer(sourceObj);
  788. static NameKeyType key_LifetimeUpdate = NAMEKEY("LifetimeUpdate");
  789. LifetimeUpdate* lup = (LifetimeUpdate*)obj->findUpdateModule(key_LifetimeUpdate);
  790. if( lup )
  791. {
  792. if( lifetimeFrames )
  793. {
  794. //Passed in override, use this value for a specific lifetime!!!
  795. lup->setLifetimeRange( lifetimeFrames, lifetimeFrames );
  796. }
  797. else if( m_maxFrames > 0 )
  798. {
  799. // They will both be zero if no lifetime was specified in the OCL. It could be in the Object so don't mess with it.
  800. // So the OCL listing will override the Object listing for lifetime, but ONLY if there is one.
  801. lup->setLifetimeRange(m_minFrames, m_maxFrames);
  802. }
  803. }
  804. if (!m_nameAreObjects)
  805. {
  806. for (DrawModule** dm = obj->getDrawable()->getDrawModules(); *dm; ++dm)
  807. {
  808. DebrisDrawInterface* di = (*dm)->getDebrisDrawInterface();
  809. if (di)
  810. {
  811. di->setModelName(modelName, m_okToChangeModelColor ? obj->getIndicatorColor() : 0, m_shadowType);
  812. if (m_animSets.size() > 0)
  813. {
  814. Int which = GameLogicRandomValue(0, m_animSets.size()-1);
  815. di->setAnimNames(m_animSets[which].m_animInitial, m_animSets[which].m_animFlying, m_animSets[which].m_animFinal, m_fxFinal);
  816. }
  817. }
  818. }
  819. }
  820. Coord3D offset = m_offset;
  821. if (mtx)
  822. adjustVector(&offset, mtx);
  823. Coord3D chunkPos;
  824. chunkPos.x = pos->x + offset.x;
  825. chunkPos.y = pos->y + offset.y;
  826. chunkPos.z = pos->z + offset.z;
  827. if (!m_particleSysName.isEmpty())
  828. {
  829. const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate(m_particleSysName);
  830. if (tmp)
  831. {
  832. ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(tmp);
  833. sys->attachToObject(obj);
  834. }
  835. }
  836. if (m_ignorePrimaryObstacle)
  837. {
  838. PhysicsBehavior* p = obj->getPhysics();
  839. if (p)
  840. p->setIgnoreCollisionsWith(sourceObj);
  841. }
  842. // set its beginning health
  843. BodyModuleInterface *body = obj->getBodyModule();
  844. Real healthPercent = GameLogicRandomValueReal( m_minHealth, m_maxHealth );
  845. if (body)
  846. body->setInitialHealth(healthPercent * 100.0f);
  847. // If they have a SlavedUpdate, then I have to tell them who their daddy is from now on.
  848. for (BehaviorModule** update = obj->getBehaviorModules(); *update; ++update)
  849. {
  850. SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface();
  851. if (sdu != NULL)
  852. {
  853. sdu->onEnslave( sourceObj );
  854. break;
  855. }
  856. }
  857. if (m_inheritsVeterancy && sourceObj && obj->getExperienceTracker()->isTrainable())
  858. {
  859. DEBUG_LOG(("Object %s inherits veterancy level %d from %s\n",
  860. obj->getTemplate()->getName().str(), sourceObj->getVeterancyLevel(), sourceObj->getTemplate()->getName().str()));
  861. VeterancyLevel v = sourceObj->getVeterancyLevel();
  862. obj->getExperienceTracker()->setVeterancyLevel(v);
  863. //In order to make things easier for the designers, we are going to transfer the unit name
  864. //to the ejected thing... so the designer can control the pilot with the scripts.
  865. TheScriptEngine->transferObjectName( sourceObj->getName(), obj );
  866. }
  867. if ( m_invulnerableTime > 0 )
  868. {
  869. obj->goInvulnerable( m_invulnerableTime );
  870. }
  871. if( BitTest( m_disposition, INHERIT_VELOCITY ) && sourceObj )
  872. {
  873. const PhysicsBehavior *sourcePhysics = sourceObj->getPhysics();
  874. PhysicsBehavior *objectPhysics = obj->getPhysics();
  875. if( sourcePhysics && objectPhysics )
  876. {
  877. objectPhysics->applyForce( sourcePhysics->getVelocity() );
  878. }
  879. }
  880. if( BitTest( m_disposition, LIKE_EXISTING ) )
  881. {
  882. if (mtx)
  883. obj->setTransformMatrix(mtx);
  884. else
  885. obj->setOrientation(orientation);
  886. obj->setPosition(&chunkPos);
  887. if (sourceObj && sourceObj->isAboveTerrain())
  888. {
  889. PhysicsBehavior* physics = obj->getPhysics();
  890. if (physics)
  891. physics->setAllowToFall(true);
  892. }
  893. }
  894. if( BitTest( m_disposition, ON_GROUND_ALIGNED ) )
  895. {
  896. chunkPos.z = 99999.0f;
  897. PathfindLayerEnum layer = TheTerrainLogic->getHighestLayerForDestination(&chunkPos);
  898. obj->setOrientation(GameLogicRandomValueReal(0.0f, 2 * PI));
  899. chunkPos.z = TheTerrainLogic->getLayerHeight( chunkPos.x, chunkPos.y, layer );
  900. // ensure we are slightly above the bridge, to account for fudge & sloppy art
  901. if (layer != LAYER_GROUND)
  902. chunkPos.z += 1.0f;
  903. obj->setLayer(layer);
  904. obj->setPosition(&chunkPos);
  905. }
  906. if( BitTest( m_disposition, SEND_IT_OUT ) )
  907. {
  908. obj->setOrientation(GameLogicRandomValueReal(0.0f, 2 * PI));
  909. chunkPos.z = TheTerrainLogic->getGroundHeight( chunkPos.x, chunkPos.y );
  910. obj->setPosition(&chunkPos);
  911. PhysicsBehavior* objUp = obj->getPhysics();
  912. if (objUp)
  913. {
  914. if (!m_nameAreObjects)
  915. objUp->setMass( m_mass );
  916. objUp->setExtraFriction(m_extraFriction);
  917. Coord3D force;
  918. Real horizForce = 4.0f * m_dispositionIntensity; // 2
  919. force.x = GameLogicRandomValueReal( -horizForce, horizForce );
  920. force.y = GameLogicRandomValueReal( -horizForce, horizForce );
  921. force.z = 0;
  922. objUp->applyForce(&force);
  923. if (m_orientInForceDirection)
  924. orientation = atan2(force.y, force.x);
  925. }
  926. }
  927. if( BitTest( m_disposition, SEND_IT_FLYING | SEND_IT_UP | RANDOM_FORCE ) )
  928. {
  929. if (mtx)
  930. {
  931. DUMPMATRIX3D(mtx);
  932. obj->setTransformMatrix(mtx);
  933. }
  934. obj->setPosition(&chunkPos);
  935. DUMPCOORD3D(&chunkPos);
  936. PhysicsBehavior* objUp = obj->getPhysics();
  937. if (objUp)
  938. {
  939. if (!m_nameAreObjects)
  940. {
  941. DUMPREAL(m_mass);
  942. objUp->setMass( m_mass );
  943. }
  944. DEBUG_ASSERTCRASH(objUp->getMass() > 0.0f, ("Zero masses are not allowed for obj!\n"));
  945. objUp->setExtraBounciness(m_extraBounciness);
  946. objUp->setExtraFriction(m_extraFriction);
  947. objUp->setAllowBouncing(true);
  948. objUp->setBounceSound(&m_bounceSound);
  949. DUMPREAL(m_extraBounciness);
  950. DUMPREAL(m_extraFriction);
  951. // if omitted from INI, calc it based on intensity.
  952. Real spinRate = m_spinRate >= 0.0f ? m_spinRate : (PI/32.0f) * m_dispositionIntensity;
  953. // Treat these as overrides.
  954. Real yawRate = m_yawRate >= 0.0f ? m_yawRate : spinRate;
  955. Real rollRate = m_rollRate >= 0.0f ? m_rollRate : spinRate;
  956. Real pitchRate = m_pitchRate >= 0.0f ? m_pitchRate : spinRate;
  957. DUMPREAL(spinRate);
  958. DUMPREAL(yawRate);
  959. DUMPREAL(rollRate);
  960. DUMPREAL(pitchRate);
  961. Real yaw = GameLogicRandomValueReal( -yawRate, yawRate );
  962. Real roll = GameLogicRandomValueReal( -rollRate, rollRate );
  963. Real pitch = GameLogicRandomValueReal( -pitchRate, pitchRate );
  964. DUMPREAL(yaw);
  965. DUMPREAL(roll);
  966. DUMPREAL(pitch);
  967. Coord3D force;
  968. if( BitTest( m_disposition, SEND_IT_FLYING ) )
  969. {
  970. Real horizForce = 4.0f * m_dispositionIntensity; // 2
  971. Real vertForce = 3.0f * m_dispositionIntensity; // 3
  972. force.x = GameLogicRandomValueReal( -horizForce, horizForce );
  973. force.y = GameLogicRandomValueReal( -horizForce, horizForce );
  974. force.z = GameLogicRandomValueReal( vertForce * 0.33f, vertForce );
  975. DUMPREAL(horizForce);
  976. DUMPREAL(vertForce);
  977. DUMPCOORD3D(&force);
  978. }
  979. else if (BitTest(m_disposition, SEND_IT_UP) )
  980. {
  981. Real horizForce = 2.0f * m_dispositionIntensity;
  982. Real vertForce = 4.0f * m_dispositionIntensity;
  983. force.x = GameLogicRandomValueReal( -horizForce, horizForce );
  984. force.y = GameLogicRandomValueReal( -horizForce, horizForce );
  985. force.z = GameLogicRandomValueReal( vertForce * 0.75f, vertForce );
  986. DUMPREAL(horizForce);
  987. DUMPREAL(vertForce);
  988. DUMPCOORD3D(&force);
  989. }
  990. else
  991. {
  992. calcRandomForce(m_minMag, m_maxMag, m_minPitch, m_maxPitch, &force);
  993. DUMPREAL(m_minMag);
  994. DUMPREAL(m_maxMag);
  995. DUMPREAL(m_minPitch);
  996. DUMPREAL(m_maxPitch);
  997. DUMPCOORD3D(&force);
  998. }
  999. objUp->applyForce(&force);
  1000. if (m_orientInForceDirection)
  1001. {
  1002. orientation = atan2(force.y, force.x);
  1003. }
  1004. DUMPREAL(orientation);
  1005. objUp->setAngles(orientation, 0, 0);
  1006. objUp->setYawRate(yaw);
  1007. objUp->setRollRate(roll);
  1008. objUp->setPitchRate(pitch);
  1009. DUMPCOORD3D(objUp->getAcceleration());
  1010. DUMPCOORD3D(objUp->getVelocity());
  1011. DUMPMATRIX3D(obj->getTransformMatrix());
  1012. }
  1013. }
  1014. if( BitTest( m_disposition, WHIRLING ) )
  1015. {
  1016. PhysicsBehavior* objUp = obj->getPhysics();
  1017. if (objUp)
  1018. {
  1019. Real yaw = GameLogicRandomValueReal( -m_dispositionIntensity, m_dispositionIntensity );
  1020. Real roll = GameLogicRandomValueReal( -m_dispositionIntensity, m_dispositionIntensity );
  1021. Real pitch = GameLogicRandomValueReal( -m_dispositionIntensity, m_dispositionIntensity );
  1022. objUp->setYawRate(yaw);
  1023. objUp->setRollRate(roll);
  1024. objUp->setPitchRate(pitch);
  1025. }
  1026. }
  1027. if( BitTest( m_disposition, FLOATING ) )
  1028. {
  1029. static NameKeyType key = NAMEKEY( "FloatUpdate" );
  1030. FloatUpdate *floatUpdate = (FloatUpdate *)obj->findUpdateModule( key );
  1031. if( floatUpdate )
  1032. floatUpdate->setEnabled( TRUE );
  1033. }
  1034. if( m_containInsideSourceObject )
  1035. {
  1036. // The Obj has been totally made, so stuff it inside ourselves if desired.
  1037. if( sourceObj->getContain() && sourceObj->getContain()->isValidContainerFor(obj, TRUE))
  1038. {
  1039. sourceObj->getContain()->addToContain( obj );
  1040. // Need to hide if they are hidden.
  1041. if( sourceObj->getDrawable() && obj->getDrawable() && sourceObj->getDrawable()->isDrawableEffectivelyHidden() )
  1042. obj->getDrawable()->setDrawableHidden( TRUE );
  1043. }
  1044. else
  1045. {
  1046. DEBUG_ASSERTCRASH(FALSE,("A OCL with ContainInsideSourceObject failed the contain and is killing the new object."));
  1047. // If we fail to contain it, we can't just leave it. Stillborn it.
  1048. TheGameLogic->destroyObject(obj);
  1049. }
  1050. }
  1051. }
  1052. void reallyCreate(const Coord3D *pos, const Matrix3D *mtx, Real orientation, const Object *sourceObj, UnsignedInt lifetimeFrames ) const
  1053. {
  1054. static const ThingTemplate* debrisTemplate = TheThingFactory->findTemplate("GenericDebris");
  1055. if (m_names.size() <= 0)
  1056. return;
  1057. if (m_requiresLivePlayer && (!sourceObj || !sourceObj->getControllingPlayer()->isPlayerActive()))
  1058. return; // don't spawn useful objects for dead players. Avoid the zombie units from Yuri's.
  1059. // Object type debris might need this information to process visual UpgradeModules.
  1060. Team *debrisOwner = NULL;
  1061. if( sourceObj )
  1062. debrisOwner = sourceObj->getControllingPlayer()->getDefaultTeam();
  1063. Object* container = NULL;
  1064. if (!m_putInContainer.isEmpty())
  1065. {
  1066. const ThingTemplate* containerTmpl = TheThingFactory->findTemplate(m_putInContainer);
  1067. if (containerTmpl)
  1068. {
  1069. container = TheThingFactory->newObject( containerTmpl, debrisOwner );
  1070. container->setProducer(sourceObj);
  1071. }
  1072. }
  1073. for (Int nn = 0; nn < m_debrisToGenerate; nn++)
  1074. {
  1075. Int pick = GameLogicRandomValue(0, m_names.size() - 1);
  1076. const ThingTemplate* tmpl;
  1077. if (m_nameAreObjects)
  1078. tmpl = TheThingFactory->findTemplate(m_names[pick]);
  1079. else
  1080. { //this is using the generic debris type so it's probably safe to
  1081. //remove if requested by the GameLOD manager.
  1082. if (TheGameLODManager->isDebrisSkipped())
  1083. continue;
  1084. tmpl = debrisTemplate;
  1085. }
  1086. DEBUG_ASSERTCRASH(tmpl, ("Object %s not found\n",m_names[pick].str()));
  1087. if (!tmpl)
  1088. continue;
  1089. Object *debris = TheThingFactory->newObject( tmpl, debrisOwner );
  1090. debris->setProducer(sourceObj);
  1091. if (m_preserveLayer && sourceObj != NULL && container == NULL)
  1092. {
  1093. PathfindLayerEnum layer = sourceObj->getLayer();
  1094. if (layer != LAYER_GROUND)
  1095. debris->setLayer(layer);
  1096. }
  1097. if (container != NULL && container->getContain() != NULL && container->getContain()->isValidContainerFor(debris, true))
  1098. container->getContain()->addToContain(debris);
  1099. // if we want the objects being created to appear in a spread formation
  1100. // PLEASE NOTE --> if/when the object placement logic is modified so that
  1101. // objects that are placed in the same location are no longer placed in a
  1102. // diagonal line but rather in random locations nearby, this logic will no
  1103. // longer be necessary and can be taken out -- amit
  1104. if (m_spreadFormation)
  1105. {
  1106. Coord3D resultPos;
  1107. FindPositionOptions fpOptions;
  1108. fpOptions.minRadius = GameLogicRandomValueReal(m_minDistanceAFormation, m_minDistanceBFormation);
  1109. fpOptions.maxRadius = m_maxDistanceFormation;
  1110. fpOptions.flags = FPF_USE_HIGHEST_LAYER;
  1111. ThePartitionManager->findPositionAround(pos, &fpOptions, &resultPos);
  1112. doStuffToObj( debris, m_names[pick], &resultPos, mtx, orientation, sourceObj, lifetimeFrames );
  1113. }
  1114. else
  1115. {
  1116. // do stuff to contained objects too
  1117. doStuffToObj( debris, m_names[pick], pos, mtx, orientation, sourceObj, lifetimeFrames );
  1118. }
  1119. if (m_fadeIn)
  1120. {
  1121. AudioEventRTS fadeAudioEvent(m_fadeSoundName);
  1122. fadeAudioEvent.setObjectID(sourceObj->getID());
  1123. TheAudio->addAudioEvent(&fadeAudioEvent);
  1124. debris->getDrawable()->fadeIn(m_fadeFrames);
  1125. }
  1126. if (m_fadeOut)
  1127. {
  1128. AudioEventRTS fadeAudioEvent(m_fadeSoundName);
  1129. fadeAudioEvent.setObjectID(sourceObj->getID());
  1130. TheAudio->addAudioEvent(&fadeAudioEvent);
  1131. debris->getDrawable()->fadeOut(m_fadeFrames);
  1132. }
  1133. }
  1134. if (container)
  1135. doStuffToObj( container, AsciiString::TheEmptyString, pos, mtx, orientation, sourceObj, lifetimeFrames );
  1136. }
  1137. static void parseDebrisObjectNames( INI* ini, void *instance, void *store, const void* /*userData*/ )
  1138. {
  1139. GenericObjectCreationNugget* debrisNugget = (GenericObjectCreationNugget*)instance;
  1140. for (const char* debrisName = ini->getNextToken(); debrisName; debrisName = ini->getNextTokenOrNull())
  1141. {
  1142. if (TheGlobalData->m_preloadAssets)
  1143. debrisModelNamesGlobalHack.push_back(debrisName);
  1144. debrisNugget->m_names.push_back(AsciiString(debrisName));
  1145. debrisName = ini->getNextTokenOrNull();
  1146. }
  1147. }
  1148. private:
  1149. struct AnimSet
  1150. {
  1151. AsciiString m_animInitial;
  1152. AsciiString m_animFlying;
  1153. AsciiString m_animFinal;
  1154. };
  1155. std::vector<AsciiString> m_names;
  1156. AsciiString m_putInContainer;
  1157. std::vector<AnimSet> m_animSets;
  1158. const FXList* m_fxFinal;
  1159. AsciiString m_particleSysName;
  1160. Int m_debrisToGenerate;
  1161. Real m_mass;
  1162. Real m_extraBounciness;
  1163. Real m_extraFriction;
  1164. Coord3D m_offset;
  1165. DebrisDisposition m_disposition;
  1166. Real m_dispositionIntensity;
  1167. Real m_spinRate;
  1168. Real m_yawRate;
  1169. Real m_rollRate;
  1170. Real m_pitchRate;
  1171. Real m_minMag, m_maxMag;
  1172. Real m_minPitch, m_maxPitch;
  1173. UnsignedInt m_minFrames, m_maxFrames;
  1174. ShadowType m_shadowType;
  1175. StaticGameLODLevel m_minLODRequired;
  1176. UnsignedInt m_invulnerableTime;
  1177. Real m_minHealth;
  1178. Real m_maxHealth;
  1179. UnsignedInt m_fadeFrames;
  1180. AsciiString m_fadeSoundName;
  1181. Real m_minDistanceAFormation;
  1182. Real m_minDistanceBFormation;
  1183. Real m_maxDistanceFormation;
  1184. Int m_objectCount; // how many objects will there be?
  1185. AudioEventRTS m_bounceSound;
  1186. Bool m_requiresLivePlayer;
  1187. Bool m_containInsideSourceObject; ///< The created stuff will be added to the Conatin module of the SourceObject
  1188. Bool m_preserveLayer;
  1189. Bool m_nameAreObjects;
  1190. Bool m_okToChangeModelColor;
  1191. Bool m_orientInForceDirection;
  1192. Bool m_spreadFormation;
  1193. Bool m_fadeIn;
  1194. Bool m_fadeOut;
  1195. Bool m_ignorePrimaryObstacle;
  1196. Bool m_inheritsVeterancy;
  1197. Bool m_skipIfSignificantlyAirborne;
  1198. };
  1199. EMPTY_DTOR(GenericObjectCreationNugget)
  1200. //-------------------------------------------------------------------------------------------------
  1201. //-------------------------------------------------------------------------------------------------
  1202. //-------------------------------------------------------------------------------------------------
  1203. //-------------------------------------------------------------------------------------------------
  1204. //-------------------------------------------------------------------------------------------------
  1205. static const FieldParse TheObjectCreationListFieldParse[] =
  1206. {
  1207. { "CreateObject", GenericObjectCreationNugget::parseObject, 0, 0},
  1208. { "CreateDebris", GenericObjectCreationNugget::parseDebris, 0, 0},
  1209. { "ApplyRandomForce", ApplyRandomForceNugget::parse, 0, 0},
  1210. { "DeliverPayload", DeliverPayloadNugget::parse, 0, 0},
  1211. { "FireWeapon", FireWeaponNugget::parse, 0, 0},
  1212. { "Attack", AttackNugget::parse, 0, 0},
  1213. { NULL, NULL, 0, 0 } // keep this last
  1214. };
  1215. //-------------------------------------------------------------------------------------------------
  1216. void ObjectCreationList::clear()
  1217. {
  1218. // do NOT delete the nuggets -- they're owned by the Store.
  1219. m_nuggets.clear();
  1220. }
  1221. //-------------------------------------------------------------------------------------------------
  1222. void ObjectCreationList::addObjectCreationNugget(ObjectCreationNugget* nugget)
  1223. {
  1224. m_nuggets.push_back(nugget);
  1225. TheObjectCreationListStore->addObjectCreationNugget(nugget);
  1226. }
  1227. //-------------------------------------------------------------------------------------------------
  1228. void ObjectCreationList::create( const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, Bool createOwner, UnsignedInt lifetimeFrames ) const
  1229. {
  1230. DEBUG_ASSERTCRASH(primaryObj != NULL, ("You should always call OCLs with a non-null primary Obj, even for positional calls, to get team ownership right"));
  1231. for (ObjectCreationNuggetVector::const_iterator i = m_nuggets.begin(); i != m_nuggets.end(); ++i)
  1232. {
  1233. (*i)->create( primaryObj, primary, secondary, createOwner, lifetimeFrames );
  1234. }
  1235. }
  1236. //-------------------------------------------------------------------------------------------------
  1237. void ObjectCreationList::create( const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, UnsignedInt lifetimeFrames ) const
  1238. {
  1239. DEBUG_ASSERTCRASH(primaryObj != NULL, ("You should always call OCLs with a non-null primary Obj, even for positional calls, to get team ownership right"));
  1240. for (ObjectCreationNuggetVector::const_iterator i = m_nuggets.begin(); i != m_nuggets.end(); ++i)
  1241. {
  1242. (*i)->create( primaryObj, primary, secondary, lifetimeFrames );
  1243. }
  1244. }
  1245. //-------------------------------------------------------------------------------------------------
  1246. void ObjectCreationList::create( const Object* primary, const Object* secondary, UnsignedInt lifetimeFrames ) const
  1247. {
  1248. DEBUG_ASSERTCRASH(primary != NULL, ("You should always call OCLs with a non-null primary Obj, even for positional calls, to get team ownership right"));
  1249. for (ObjectCreationNuggetVector::const_iterator i = m_nuggets.begin(); i != m_nuggets.end(); ++i)
  1250. {
  1251. (*i)->create( primary, secondary, lifetimeFrames );
  1252. }
  1253. }
  1254. //-------------------------------------------------------------------------------------------------
  1255. //-------------------------------------------------------------------------------------------------
  1256. //-------------------------------------------------------------------------------------------------
  1257. //-------------------------------------------------------------------------------------------------
  1258. ObjectCreationListStore::ObjectCreationListStore()
  1259. {
  1260. }
  1261. //-------------------------------------------------------------------------------------------------
  1262. ObjectCreationListStore::~ObjectCreationListStore()
  1263. {
  1264. for (ObjectCreationNuggetVector::iterator i = m_nuggets.begin(); i != m_nuggets.end(); ++i)
  1265. {
  1266. if (*i)
  1267. (*i)->deleteInstance();
  1268. }
  1269. m_nuggets.clear();
  1270. }
  1271. //-------------------------------------------------------------------------------------------------
  1272. const ObjectCreationList *ObjectCreationListStore::findObjectCreationList(const char* name) const
  1273. {
  1274. if (stricmp(name, "None") == 0)
  1275. return NULL;
  1276. ObjectCreationListMap::const_iterator it = m_ocls.find(NAMEKEY(name));
  1277. if (it == m_ocls.end())
  1278. {
  1279. return NULL;
  1280. }
  1281. else
  1282. {
  1283. return &(*it).second;
  1284. }
  1285. }
  1286. //-------------------------------------------------------------------------------------------------
  1287. void ObjectCreationListStore::addObjectCreationNugget(ObjectCreationNugget* nugget)
  1288. {
  1289. m_nuggets.push_back(nugget);
  1290. }
  1291. //-------------------------------------------------------------------------------------------------
  1292. /*static */ void ObjectCreationListStore::parseObjectCreationListDefinition(INI *ini)
  1293. {
  1294. // read the ObjectCreationList name
  1295. const char *c = ini->getNextToken();
  1296. NameKeyType key = TheNameKeyGenerator->nameToKey(c);
  1297. ObjectCreationList& ocl = TheObjectCreationListStore->m_ocls[key];
  1298. ocl.clear();
  1299. ini->initFromINI(&ocl, TheObjectCreationListFieldParse);
  1300. }
  1301. //-------------------------------------------------------------------------------------------------
  1302. /*static*/ void INI::parseObjectCreationListDefinition(INI *ini)
  1303. {
  1304. ObjectCreationListStore::parseObjectCreationListDefinition(ini);
  1305. }