| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: ObjectCreationList.cpp ///////////////////////////////////////////////////////////////////////////////
- // Author: Steven Johnson, December 2001
- // Desc: ObjectCreationList descriptions
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #define DEFINE_SHADOW_NAMES // for TheShadowNames[]
- #define DEFINE_WEAPONSLOTTYPE_NAMES
- #define NO_DEBUG_CRC
- #include "Common/AudioEventRTS.h"
- #include "Common/DrawModule.h"
- #include "Common/GlobalData.h"
- #include "Common/INI.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/ThingTemplate.h"
- #include "Common/ThingFactory.h"
- #include "Common/GameLOD.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/FXList.h"
- #include "GameClient/ParticleSys.h"
- #include "GameClient/Shadow.h"
- #include "GameLogic/ExperienceTracker.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/ContainModule.h"
- #include "GameLogic/Module/DeliverPayloadAIUpdate.h"
- #include "GameLogic/Module/FloatUpdate.h"
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/Module/SpecialPowerCompletionDie.h"
- #include "GameLogic/Module/LifetimeUpdate.h"
- #include "GameLogic/Module/RadiusDecalUpdate.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/ObjectCreationList.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/ScriptEngine.h"
- #include "GameLogic/Weapon.h"
- #include "GameLogic/WeaponSet.h"
- #include "Common/CRCDebug.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ObjectCreationListStore *TheObjectCreationListStore = NULL; ///< the ObjectCreationList store definition
- //-------------------------------------------------------------------------------------------------
- static void adjustVector(Coord3D *vec, const Matrix3D* mtx)
- {
- if (mtx)
- {
- Vector3 vectmp;
- vectmp.X = vec->x;
- vectmp.Y = vec->y;
- vectmp.Z = vec->z;
- vectmp = mtx->Rotate_Vector(vectmp);
- vec->x = vectmp.X;
- vec->y = vectmp.Y;
- vec->z = vectmp.Z;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // PRIVATE CLASSES ///////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- void ObjectCreationNugget::create( const Object* primary, const Object* secondary, UnsignedInt lifetimeFrames ) const
- {
- create( primary, primary ? primary->getPosition() : NULL, secondary ? secondary->getPosition() : NULL, lifetimeFrames );
- }
- //-------------------------------------------------------------------------------------------------
- //This one is called only when we have a nugget that doesn't care about createOwner.
- void ObjectCreationNugget::create( const Object *primaryObj, const Coord3D *primary, const Coord3D *secondary, Bool createOwner, UnsignedInt lifetimeFrames ) const
- {
- create( primaryObj, primary, secondary, lifetimeFrames );
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- class FireWeaponNugget : public ObjectCreationNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(FireWeaponNugget, "FireWeaponNugget")
-
- public:
- FireWeaponNugget() :
- m_weapon(NULL)
- {
- }
- virtual void create( const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, UnsignedInt lifetimeFrames = 0 ) const
- {
- if (!primaryObj || !primary || !secondary)
- {
- DEBUG_CRASH(("You must have a primary and secondary source for this effect"));
- return;
- }
- if (m_weapon)
- {
- TheWeaponStore->createAndFireTempWeapon( m_weapon, primaryObj, secondary );
- }
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "Weapon", INI::parseWeaponTemplate, NULL, offsetof( FireWeaponNugget, m_weapon ) },
- { 0, 0, 0, 0 }
- };
- FireWeaponNugget* nugget = newInstance(FireWeaponNugget);
- ini->initFromINI(nugget, myFieldParse);
- ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
- }
- private:
- const WeaponTemplate* m_weapon;
- };
- EMPTY_DTOR(FireWeaponNugget)
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- class AttackNugget : public ObjectCreationNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(AttackNugget, "AttackNugget")
- public:
- AttackNugget() :
- m_numberOfShots(1),
- m_weaponSlot(PRIMARY_WEAPON),
- m_deliveryDecalRadius(0)
- {
- }
- virtual void create( const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, UnsignedInt lifetimeFrames = 0 ) const
- {
- if (!primaryObj || !primary || !secondary)
- {
- DEBUG_CRASH(("You must have a primary and secondary source for this effect"));
- return;
- }
- // Star trekkin, across the universe.
- // Boldly goin forward now, cause we can't find reverse!
- // 1:30 left on the clock, Demo looming, should I de-const all of OCL since this one effect needs the
- // Primary to help make the objects? Should I rewrite superweapons to completely subsume them
- // into normal special powers, so I can have a spell attack with a global timer and a spinny clock?
- // Should I const cast? Should I eat them? No! I'd rather hurt them stomp them crush them hurt them
- // stomp them while I dance! Down. Those insects make me dance! Down. I'll hurt them while I dance.
- Object *primaryObject = const_cast<Object *>(primaryObj);
- AIUpdateInterface *ai = primaryObject->getAIUpdateInterface();
- if( ai )
- {
- // lock merely fires till the weapon is empty or the attack is "done"
- primaryObject->setWeaponLock( m_weaponSlot, LOCKED_TEMPORARILY );
- ai->aiAttackPosition( secondary, m_numberOfShots, CMD_FROM_AI );
- }
- static NameKeyType key_RadiusDecalUpdate = NAMEKEY("RadiusDecalUpdate");
- RadiusDecalUpdate *rd = (RadiusDecalUpdate*)primaryObject->findUpdateModule(key_RadiusDecalUpdate);
- if (rd)
- {
- rd->createRadiusDecal(m_deliveryDecalTemplate, m_deliveryDecalRadius, *secondary);
- rd->killWhenNoLongerAttacking(true);
- }
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "NumberOfShots", INI::parseInt, NULL, offsetof( AttackNugget, m_numberOfShots ) },
- { "WeaponSlot", INI::parseLookupList, TheWeaponSlotTypeNamesLookupList, offsetof( AttackNugget, m_weaponSlot ) },
- { "DeliveryDecal", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( AttackNugget, m_deliveryDecalTemplate ) },
- { "DeliveryDecalRadius", INI::parseReal, NULL, offsetof(AttackNugget, m_deliveryDecalRadius) },
- { 0, 0, 0, 0 }
- };
- AttackNugget* nugget = newInstance(AttackNugget);
- ini->initFromINI(nugget, myFieldParse);
- ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
- }
- private:
- RadiusDecalTemplate m_deliveryDecalTemplate;
- Real m_deliveryDecalRadius;
- Int m_numberOfShots;
- WeaponSlotType m_weaponSlot;
- };
- EMPTY_DTOR(AttackNugget)
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- class DeliverPayloadNugget : public ObjectCreationNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(DeliverPayloadNugget, "DeliverPayloadNugget")
- public:
- DeliverPayloadNugget() :
- m_startAtPreferredHeight(true),
- m_startAtMaxSpeed(false),
- m_formationSize(1),
- m_formationSpacing(25.0f),
- m_errorRadius(0.0f),
- m_delayDeliveryFramesMax(0),
- m_convergenceFactor( 0.0f )
- {
- //Note: m_data is constructed with default values.
-
- // Added by Sadullah Nader
- // Initialization missing and needed
-
- m_payload.clear();
- m_putInContainerName.clear();
- m_transportName.clear();
- // End Add
- }
- virtual void create(const Object *primaryObj, const Coord3D *primary, const Coord3D *secondary, UnsignedInt lifetimeFrames = 0 ) const
- {
- create( primaryObj, primary, secondary, true, lifetimeFrames );
- }
- virtual void create(const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, Bool createOwner, UnsignedInt lifetimeFrames = 0 ) const
- {
- if (!primaryObj || !primary || !secondary)
- {
- DEBUG_CRASH(("You must have a primary and secondary source for this effect"));
- return;
- }
- Team* owner = primaryObj ? primaryObj->getControllingPlayer()->getDefaultTeam() : NULL;
- //What I'm doing for the purposes of the formations is to calculate the relative positions of
- //each member of the formation. To do so, we take the vector from the target location to the
- //lead plane location, normalize it, then rotate it 90 degrees (CW and CCW). When we add the
- //resultant vectors to the initial vectors, we can calculate the delta positions for each plane.
- Real CCWx = 0.0f, CCWy = 0.0f, CWx = 0.0f, CWy = 0.0f;
- if( m_formationSize > 1 )
- {
- //Get the delta x and y values from the target to the origin.
- Real dx = primary->x - secondary->x;
- Real dy = primary->y - secondary->y;
-
- //Calc length
- Real length = sqrt( dx*dx + dy*dy );
- //Normalize length
- dx /= length;
- dy /= length;
- //Rotate 90 degrees CCW.
- Real radians = 90.0f * PI / 180.0f;
- Real s = Sin( radians );
- Real c = Cos( radians );
- CCWx = dx * c + dy * -s + dx;
- CCWy = dx * s + dy * c + dy;
- //Rotate 90 degrees CW
- s = Sin( -radians );
- c = Cos( -radians );
- CWx = dx * c + dy * -s + dx;
- CWy = dx * s + dy * c + dy;
- }
-
- for( Int formationIndex = 0; formationIndex < m_formationSize; formationIndex++ )
- {
- Coord3D offset;
- offset.zero();
- Int offsetMultiplier = ( formationIndex + 1 ) / 2 * m_formationSpacing;
- if( formationIndex % 2 )
- {
- //Formation index is odd -- use the CCW deltas
- offset.x = CCWx * offsetMultiplier;
- offset.y = CCWy * offsetMultiplier;
- }
- else
- {
- //Formation index is even -- use the CW deltas
- offset.x = CWx * offsetMultiplier;
- offset.y = CWy * offsetMultiplier;
- }
- Coord3D startPos = *primary;
- Coord3D moveToPos = *secondary;
- startPos.add( &offset );
- //Also give our moveToPos the same offset to maintain perfect formation.
- moveToPos.add( &offset );
- Coord3D targetPos = *secondary;
-
- //Our target position only applies when using fireweapon and when we have multiple planes,
- //as is the case with the napalm strike. The target position either be somewhere between the
- //moveToPos of the lead plane and that of the relative offset -- determined by the convergenceFactor.
- targetPos.x += offset.x * (1.0f - m_convergenceFactor);
- targetPos.y += offset.y * (1.0f - m_convergenceFactor);
- // first guy in each formation is always spot-on (to keep targeting cursor well-matched)
- if ( m_errorRadius > 1.0f && formationIndex > 0 )
- {
- Real randomRadius = GameLogicRandomValueReal(0, m_errorRadius );
- Real randomAngle = GameLogicRandomValueReal(0, PI*2 );
- targetPos.x += randomRadius * Cos( randomAngle );
- targetPos.y += randomRadius * Sin( randomAngle );
- }
- Real orient = atan2( moveToPos.y - startPos.y, moveToPos.x - startPos.x);
- if( m_data.m_distToTarget > 0 )
- {
- const Real SLOP = 1.5f;
- startPos.x -= Cos(orient) * m_data.m_distToTarget * SLOP;
- startPos.y -= Sin(orient) * m_data.m_distToTarget * SLOP;
- }
- Object *transport;
- if( createOwner )
- {
- const ThingTemplate* ttn = TheThingFactory->findTemplate(m_transportName);
- transport = TheThingFactory->newObject( ttn, owner );
- if( !transport )
- {
- return;
- }
- transport->setPosition(&startPos);
- transport->setOrientation(orient);
- transport->setProducer(primaryObj);
- //Adding this nifty flag allows enemy players to target it manually with weapons :)
- transport->setScriptStatus( OBJECT_STATUS_SCRIPT_TARGETABLE );
- if ( m_delayDeliveryFramesMax > 0 )
- {
- transport->setDisabledUntil( DISABLED_DEFAULT, TheGameLogic->getFrame() + GameLogicRandomValue(0, m_delayDeliveryFramesMax) );
- }
- }
- else
- {
- transport = (Object*)primaryObj;
- }
- // Notify special power tracking
- SpecialPowerCompletionDie *die = transport->findSpecialPowerCompletionDie();
- if (die)
- {
- if (formationIndex == 0)
- die->setCreator(primaryObj->getID());
- else
- die->setCreator(INVALID_ID);
- }
- static NameKeyType key_DeliverPayloadAIUpdate = NAMEKEY("DeliverPayloadAIUpdate");
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)transport->findUpdateModule(key_DeliverPayloadAIUpdate);
- if( ai )
- {
- if( m_startAtMaxSpeed && createOwner )
- {
- PhysicsBehavior* physics = transport->getPhysics();
- if (physics)
- {
- Coord3D startingForce = *transport->getUnitDirectionVector2D();
- Real maxSpeed = ai->getCurLocomotor()->getMaxSpeedForCondition(transport->getBodyModule()->getDamageState());
- Real factor = maxSpeed * physics->getMass();
- startingForce.x *= factor;
- startingForce.y *= factor;
- startingForce.z *= factor;
- physics->applyMotiveForce( &startingForce );
- }
- }
-
- // only the first guy in each formation gets a delivery decal
- DeliverPayloadData data = m_data;
- if (formationIndex != 0)
- data.m_deliveryDecalRadius = 0;
- ai->deliverPayload( &moveToPos, &targetPos, &data );
- if( m_startAtPreferredHeight && createOwner )
- {
- startPos.z = TheTerrainLogic->getGroundHeight(startPos.x, startPos.y) + ai->getCurLocomotor()->getPreferredHeight();
- transport->setPosition(&startPos);
- }
- const ThingTemplate* putInContainerTmpl = m_putInContainerName.isEmpty() ? NULL : TheThingFactory->findTemplate(m_putInContainerName);
- for (std::vector<Payload>::const_iterator it = m_payload.begin(); it != m_payload.end(); ++it)
- {
- const ThingTemplate* payloadTmpl = TheThingFactory->findTemplate(it->m_payloadName);
- for (int i = 0; i < it->m_payloadCount; ++i)
- {
- Object* payload = TheThingFactory->newObject( payloadTmpl, owner );
- payload->setPosition(&startPos);
- payload->setProducer(transport);
- // Notify special power tracking
- SpecialPowerCompletionDie *die = payload->findSpecialPowerCompletionDie();
- if (die)
- {
- if (formationIndex == 0 && i == 0)
- die->setCreator(primaryObj->getID());
- else
- die->setCreator(INVALID_ID);
- }
- if (putInContainerTmpl)
- {
- Object* container = TheThingFactory->newObject( putInContainerTmpl, owner );
- container->setPosition(&startPos);
- container->setProducer(transport);
- // Notify special power tracking
- SpecialPowerCompletionDie *die = container->findSpecialPowerCompletionDie();
- if (die)
- {
- if (formationIndex == 0 && i == 0)
- die->setCreator(primaryObj->getID());
- else
- die->setCreator(INVALID_ID);
- }
- if (container->getContain() && container->getContain()->isValidContainerFor(payload, true))
- {
- container->getContain()->addToContain(payload);
- payload = container;
- }
- else
- {
- DEBUG_CRASH(("DeliverPayload: PutInContainer %s is full, or not valid for the payload %s!",m_putInContainerName.str(),it->m_payloadName.str()));
- }
- }
- if (transport->getContain() && transport->getContain()->isValidContainerFor(payload, true))
- {
- transport->getContain()->addToContain(payload);
- }
- else
- {
- DEBUG_CRASH(("DeliverPayload: transport %s is full, or not valid for the payload %s!",m_transportName.str(),it->m_payloadName.str()));
- }
- }
- }
- }
- else
- {
- DEBUG_CRASH(("You should really have a DeliverPayloadAIUpdate here"));
- }
- }
- }
- static void parsePayload( INI* ini, void *instance, void *store, const void* /*userData*/ )
- {
- DeliverPayloadNugget* self = (DeliverPayloadNugget*)instance;
- const char* name = ini->getNextToken();
- const char* countStr = ini->getNextTokenOrNull();
- Int count = countStr ? INI::scanInt(countStr) : 1;
-
- Payload p;
- p.m_payloadName.set(name);
- p.m_payloadCount = count;
- self->m_payload.push_back(p);
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- //***************************************************************
- //OBJECT CREATION LIST SPECIFIC DATA -- once created data no longer needed
- //The transport(s) that carry all the payload items (and initial physics information)
- { "Transport", INI::parseAsciiString, NULL, offsetof(DeliverPayloadNugget, m_transportName) },
- { "StartAtPreferredHeight", INI::parseBool, NULL, offsetof(DeliverPayloadNugget, m_startAtPreferredHeight) },
- { "StartAtMaxSpeed", INI::parseBool, NULL, offsetof(DeliverPayloadNugget, m_startAtMaxSpeed) },
- //For multiple transports, this defines the formation (and convergence if all weapons will hit same target)
- { "FormationSize", INI::parseUnsignedInt, NULL, offsetof( DeliverPayloadNugget, m_formationSize) },
- { "FormationSpacing", INI::parseReal, NULL, offsetof( DeliverPayloadNugget, m_formationSpacing) },
- { "WeaponConvergenceFactor", INI::parseReal, NULL, offsetof( DeliverPayloadNugget, m_convergenceFactor ) },
- { "WeaponErrorRadius", INI::parseReal, NULL, offsetof( DeliverPayloadNugget, m_errorRadius ) },
- { "DelayDeliveryMax", INI::parseDurationUnsignedInt, NULL, offsetof( DeliverPayloadNugget, m_delayDeliveryFramesMax ) },
- //Payload information (it's all created now and stored inside)
- { "Payload", parsePayload, NULL, 0 },
- { "PutInContainer", INI::parseAsciiString, NULL, offsetof( DeliverPayloadNugget, m_putInContainerName) },
- //END OBJECT CREATION LIST SPECIFIC DATA
- //***************************************************************
-
- //***************************************************************
- //DELIVERPAYLOADDATA contains the rest (and most) of the parsed data.
- //***************************************************************
- { 0, 0, 0, 0 }
- };
- DeliverPayloadNugget* nugget = newInstance(DeliverPayloadNugget);
- MultiIniFieldParse p;
- p.add(myFieldParse);
- p.add(DeliverPayloadData::getFieldParse(), offsetof( DeliverPayloadNugget, m_data ));
- ini->initFromINIMulti(nugget, p);
- ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
- }
- private:
- struct Payload
- {
- AsciiString m_payloadName;
- Int m_payloadCount;
- };
- //Specific data needed to create the transport(s), internal payload, and initial physics.
- AsciiString m_transportName;
- AsciiString m_putInContainerName;
- std::vector<Payload> m_payload;
- Real m_formationSpacing;
- Real m_convergenceFactor;
- Real m_errorRadius;
- UnsignedInt m_delayDeliveryFramesMax;
- UnsignedInt m_formationSize;
- Bool m_startAtPreferredHeight;
- Bool m_startAtMaxSpeed;
- //AI specific data passed over to DeliverPayloadAIUpdate::deliver()
- DeliverPayloadData m_data;
- };
- EMPTY_DTOR(DeliverPayloadNugget)
- //-------------------------------------------------------------------------------------------------
- static void calcRandomForce(Real minMag, Real maxMag, Real minPitch, Real maxPitch, Coord3D* force)
- {
- Real angle = GameLogicRandomValueReal(0, 2*PI);
- Real pitch = GameLogicRandomValueReal(minPitch, maxPitch);
- Real mag = GameLogicRandomValueReal(minMag, maxMag);
- Matrix3D mtx(1);
- mtx.Scale(mag);
- mtx.Rotate_Z(angle);
- mtx.Rotate_Y(-pitch);
- Vector3 v = mtx.Get_X_Vector();
- force->x = v.X;
- force->y = v.Y;
- force->z = v.Z;
- }
- //-------------------------------------------------------------------------------------------------
- class ApplyRandomForceNugget : public ObjectCreationNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ApplyRandomForceNugget, "ApplyRandomForceNugget")
- public:
- ApplyRandomForceNugget() :
- m_spinRate(0.0f),
- m_minMag(0.0f),
- m_maxMag(0.0f),
- m_minPitch(0.0f),
- m_maxPitch(0.0f)
- {
- }
- virtual void create( const Object* primary, const Object* secondary, UnsignedInt lifetimeFrames = 0 ) const
- {
- if (primary)
- {
- /// @todo srj -- ack. const_cast is evil.
- PhysicsBehavior* p = const_cast<Object*>(primary)->getPhysics();
- if (p)
- {
- Coord3D force;
- calcRandomForce(m_minMag, m_maxMag, m_minPitch, m_maxPitch, &force);
- p->applyForce(&force);
- Real yaw = GameLogicRandomValueReal( -m_spinRate, m_spinRate );
- Real roll = GameLogicRandomValueReal( -m_spinRate, m_spinRate );
- Real pitch = GameLogicRandomValueReal( -m_spinRate, m_spinRate );
- p->setYawRate(yaw);
- p->setRollRate(roll);
- p->setPitchRate(pitch);
- }
- else
- {
- DEBUG_CRASH(("You must have a Physics module source for this effect"));
- }
- }
- else
- {
- DEBUG_CRASH(("You must have a primary source for this effect"));
- }
- }
- virtual void create(const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, UnsignedInt lifetimeFrames = 0 ) const
- {
- DEBUG_CRASH(("You must call this effect with an object, not a location"));
- }
- static void parse(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "SpinRate", INI::parseAngularVelocityReal, NULL, offsetof(ApplyRandomForceNugget, m_spinRate) },
- { "MinForceMagnitude", INI::parseReal, NULL, offsetof(ApplyRandomForceNugget, m_minMag) },
- { "MaxForceMagnitude", INI::parseReal, NULL, offsetof(ApplyRandomForceNugget, m_maxMag) },
- { "MinForcePitch", INI::parseAngleReal, NULL, offsetof(ApplyRandomForceNugget, m_minPitch) },
- { "MaxForcePitch", INI::parseAngleReal, NULL, offsetof(ApplyRandomForceNugget, m_maxPitch) },
- { 0, 0, 0, 0 }
- };
- ApplyRandomForceNugget* nugget = newInstance(ApplyRandomForceNugget);
- ini->initFromINI(nugget, myFieldParse);
- ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
- }
- protected:
- private:
- Real m_spinRate;
- Real m_minMag, m_maxMag;
- Real m_minPitch, m_maxPitch;
- };
- EMPTY_DTOR(ApplyRandomForceNugget)
- //-------------------------------------------------------------------------------------------------
- enum DebrisDisposition
- {
- LIKE_EXISTING = 0x00000001,
- ON_GROUND_ALIGNED = 0x00000002,
- SEND_IT_FLYING = 0x00000004,
- SEND_IT_UP = 0x00000008,
- SEND_IT_OUT = 0x00000010,
- RANDOM_FORCE = 0x00000020,
- FLOATING = 0x00000040,
- INHERIT_VELOCITY = 0x00000080,
- WHIRLING = 0x00000100
- };
- static const char* DebrisDispositionNames[] =
- {
- "LIKE_EXISTING",
- "ON_GROUND_ALIGNED",
- "SEND_IT_FLYING",
- "SEND_IT_UP",
- "SEND_IT_OUT",
- "RANDOM_FORCE",
- "FLOATING",
- "INHERIT_VELOCITY",
- "WHIRLING",
- };
- std::vector<AsciiString> debrisModelNamesGlobalHack;
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- static void parseFrictionPerSec( INI* ini, void * /*instance*/, void *store, const void* /*userData*/ )
- {
- Real fricPerSec = INI::scanReal(ini->getNextToken());
- Real fricPerFrame = fricPerSec * SECONDS_PER_LOGICFRAME_REAL;
- *(Real *)store = fricPerFrame;
- }
- //-------------------------------------------------------------------------------------------------
- class GenericObjectCreationNugget : public ObjectCreationNugget
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(GenericObjectCreationNugget, "GenericObjectCreationNugget")
- public:
- GenericObjectCreationNugget() :
- m_requiresLivePlayer(FALSE),
- m_debrisToGenerate(1),
- m_mass(0),
- m_extraBounciness(0),
- m_extraFriction(0),
- m_disposition(ON_GROUND_ALIGNED),
- m_dispositionIntensity(0.0f),
- m_spinRate(-1.0f),
- m_yawRate(-1.0f),
- m_rollRate(-1.0f),
- m_pitchRate(-1.0f),
- m_nameAreObjects(true),
- m_okToChangeModelColor(false),
- m_minLODRequired(STATIC_GAME_LOD_LOW),
- m_ignorePrimaryObstacle(false),
- m_inheritsVeterancy(false),
- m_skipIfSignificantlyAirborne(false),
- m_invulnerableTime(0),
- m_containInsideSourceObject(FALSE),
- m_minHealth(1.0f),
- m_maxHealth(1.0f),
- m_orientInForceDirection(false),
- m_spreadFormation(false),
- m_minDistanceAFormation(0.0f),
- m_minDistanceBFormation(0.0f),
- m_maxDistanceFormation(0.0f),
- m_fadeIn(false),
- m_fadeOut(false),
- m_fadeFrames(0),
- m_fadeSoundName(AsciiString::TheEmptyString), // Added By Sadullah Nader
- m_particleSysName(AsciiString::TheEmptyString), // Added By Sadullah Nader
- m_putInContainer(AsciiString::TheEmptyString), // Added By Sadullah Nader
- m_minMag(0.0f),
- m_maxMag(0.0f),
- m_minPitch(0.0f),
- m_maxPitch(0.0f),
- m_minFrames(0),
- m_maxFrames(0),
- m_shadowType(SHADOW_NONE),
- m_fxFinal(NULL),
- m_preserveLayer(true),
- m_objectCount(0) // Added By Sadullah Nader
- {
- // Change Made by Sadullah Nader
- // for init purposes, easier to read
- m_offset.zero();
- }
- virtual void create(const Object* primary, const Object* secondary, UnsignedInt lifetimeFrames = 0 ) const
- {
- if (primary)
- {
- if (m_skipIfSignificantlyAirborne && primary->isSignificantlyAboveTerrain())
- return;
- reallyCreate( primary->getPosition(), primary->getTransformMatrix(), primary->getOrientation(), primary, lifetimeFrames );
- }
- else
- {
- DEBUG_CRASH(("You must have a primary source for this effect"));
- }
- }
- virtual void create(const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, UnsignedInt lifetimeFrames = 0 ) const
- {
- if (primary)
- {
- const Matrix3D *xfrm = NULL;
- const Real orientation = 0.0;
- reallyCreate( primary, xfrm, orientation, primaryObj, lifetimeFrames );
- }
- else
- {
- DEBUG_CRASH(("You must have a primary source for this effect"));
- }
- }
- static const FieldParse* getCommonFieldParse()
- {
- static const FieldParse commonFieldParse[] =
- {
- { "PutInContainer", INI::parseAsciiString, NULL, offsetof( GenericObjectCreationNugget, m_putInContainer) },
- { "ParticleSystem", INI::parseAsciiString, NULL, offsetof( GenericObjectCreationNugget, m_particleSysName) },
- { "Count", INI::parseInt, NULL, offsetof( GenericObjectCreationNugget, m_debrisToGenerate ) },
- { "IgnorePrimaryObstacle", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_ignorePrimaryObstacle) },
- { "OrientInForceDirection", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_orientInForceDirection) },
- { "ExtraBounciness", INI::parseReal, NULL, offsetof( GenericObjectCreationNugget, m_extraBounciness ) },
- { "ExtraFriction", parseFrictionPerSec, NULL, offsetof( GenericObjectCreationNugget, m_extraFriction ) },
- { "Offset", INI::parseCoord3D, NULL, offsetof( GenericObjectCreationNugget, m_offset ) },
- { "Disposition", INI::parseBitString32, DebrisDispositionNames, offsetof( GenericObjectCreationNugget, m_disposition ) },
- { "DispositionIntensity", INI::parseReal, NULL, offsetof( GenericObjectCreationNugget, m_dispositionIntensity ) },
- { "SpinRate", INI::parseAngularVelocityReal, NULL, offsetof(GenericObjectCreationNugget, m_spinRate) },
- { "YawRate", INI::parseAngularVelocityReal, NULL, offsetof(GenericObjectCreationNugget, m_yawRate) },
- { "RollRate", INI::parseAngularVelocityReal, NULL, offsetof(GenericObjectCreationNugget, m_rollRate) },
- { "PitchRate", INI::parseAngularVelocityReal, NULL, offsetof(GenericObjectCreationNugget, m_pitchRate) },
- { "MinForceMagnitude", INI::parseReal, NULL, offsetof(GenericObjectCreationNugget, m_minMag) },
- { "MaxForceMagnitude", INI::parseReal, NULL, offsetof(GenericObjectCreationNugget, m_maxMag) },
- { "MinForcePitch", INI::parseAngleReal, NULL, offsetof(GenericObjectCreationNugget, m_minPitch) },
- { "MaxForcePitch", INI::parseAngleReal, NULL, offsetof(GenericObjectCreationNugget, m_maxPitch) },
- { "MinLifetime", INI::parseDurationUnsignedInt, NULL, offsetof( GenericObjectCreationNugget, m_minFrames ) },
- { "MaxLifetime", INI::parseDurationUnsignedInt, NULL, offsetof( GenericObjectCreationNugget, m_maxFrames ) },
- { "SpreadFormation", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_spreadFormation) },
- { "MinDistanceAFormation", INI::parseReal, NULL, offsetof(GenericObjectCreationNugget, m_minDistanceAFormation) },
- { "MinDistanceBFormation", INI::parseReal, NULL, offsetof(GenericObjectCreationNugget, m_minDistanceBFormation) },
- { "MaxDistanceFormation", INI::parseReal, NULL, offsetof(GenericObjectCreationNugget, m_maxDistanceFormation) },
- { "FadeIn", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_fadeIn) },
- { "FadeOut", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_fadeOut) },
- { "FadeTime", INI::parseDurationUnsignedInt, NULL, offsetof(GenericObjectCreationNugget, m_fadeFrames) },
- { "FadeSound", INI::parseAsciiString, NULL, offsetof( GenericObjectCreationNugget, m_fadeSoundName) },
- { "PreserveLayer", INI::parseBool, NULL, offsetof( GenericObjectCreationNugget, m_preserveLayer) },
- { 0, 0, 0, 0 }
- };
- return commonFieldParse;
- }
- static void parseObject(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "ContainInsideSourceObject", INI::parseBool, NULL, offsetof( GenericObjectCreationNugget, m_containInsideSourceObject) },
- { "ObjectNames", parseDebrisObjectNames, NULL, 0 },
- { "ObjectCount", INI::parseInt, NULL, offsetof(GenericObjectCreationNugget, m_objectCount) },
- { "InheritsVeterancy", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_inheritsVeterancy) },
- { "SkipIfSignificantlyAirborne", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_skipIfSignificantlyAirborne) },
- { "InvulnerableTime", INI::parseDurationUnsignedInt, NULL, offsetof(GenericObjectCreationNugget, m_invulnerableTime) },
- { "MinHealth", INI::parsePercentToReal, NULL, offsetof(GenericObjectCreationNugget, m_minHealth) },
- { "MaxHealth", INI::parsePercentToReal, NULL, offsetof(GenericObjectCreationNugget, m_maxHealth) },
- { "RequiresLivePlayer", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_requiresLivePlayer) },
- { 0, 0, 0, 0 }
- };
- MultiIniFieldParse p;
- p.add(getCommonFieldParse());
- p.add(myFieldParse);
- GenericObjectCreationNugget* nugget = newInstance(GenericObjectCreationNugget);
- nugget->m_nameAreObjects = true;
- ini->initFromINIMulti(nugget, p);
- ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
- }
- static void parseDebris(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
- {
- static const FieldParse myFieldParse[] =
- {
- { "ModelNames", parseDebrisObjectNames, NULL, 0 },
- { "Mass", INI::parsePositiveNonZeroReal, NULL, offsetof( GenericObjectCreationNugget, m_mass ) },
- { "AnimationSet", parseAnimSet, NULL, offsetof( GenericObjectCreationNugget, m_animSets) },
- { "FXFinal", INI::parseFXList, NULL, offsetof( GenericObjectCreationNugget, m_fxFinal) },
- { "OkToChangeModelColor", INI::parseBool, NULL, offsetof(GenericObjectCreationNugget, m_okToChangeModelColor) },
- { "MinLODRequired", INI::parseStaticGameLODLevel, NULL, offsetof(GenericObjectCreationNugget, m_minLODRequired) },
- { "Shadow", INI::parseBitString32, TheShadowNames, offsetof( GenericObjectCreationNugget, m_shadowType ) },
- { "BounceSound", INI::parseAudioEventRTS, NULL, offsetof( GenericObjectCreationNugget, m_bounceSound) },
- { 0, 0, 0, 0 }
- };
- MultiIniFieldParse p;
- p.add(getCommonFieldParse());
- p.add(myFieldParse);
- GenericObjectCreationNugget* nugget = newInstance(GenericObjectCreationNugget);
- nugget->m_nameAreObjects = false;
- ini->initFromINIMulti(nugget, p);
- DEBUG_ASSERTCRASH(nugget->m_mass > 0.0f, ("Zero masses are not allowed for debris!\n"));
- ((ObjectCreationList*)instance)->addObjectCreationNugget(nugget);
- }
- static void parseAnimSet(INI *ini, void * /*instance*/, void* store, const void* /*userData*/)
- {
- AnimSet anim;
- anim.m_animInitial = ini->getNextAsciiString();
- anim.m_animFlying = ini->getNextAsciiString();
- anim.m_animFinal = ini->getNextAsciiString();
- ((std::vector<AnimSet>*)store)->push_back(anim);
- }
- protected:
- void doStuffToObj(
- Object* obj,
- const AsciiString& modelName,
- const Coord3D *pos,
- const Matrix3D *mtx,
- Real orientation,
- const Object *sourceObj,
- UnsignedInt lifetimeFrames
- ) const
- {
- obj->setProducer(sourceObj);
- static NameKeyType key_LifetimeUpdate = NAMEKEY("LifetimeUpdate");
- LifetimeUpdate* lup = (LifetimeUpdate*)obj->findUpdateModule(key_LifetimeUpdate);
- if( lup )
- {
- if( lifetimeFrames )
- {
- //Passed in override, use this value for a specific lifetime!!!
- lup->setLifetimeRange( lifetimeFrames, lifetimeFrames );
- }
- else if( m_maxFrames > 0 )
- {
- // 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.
- // So the OCL listing will override the Object listing for lifetime, but ONLY if there is one.
- lup->setLifetimeRange(m_minFrames, m_maxFrames);
- }
- }
-
- if (!m_nameAreObjects)
- {
- for (DrawModule** dm = obj->getDrawable()->getDrawModules(); *dm; ++dm)
- {
- DebrisDrawInterface* di = (*dm)->getDebrisDrawInterface();
- if (di)
- {
- di->setModelName(modelName, m_okToChangeModelColor ? obj->getIndicatorColor() : 0, m_shadowType);
- if (m_animSets.size() > 0)
- {
- Int which = GameLogicRandomValue(0, m_animSets.size()-1);
- di->setAnimNames(m_animSets[which].m_animInitial, m_animSets[which].m_animFlying, m_animSets[which].m_animFinal, m_fxFinal);
- }
- }
- }
- }
- Coord3D offset = m_offset;
- if (mtx)
- adjustVector(&offset, mtx);
- Coord3D chunkPos;
- chunkPos.x = pos->x + offset.x;
- chunkPos.y = pos->y + offset.y;
- chunkPos.z = pos->z + offset.z;
-
- if (!m_particleSysName.isEmpty())
- {
- const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate(m_particleSysName);
- if (tmp)
- {
- ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(tmp);
- sys->attachToObject(obj);
- }
- }
-
- if (m_ignorePrimaryObstacle)
- {
- PhysicsBehavior* p = obj->getPhysics();
- if (p)
- p->setIgnoreCollisionsWith(sourceObj);
- }
- // set its beginning health
- BodyModuleInterface *body = obj->getBodyModule();
- Real healthPercent = GameLogicRandomValueReal( m_minHealth, m_maxHealth );
- if (body)
- body->setInitialHealth(healthPercent * 100.0f);
- // If they have a SlavedUpdate, then I have to tell them who their daddy is from now on.
- for (BehaviorModule** update = obj->getBehaviorModules(); *update; ++update)
- {
- SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface();
- if (sdu != NULL)
- {
- sdu->onEnslave( sourceObj );
- break;
- }
- }
- if (m_inheritsVeterancy && sourceObj && obj->getExperienceTracker()->isTrainable())
- {
- DEBUG_LOG(("Object %s inherits veterancy level %d from %s\n",
- obj->getTemplate()->getName().str(), sourceObj->getVeterancyLevel(), sourceObj->getTemplate()->getName().str()));
- VeterancyLevel v = sourceObj->getVeterancyLevel();
- obj->getExperienceTracker()->setVeterancyLevel(v);
- //In order to make things easier for the designers, we are going to transfer the unit name
- //to the ejected thing... so the designer can control the pilot with the scripts.
- TheScriptEngine->transferObjectName( sourceObj->getName(), obj );
- }
- if ( m_invulnerableTime > 0 )
- {
- obj->goInvulnerable( m_invulnerableTime );
- }
- if( BitTest( m_disposition, INHERIT_VELOCITY ) && sourceObj )
- {
- const PhysicsBehavior *sourcePhysics = sourceObj->getPhysics();
- PhysicsBehavior *objectPhysics = obj->getPhysics();
- if( sourcePhysics && objectPhysics )
- {
- objectPhysics->applyForce( sourcePhysics->getVelocity() );
- }
- }
- if( BitTest( m_disposition, LIKE_EXISTING ) )
- {
- if (mtx)
- obj->setTransformMatrix(mtx);
- else
- obj->setOrientation(orientation);
- obj->setPosition(&chunkPos);
- if (sourceObj && sourceObj->isAboveTerrain())
- {
- PhysicsBehavior* physics = obj->getPhysics();
- if (physics)
- physics->setAllowToFall(true);
- }
- }
- if( BitTest( m_disposition, ON_GROUND_ALIGNED ) )
- {
- chunkPos.z = 99999.0f;
- PathfindLayerEnum layer = TheTerrainLogic->getHighestLayerForDestination(&chunkPos);
- obj->setOrientation(GameLogicRandomValueReal(0.0f, 2 * PI));
- chunkPos.z = TheTerrainLogic->getLayerHeight( chunkPos.x, chunkPos.y, layer );
- // ensure we are slightly above the bridge, to account for fudge & sloppy art
- if (layer != LAYER_GROUND)
- chunkPos.z += 1.0f;
- obj->setLayer(layer);
- obj->setPosition(&chunkPos);
- }
- if( BitTest( m_disposition, SEND_IT_OUT ) )
- {
- obj->setOrientation(GameLogicRandomValueReal(0.0f, 2 * PI));
- chunkPos.z = TheTerrainLogic->getGroundHeight( chunkPos.x, chunkPos.y );
- obj->setPosition(&chunkPos);
- PhysicsBehavior* objUp = obj->getPhysics();
- if (objUp)
- {
- if (!m_nameAreObjects)
- objUp->setMass( m_mass );
- objUp->setExtraFriction(m_extraFriction);
- Coord3D force;
- Real horizForce = 4.0f * m_dispositionIntensity; // 2
- force.x = GameLogicRandomValueReal( -horizForce, horizForce );
- force.y = GameLogicRandomValueReal( -horizForce, horizForce );
- force.z = 0;
- objUp->applyForce(&force);
- if (m_orientInForceDirection)
- orientation = atan2(force.y, force.x);
- }
- }
- if( BitTest( m_disposition, SEND_IT_FLYING | SEND_IT_UP | RANDOM_FORCE ) )
- {
- if (mtx)
- {
- DUMPMATRIX3D(mtx);
- obj->setTransformMatrix(mtx);
- }
- obj->setPosition(&chunkPos);
- DUMPCOORD3D(&chunkPos);
- PhysicsBehavior* objUp = obj->getPhysics();
- if (objUp)
- {
- if (!m_nameAreObjects)
- {
- DUMPREAL(m_mass);
- objUp->setMass( m_mass );
- }
- DEBUG_ASSERTCRASH(objUp->getMass() > 0.0f, ("Zero masses are not allowed for obj!\n"));
- objUp->setExtraBounciness(m_extraBounciness);
- objUp->setExtraFriction(m_extraFriction);
- objUp->setAllowBouncing(true);
- objUp->setBounceSound(&m_bounceSound);
- DUMPREAL(m_extraBounciness);
- DUMPREAL(m_extraFriction);
-
- // if omitted from INI, calc it based on intensity.
- Real spinRate = m_spinRate >= 0.0f ? m_spinRate : (PI/32.0f) * m_dispositionIntensity;
- // Treat these as overrides.
- Real yawRate = m_yawRate >= 0.0f ? m_yawRate : spinRate;
- Real rollRate = m_rollRate >= 0.0f ? m_rollRate : spinRate;
- Real pitchRate = m_pitchRate >= 0.0f ? m_pitchRate : spinRate;
- DUMPREAL(spinRate);
- DUMPREAL(yawRate);
- DUMPREAL(rollRate);
- DUMPREAL(pitchRate);
-
- Real yaw = GameLogicRandomValueReal( -yawRate, yawRate );
- Real roll = GameLogicRandomValueReal( -rollRate, rollRate );
- Real pitch = GameLogicRandomValueReal( -pitchRate, pitchRate );
- DUMPREAL(yaw);
- DUMPREAL(roll);
- DUMPREAL(pitch);
- Coord3D force;
- if( BitTest( m_disposition, SEND_IT_FLYING ) )
- {
- Real horizForce = 4.0f * m_dispositionIntensity; // 2
- Real vertForce = 3.0f * m_dispositionIntensity; // 3
- force.x = GameLogicRandomValueReal( -horizForce, horizForce );
- force.y = GameLogicRandomValueReal( -horizForce, horizForce );
- force.z = GameLogicRandomValueReal( vertForce * 0.33f, vertForce );
- DUMPREAL(horizForce);
- DUMPREAL(vertForce);
- DUMPCOORD3D(&force);
- }
- else if (BitTest(m_disposition, SEND_IT_UP) )
- {
- Real horizForce = 2.0f * m_dispositionIntensity;
- Real vertForce = 4.0f * m_dispositionIntensity;
-
- force.x = GameLogicRandomValueReal( -horizForce, horizForce );
- force.y = GameLogicRandomValueReal( -horizForce, horizForce );
- force.z = GameLogicRandomValueReal( vertForce * 0.75f, vertForce );
- DUMPREAL(horizForce);
- DUMPREAL(vertForce);
- DUMPCOORD3D(&force);
- }
- else
- {
- calcRandomForce(m_minMag, m_maxMag, m_minPitch, m_maxPitch, &force);
- DUMPREAL(m_minMag);
- DUMPREAL(m_maxMag);
- DUMPREAL(m_minPitch);
- DUMPREAL(m_maxPitch);
- DUMPCOORD3D(&force);
- }
- objUp->applyForce(&force);
- if (m_orientInForceDirection)
- {
- orientation = atan2(force.y, force.x);
- }
- DUMPREAL(orientation);
- objUp->setAngles(orientation, 0, 0);
- objUp->setYawRate(yaw);
- objUp->setRollRate(roll);
- objUp->setPitchRate(pitch);
- DUMPCOORD3D(objUp->getAcceleration());
- DUMPCOORD3D(objUp->getVelocity());
- DUMPMATRIX3D(obj->getTransformMatrix());
- }
- }
- if( BitTest( m_disposition, WHIRLING ) )
- {
- PhysicsBehavior* objUp = obj->getPhysics();
- if (objUp)
- {
- Real yaw = GameLogicRandomValueReal( -m_dispositionIntensity, m_dispositionIntensity );
- Real roll = GameLogicRandomValueReal( -m_dispositionIntensity, m_dispositionIntensity );
- Real pitch = GameLogicRandomValueReal( -m_dispositionIntensity, m_dispositionIntensity );
- objUp->setYawRate(yaw);
- objUp->setRollRate(roll);
- objUp->setPitchRate(pitch);
- }
- }
-
- if( BitTest( m_disposition, FLOATING ) )
- {
- static NameKeyType key = NAMEKEY( "FloatUpdate" );
- FloatUpdate *floatUpdate = (FloatUpdate *)obj->findUpdateModule( key );
- if( floatUpdate )
- floatUpdate->setEnabled( TRUE );
- }
- if( m_containInsideSourceObject )
- {
- // The Obj has been totally made, so stuff it inside ourselves if desired.
- if( sourceObj->getContain() && sourceObj->getContain()->isValidContainerFor(obj, TRUE))
- {
- sourceObj->getContain()->addToContain( obj );
- // Need to hide if they are hidden.
- if( sourceObj->getDrawable() && obj->getDrawable() && sourceObj->getDrawable()->isDrawableEffectivelyHidden() )
- obj->getDrawable()->setDrawableHidden( TRUE );
- }
- else
- {
- DEBUG_ASSERTCRASH(FALSE,("A OCL with ContainInsideSourceObject failed the contain and is killing the new object."));
- // If we fail to contain it, we can't just leave it. Stillborn it.
- TheGameLogic->destroyObject(obj);
- }
- }
- }
- void reallyCreate(const Coord3D *pos, const Matrix3D *mtx, Real orientation, const Object *sourceObj, UnsignedInt lifetimeFrames ) const
- {
- static const ThingTemplate* debrisTemplate = TheThingFactory->findTemplate("GenericDebris");
- if (m_names.size() <= 0)
- return;
- if (m_requiresLivePlayer && (!sourceObj || !sourceObj->getControllingPlayer()->isPlayerActive()))
- return; // don't spawn useful objects for dead players. Avoid the zombie units from Yuri's.
- // Object type debris might need this information to process visual UpgradeModules.
- Team *debrisOwner = NULL;
- if( sourceObj )
- debrisOwner = sourceObj->getControllingPlayer()->getDefaultTeam();
-
- Object* container = NULL;
- if (!m_putInContainer.isEmpty())
- {
- const ThingTemplate* containerTmpl = TheThingFactory->findTemplate(m_putInContainer);
- if (containerTmpl)
- {
- container = TheThingFactory->newObject( containerTmpl, debrisOwner );
- container->setProducer(sourceObj);
- }
- }
- for (Int nn = 0; nn < m_debrisToGenerate; nn++)
- {
- Int pick = GameLogicRandomValue(0, m_names.size() - 1);
- const ThingTemplate* tmpl;
- if (m_nameAreObjects)
- tmpl = TheThingFactory->findTemplate(m_names[pick]);
- else
- { //this is using the generic debris type so it's probably safe to
- //remove if requested by the GameLOD manager.
- if (TheGameLODManager->isDebrisSkipped())
- continue;
- tmpl = debrisTemplate;
- }
- DEBUG_ASSERTCRASH(tmpl, ("Object %s not found\n",m_names[pick].str()));
- if (!tmpl)
- continue;
- Object *debris = TheThingFactory->newObject( tmpl, debrisOwner );
- debris->setProducer(sourceObj);
- if (m_preserveLayer && sourceObj != NULL && container == NULL)
- {
- PathfindLayerEnum layer = sourceObj->getLayer();
- if (layer != LAYER_GROUND)
- debris->setLayer(layer);
- }
- if (container != NULL && container->getContain() != NULL && container->getContain()->isValidContainerFor(debris, true))
- container->getContain()->addToContain(debris);
- // if we want the objects being created to appear in a spread formation
- // PLEASE NOTE --> if/when the object placement logic is modified so that
- // objects that are placed in the same location are no longer placed in a
- // diagonal line but rather in random locations nearby, this logic will no
- // longer be necessary and can be taken out -- amit
- if (m_spreadFormation)
- {
- Coord3D resultPos;
- FindPositionOptions fpOptions;
- fpOptions.minRadius = GameLogicRandomValueReal(m_minDistanceAFormation, m_minDistanceBFormation);
- fpOptions.maxRadius = m_maxDistanceFormation;
- fpOptions.flags = FPF_USE_HIGHEST_LAYER;
- ThePartitionManager->findPositionAround(pos, &fpOptions, &resultPos);
- doStuffToObj( debris, m_names[pick], &resultPos, mtx, orientation, sourceObj, lifetimeFrames );
- }
- else
- {
- // do stuff to contained objects too
- doStuffToObj( debris, m_names[pick], pos, mtx, orientation, sourceObj, lifetimeFrames );
- }
- if (m_fadeIn)
- {
- AudioEventRTS fadeAudioEvent(m_fadeSoundName);
- fadeAudioEvent.setObjectID(sourceObj->getID());
- TheAudio->addAudioEvent(&fadeAudioEvent);
- debris->getDrawable()->fadeIn(m_fadeFrames);
- }
- if (m_fadeOut)
- {
- AudioEventRTS fadeAudioEvent(m_fadeSoundName);
- fadeAudioEvent.setObjectID(sourceObj->getID());
- TheAudio->addAudioEvent(&fadeAudioEvent);
- debris->getDrawable()->fadeOut(m_fadeFrames);
- }
- }
- if (container)
- doStuffToObj( container, AsciiString::TheEmptyString, pos, mtx, orientation, sourceObj, lifetimeFrames );
- }
- static void parseDebrisObjectNames( INI* ini, void *instance, void *store, const void* /*userData*/ )
- {
- GenericObjectCreationNugget* debrisNugget = (GenericObjectCreationNugget*)instance;
- for (const char* debrisName = ini->getNextToken(); debrisName; debrisName = ini->getNextTokenOrNull())
- {
- if (TheGlobalData->m_preloadAssets)
- debrisModelNamesGlobalHack.push_back(debrisName);
- debrisNugget->m_names.push_back(AsciiString(debrisName));
- debrisName = ini->getNextTokenOrNull();
- }
- }
- private:
- struct AnimSet
- {
- AsciiString m_animInitial;
- AsciiString m_animFlying;
- AsciiString m_animFinal;
- };
- std::vector<AsciiString> m_names;
- AsciiString m_putInContainer;
- std::vector<AnimSet> m_animSets;
- const FXList* m_fxFinal;
- AsciiString m_particleSysName;
- Int m_debrisToGenerate;
- Real m_mass;
- Real m_extraBounciness;
- Real m_extraFriction;
- Coord3D m_offset;
- DebrisDisposition m_disposition;
- Real m_dispositionIntensity;
- Real m_spinRate;
- Real m_yawRate;
- Real m_rollRate;
- Real m_pitchRate;
- Real m_minMag, m_maxMag;
- Real m_minPitch, m_maxPitch;
- UnsignedInt m_minFrames, m_maxFrames;
- ShadowType m_shadowType;
- StaticGameLODLevel m_minLODRequired;
- UnsignedInt m_invulnerableTime;
- Real m_minHealth;
- Real m_maxHealth;
- UnsignedInt m_fadeFrames;
- AsciiString m_fadeSoundName;
- Real m_minDistanceAFormation;
- Real m_minDistanceBFormation;
- Real m_maxDistanceFormation;
- Int m_objectCount; // how many objects will there be?
- AudioEventRTS m_bounceSound;
- Bool m_requiresLivePlayer;
- Bool m_containInsideSourceObject; ///< The created stuff will be added to the Conatin module of the SourceObject
- Bool m_preserveLayer;
- Bool m_nameAreObjects;
- Bool m_okToChangeModelColor;
- Bool m_orientInForceDirection;
- Bool m_spreadFormation;
- Bool m_fadeIn;
- Bool m_fadeOut;
- Bool m_ignorePrimaryObstacle;
- Bool m_inheritsVeterancy;
- Bool m_skipIfSignificantlyAirborne;
- };
- EMPTY_DTOR(GenericObjectCreationNugget)
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- static const FieldParse TheObjectCreationListFieldParse[] =
- {
- { "CreateObject", GenericObjectCreationNugget::parseObject, 0, 0},
- { "CreateDebris", GenericObjectCreationNugget::parseDebris, 0, 0},
- { "ApplyRandomForce", ApplyRandomForceNugget::parse, 0, 0},
- { "DeliverPayload", DeliverPayloadNugget::parse, 0, 0},
- { "FireWeapon", FireWeaponNugget::parse, 0, 0},
- { "Attack", AttackNugget::parse, 0, 0},
- { NULL, NULL, 0, 0 } // keep this last
- };
- //-------------------------------------------------------------------------------------------------
- void ObjectCreationList::clear()
- {
- // do NOT delete the nuggets -- they're owned by the Store.
- m_nuggets.clear();
- }
- //-------------------------------------------------------------------------------------------------
- void ObjectCreationList::addObjectCreationNugget(ObjectCreationNugget* nugget)
- {
- m_nuggets.push_back(nugget);
- TheObjectCreationListStore->addObjectCreationNugget(nugget);
- }
- //-------------------------------------------------------------------------------------------------
- void ObjectCreationList::create( const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, Bool createOwner, UnsignedInt lifetimeFrames ) const
- {
- DEBUG_ASSERTCRASH(primaryObj != NULL, ("You should always call OCLs with a non-null primary Obj, even for positional calls, to get team ownership right"));
- for (ObjectCreationNuggetVector::const_iterator i = m_nuggets.begin(); i != m_nuggets.end(); ++i)
- {
- (*i)->create( primaryObj, primary, secondary, createOwner, lifetimeFrames );
- }
- }
- //-------------------------------------------------------------------------------------------------
- void ObjectCreationList::create( const Object* primaryObj, const Coord3D *primary, const Coord3D* secondary, UnsignedInt lifetimeFrames ) const
- {
- DEBUG_ASSERTCRASH(primaryObj != NULL, ("You should always call OCLs with a non-null primary Obj, even for positional calls, to get team ownership right"));
- for (ObjectCreationNuggetVector::const_iterator i = m_nuggets.begin(); i != m_nuggets.end(); ++i)
- {
- (*i)->create( primaryObj, primary, secondary, lifetimeFrames );
- }
- }
- //-------------------------------------------------------------------------------------------------
- void ObjectCreationList::create( const Object* primary, const Object* secondary, UnsignedInt lifetimeFrames ) const
- {
- DEBUG_ASSERTCRASH(primary != NULL, ("You should always call OCLs with a non-null primary Obj, even for positional calls, to get team ownership right"));
- for (ObjectCreationNuggetVector::const_iterator i = m_nuggets.begin(); i != m_nuggets.end(); ++i)
- {
- (*i)->create( primary, secondary, lifetimeFrames );
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- ObjectCreationListStore::ObjectCreationListStore()
- {
- }
- //-------------------------------------------------------------------------------------------------
- ObjectCreationListStore::~ObjectCreationListStore()
- {
- for (ObjectCreationNuggetVector::iterator i = m_nuggets.begin(); i != m_nuggets.end(); ++i)
- {
- if (*i)
- (*i)->deleteInstance();
- }
- m_nuggets.clear();
- }
- //-------------------------------------------------------------------------------------------------
- const ObjectCreationList *ObjectCreationListStore::findObjectCreationList(const char* name) const
- {
- if (stricmp(name, "None") == 0)
- return NULL;
- ObjectCreationListMap::const_iterator it = m_ocls.find(NAMEKEY(name));
- if (it == m_ocls.end())
- {
- return NULL;
- }
- else
- {
- return &(*it).second;
- }
- }
- //-------------------------------------------------------------------------------------------------
- void ObjectCreationListStore::addObjectCreationNugget(ObjectCreationNugget* nugget)
- {
- m_nuggets.push_back(nugget);
- }
- //-------------------------------------------------------------------------------------------------
- /*static */ void ObjectCreationListStore::parseObjectCreationListDefinition(INI *ini)
- {
- // read the ObjectCreationList name
- const char *c = ini->getNextToken();
- NameKeyType key = TheNameKeyGenerator->nameToKey(c);
- ObjectCreationList& ocl = TheObjectCreationListStore->m_ocls[key];
- ocl.clear();
- ini->initFromINI(&ocl, TheObjectCreationListFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- /*static*/ void INI::parseObjectCreationListDefinition(INI *ini)
- {
- ObjectCreationListStore::parseObjectCreationListDefinition(ini);
- }
|