| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- /*
- ** 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: MobNexusContain.cpp //////////////////////////////////////////////////////////////////////
- // Author: Mark Lorenzen, August 2002
- // Desc: Contain module for mob units.
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/Player.h"
- #include "Common/ThingTemplate.h"
- #include "Common/ThingFactory.h"
- #include "Common/Xfer.h"
- #include "GameClient/Drawable.h"
- #include "GameLogic/AI.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/StealthUpdate.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/MobNexusContain.h"
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/Object.h"
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- MobNexusContainModuleData::MobNexusContainModuleData()
- {
- m_slotCapacity = 0;
- m_scatterNearbyOnExit = true;
- m_orientLikeContainerOnExit = false;
- m_keepContainerVelocityOnExit = false;
- m_exitPitchRate = 0.0f;
- m_initialPayload.count = 0;
- m_healthRegen = 0.0f;
- //
- // by default we say that MobNexae can have infantry inside them, this will be totally
- // overwritten by any data provided from the INI entry tho
- //
- m_allowInsideKindOf = MAKE_KINDOF_MASK( KINDOF_INFANTRY );
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void MobNexusContainModuleData::parseInitialPayload( INI* ini, void *instance, void *store, const void* /*userData*/ )
- {
- MobNexusContainModuleData* self = (MobNexusContainModuleData*)instance;
- const char* name = ini->getNextToken();
- const char* countStr = ini->getNextTokenOrNull();
- Int count = countStr ? INI::scanInt(countStr) : 1;
-
- self->m_initialPayload.name.set(name);
- self->m_initialPayload.count = count;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void MobNexusContainModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- OpenContainModuleData::buildFieldParse(p);
- static const FieldParse dataFieldParse[] =
- {
- { "Slots", INI::parseInt, NULL, offsetof( MobNexusContainModuleData, m_slotCapacity ) },
- { "ScatterNearbyOnExit", INI::parseBool, NULL, offsetof( MobNexusContainModuleData, m_scatterNearbyOnExit ) },
- { "OrientLikeContainerOnExit", INI::parseBool, NULL, offsetof( MobNexusContainModuleData, m_orientLikeContainerOnExit ) },
- { "KeepContainerVelocityOnExit", INI::parseBool, NULL, offsetof( MobNexusContainModuleData, m_keepContainerVelocityOnExit ) },
- { "ExitBone", INI::parseAsciiString, NULL, offsetof( MobNexusContainModuleData, m_exitBone ) },
- { "ExitPitchRate", INI::parseAngularVelocityReal, NULL, offsetof( MobNexusContainModuleData, m_exitPitchRate ) },
- { "InitialPayload", parseInitialPayload, NULL, 0 },
- { "HealthRegen%PerSec", INI::parseReal, NULL, offsetof( MobNexusContainModuleData, m_healthRegen ) },
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- }
- // PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Int MobNexusContain::getContainMax( void ) const
- {
- if (getMobNexusContainModuleData())
- return getMobNexusContainModuleData()->m_slotCapacity;
- return 0;
- }
- // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- MobNexusContain::MobNexusContain( Thing *thing, const ModuleData *moduleData ) :
- OpenContain( thing, moduleData )
- {
- m_extraSlotsInUse = 0;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- MobNexusContain::~MobNexusContain( void )
- {
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /**
- can this container contain this kind of object?
- and, if checkCapacity is TRUE, does this container have enough space left to hold the given unit?
- */
- Bool MobNexusContain::isValidContainerFor(const Object* rider, Bool checkCapacity) const
- {
- // sanity
- if (!rider)
- return false;
- //The point of this new code is to determine when something has a negative 1 contain max, to
- //look at the object inside of it to use that as the valid check. There is a case, when a
- //paratrooper (an infantry contained in a parachute). In this case, when we pass this object
- //to contain in a --- plane, we want to check the infantry, not the parachute.
- // const MobNexusContainModuleData *modData = getMobNexusContainModuleData();
- //Check if we are a fake container, and if so, get an object inside it to see what kind this object *is*.
- if( rider->getContain() && rider->getContain()->isSpecialZeroSlotContainer() )
- {
- //Report the first thing inside it!
- const ContainedItemsList *items = rider->getContain()->getContainedItemsList();
- if( items )
- {
- ContainedItemsList::const_iterator it;
- it = items->begin();
- if( *it )
- {
- //Replace the object we are checking with the *first* object contained within it.
- rider = *it;
- }
- }
- }
- else
- {
- //blech! This case may or may not occur... in which case, just use the supplied object.
- }
- // extend functionality
- if( OpenContain::isValidContainerFor( rider, checkCapacity ) == false )
- return false;
- // only allied objects can be MobNexused.
- // order matters: we want to know if IT considers ME to be an ally (a reversal of the usual situation)
- if (rider->getRelationship(getObject()) != ALLIES)
- return false;
- Int mobNexusSlotCount = rider->getTransportSlotCount();
- // if 0, this object isn't MobNexusable.
- if (mobNexusSlotCount == 0)
- return false;
- if (checkCapacity)
- {
- return (m_extraSlotsInUse + getContainCount() + mobNexusSlotCount <= getContainMax());
- }
- else
- {
- return true;
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void MobNexusContain::onContaining( Object *rider )
- {
- OpenContain::onContaining(rider);
- // objects inside a MobNexus are held
- rider->setDisabled( DISABLED_HELD );
- Int mobNexusSlotCount = rider->getTransportSlotCount();
- DEBUG_ASSERTCRASH(mobNexusSlotCount > 0, ("Hmm, this object isnt MobNexusable"));
- m_extraSlotsInUse += mobNexusSlotCount - 1;
- DEBUG_ASSERTCRASH(m_extraSlotsInUse >= 0 && m_extraSlotsInUse + getContainCount() <= getContainMax(), ("Hmm, bad slot count"));
- //
- // when we go from holding nothing to holding something we have a model condition
- // to visually show the change
- //
- if( getContainCount() == 1 )
- {
- Drawable *draw = getObject()->getDrawable();
- if( draw )
- draw->setModelConditionState( MODELCONDITION_LOADED );
- } // end if
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void MobNexusContain::onRemoving( Object *rider )
- {
- OpenContain::onRemoving(rider);
- // object is no longer held inside a MobNexus
- rider->clearDisabled( DISABLED_HELD );
- const MobNexusContainModuleData* d = getMobNexusContainModuleData();
- if (!d->m_exitBone.isEmpty())
- {
- Drawable* draw = getObject()->getDrawable();
- if (draw)
- {
- Coord3D bonePos, worldPos;
- if (draw->getPristineBonePositions(d->m_exitBone.str(), 0, &bonePos, NULL, 1) == 1)
- {
- getObject()->convertBonePosToWorldPos(&bonePos, NULL, &worldPos, NULL);
- rider->setPosition(&worldPos);
- }
- }
- }
- if (d->m_orientLikeContainerOnExit)
- {
- rider->setOrientation(getObject()->getOrientation());
- }
- if (d->m_keepContainerVelocityOnExit)
- {
- PhysicsBehavior* parent = getObject()->getPhysics();
- PhysicsBehavior* child = rider->getPhysics();
- if (parent && child)
- {
- Coord3D startingForce = *parent->getVelocity();
- Real mass = child->getMass();
- startingForce.x *= mass;
- startingForce.y *= mass;
- startingForce.z *= mass;
- child->applyMotiveForce( &startingForce );
- Real pitchRate = child->getCenterOfMassOffset() * d->m_exitPitchRate;
- child->setPitchRate( pitchRate );
- }
- }
- if (d->m_scatterNearbyOnExit)
- scatterToNearbyPosition(rider);
- Int mobNexusSlotCount = rider->getTransportSlotCount();
- DEBUG_ASSERTCRASH(mobNexusSlotCount > 0, ("This object isnt MobNexusable"));
- m_extraSlotsInUse -= mobNexusSlotCount - 1;
- DEBUG_ASSERTCRASH(m_extraSlotsInUse >= 0 && m_extraSlotsInUse + getContainCount() <= getContainMax(), ("Bad slot count, MobNexus"));
- // when we are empty again, clear the model condition for loaded
- if( getContainCount() == 0 )
- {
- Drawable *draw = getObject()->getDrawable();
- if( draw )
- draw->clearModelConditionState( MODELCONDITION_LOADED );
- } // end if
- if (getObject()->isAboveTerrain())
- {
- // temporarily mark the guy as being allowed to fall
- // (overriding his locomotor's stick-to-ground attribute).
- // this will be reset (by PhysicsBehavior) when he touches the ground.
- PhysicsBehavior* physics = rider->getPhysics();
- if (physics)
- physics->setAllowToFall(true);
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void MobNexusContain::onObjectCreated()
- {
- MobNexusContainModuleData* self = (MobNexusContainModuleData*)getMobNexusContainModuleData();
- Int count = self->m_initialPayload.count;
- const ThingTemplate* payloadTemplate = TheThingFactory->findTemplate( self->m_initialPayload.name );
- Object* object = getObject();
- for( int i = 0; i < count; i++ )
- {
- //We are creating a MobNexus that comes with a initial payload, so add it now!
- Object* payload = TheThingFactory->newObject( payloadTemplate, object->getControllingPlayer()->getDefaultTeam() );
- if( object->getContain() && object->getContain()->isValidContainerFor( payload, true ) )
- {
- object->getContain()->addToContain( payload );
- }
- else
- {
- DEBUG_CRASH( ( "DeliverPayload: PutInContainer %s is full, or not valid for the payload %s!", object->getName().str(), self->m_initialPayload.name.str() ) );
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- UpdateSleepTime MobNexusContain::update()
- {
- MobNexusContainModuleData *moduleData = (MobNexusContainModuleData*)getModuleData();
- if( moduleData && moduleData->m_healthRegen )
- {
- ContainModuleInterface *contain = getObject()->getContain();
- if( contain )
- {
- //This MobNexus has a health regeneration value, so go through and heal all inside.
- const ContainedItemsList* items = contain->getContainedItemsList();
- if( items )
- {
- ContainedItemsList::const_iterator it;
- it = items->begin();
- while( *it )
- {
- Object *object = *it;
- //Advance to the next iterator
- it++;
- //Determine if we need healing or not.
- BodyModuleInterface *body = object->getBodyModule();
- if( body->getHealth() < body->getMaxHealth() )
- {
- //Calculate the health to be regenerated on each unit.
- Real regen = body->getMaxHealth() * moduleData->m_healthRegen / 100.0f * SECONDS_PER_LOGICFRAME_REAL;
- //Perform the actual healing for this frame.
- // DamageInfo damageInfo;
- // damageInfo.in.m_damageType = DAMAGE_HEALING;
- // damageInfo.in.m_deathType = DEATH_NONE;
- // damageInfo.in.m_sourceID = getObject()->getID();
- // damageInfo.in.m_amount = regen;
- // object->attemptDamage( &damageInfo );
- object->attemptHealing( regen, getObject() );
- }
- }
- }
- }
- }
- return OpenContain::update(); //extend
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- ExitDoorType MobNexusContain::reserveDoorForExit( const ThingTemplate* objType, Object *specificObject )
- {
- if( specificObject == NULL )
- return DOOR_1;// I can, in general, exit people.
- // This is an override, not an extend. I will check for game legality for
- // okaying the call to exitObjectViaDoor.
- Object *me = getObject();
-
- // this is present solely for some MobNexuss to override, so that they can land before
- // allowing people to exit...
- AIUpdateInterface* ai = me->getAIUpdateInterface();
- if (ai && ai->getAiFreeToExit(me) != FREE_TO_EXIT)
- return DOOR_NONE_AVAILABLE;
- // I can always kick people out if I am in the air, I know what I'm doing
- if( me->isUsingAirborneLocomotor() )
- return DOOR_1;
-
- const Coord3D *myPosition = me->getPosition();
- if( !specificObject->getAIUpdateInterface() )
- {
- return DOOR_NONE_AVAILABLE;
- }
- const Locomotor *hisLocomotor = specificObject->getAIUpdateInterface()->getCurLocomotor();
- // He can't get to this spot naturally, so I can't force him there. (amphib MobNexus)
- if( ! TheAI->pathfinder()->validMovementTerrain(me->getLayer(), hisLocomotor, myPosition ) )
- return DOOR_NONE_AVAILABLE;
-
- return DOOR_1;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void MobNexusContain::unreserveDoorForExit( ExitDoorType exitDoor )
- {
- /* nothing */
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool MobNexusContain::tryToEvacuate( Bool exposeStealthedUnits )
- {
- Bool exitedAnyone = false;
- ContainedItemsList::const_iterator it = getContainList().begin();
- while( it != getContainList().end() )
- {
- // get the object
- Object *obj = *it;
- // increment the iterator, since removal will pull this guy from the list somewhere else
- // and we might not actually kick anyone so we don't want to loop forever.
- ++it;
- ExitDoorType exitDoor = reserveDoorForExit(obj->getTemplate(), obj);
- if(exitDoor != DOOR_NONE_AVAILABLE)
- {
- exitObjectViaDoor( obj, exitDoor );
- exitedAnyone = true;
- if( obj->isKindOf( KINDOF_STEALTH_GARRISON ) && exposeStealthedUnits )
- {
- static NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
- StealthUpdate* stealth = (StealthUpdate*)obj->findUpdateModule( key_StealthUpdate );
- if( stealth )
- {
- stealth->markAsDetected();
- }
- }
- }
- }
- return exitedAnyone;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC - I like the word nexus */
- // ------------------------------------------------------------------------------------------------
- void MobNexusContain::crc( Xfer *xfer )
- {
- // extend base class
- OpenContain::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void MobNexusContain::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- OpenContain::xfer( xfer );
- // extra slots in use
- xfer->xferInt( &m_extraSlotsInUse );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void MobNexusContain::loadPostProcess( void )
- {
- // extend base class
- OpenContain::loadPostProcess();
- } // end loadPostProcess
|