ObjectCreationList.cpp 59 KB

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