| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195 |
- /*
- ** 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. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // DeliverPayloadAIUpdate.cpp ////////////
- // Author: Graham Smallwood, March 2002
- // Desc: State machine that controls the approach and deployment of airborne cargo
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #define DEFINE_WEAPONSLOTTYPE_NAMES
- #include "Common/Player.h"
- #include "Common/RandomValue.h"
- #include "Common/ThingFactory.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Xfer.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/FXList.h"
- #include "GameClient/InGameUI.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/ContainModule.h"
- #include "GameLogic/Module/DeliverPayloadAIUpdate.h"
- #include "GameLogic/Module/GenerateMinefieldBehavior.h"
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/Module/ParachuteContain.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/Weapon.h"
- #include "GameLogic/WeaponSet.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- //-------------------------------------------------------------------------------------------------
- const FieldParse* DeliverPayloadData::getFieldParse()
- {
- static const FieldParse dataFieldParse[] =
- {
- { "DeliveryDistance", INI::parseReal, NULL, offsetof( DeliverPayloadData, m_distToTarget) },
- { "PreOpenDistance", INI::parseReal, NULL, offsetof( DeliverPayloadData, m_preOpenDistance) },
- { "MaxAttempts", INI::parseInt, NULL, offsetof( DeliverPayloadData, m_maxAttempts) },
- //Drop information
- { "DropDelay", INI::parseDurationUnsignedInt,NULL, offsetof( DeliverPayloadData, m_dropDelay ) },
- { "DropOffset", INI::parseCoord3D, NULL, offsetof( DeliverPayloadData, m_dropOffset ) },
- { "DropVariance", INI::parseCoord3D, NULL, offsetof( DeliverPayloadData, m_dropVariance ) },
- { "InheritTransportVelocity", INI::parseBool, NULL, offsetof( DeliverPayloadData, m_inheritTransportVelocity ) },
- { "ExitPitchRate", INI::parseAngularVelocityReal,NULL, offsetof( DeliverPayloadData, m_exitPitchRate ) },
- { "ParachuteDirectly", INI::parseBool, NULL, offsetof( DeliverPayloadData, m_isParachuteDirectly) },
- //Visible payload information (payload assumed to be show visibly and it's created only when dropped)
- { "VisibleItemsDroppedPerInterval", INI::parseInt, NULL, offsetof( DeliverPayloadData, m_visibleItemsDroppedPerInterval ) },
- { "VisibleDropBoneBaseName", INI::parseAsciiString, NULL, offsetof( DeliverPayloadData, m_visibleDropBoneName ) },
- { "VisibleSubObjectBaseName", INI::parseAsciiString, NULL, offsetof( DeliverPayloadData, m_visibleSubObjectName ) },
- { "VisibleNumBones", INI::parseInt, NULL, offsetof( DeliverPayloadData, m_visibleNumBones ) },
- { "VisiblePayloadTemplateName", INI::parseAsciiString, NULL, offsetof( DeliverPayloadData, m_visiblePayloadTemplateName ) },
- { "VisiblePayloadWeaponTemplate", INI::parseWeaponTemplate, NULL, offsetof( DeliverPayloadData, m_visiblePayloadWeaponTemplate ) },
- { "SelfDestructObject", INI::parseBool, NULL, offsetof( DeliverPayloadData, m_selfDestructObject ) },
-
- //Weapon based payload
- { "FireWeapon", INI::parseBool, NULL, offsetof( DeliverPayloadData, m_fireWeapon ) },
-
- //Specify an additional weaponslot to be fired while strafing
- { "DiveStartDistance", INI::parseReal, NULL, offsetof( DeliverPayloadData, m_diveStartDistance ) },
- { "DiveEndDistance", INI::parseReal, NULL, offsetof( DeliverPayloadData, m_diveEndDistance ) },
- { "StrafingWeaponSlot", INI::parseLookupList, TheWeaponSlotTypeNamesLookupList, offsetof( DeliverPayloadData, m_strafingWeaponSlot ) },
- { "StrafeWeaponFX", INI::parseFXList, NULL, offsetof( DeliverPayloadData, m_strafeFX ) },
- { "StrafeLength", INI::parseReal, NULL, offsetof( DeliverPayloadData, m_strafeLength ) },
- { "DeliveryDecal", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( DeliverPayloadData, m_deliveryDecalTemplate ) },
- { "DeliveryDecalRadius", INI::parseReal, NULL, offsetof(DeliverPayloadData, m_deliveryDecalRadius) },
- { 0, 0, 0, 0 }
- };
- return dataFieldParse;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- AIStateMachine* DeliverPayloadAIUpdate::makeStateMachine()
- {
- return newInstance(AIStateMachine)( getObject(), "DeliverPayloadBasicAI");
- }
- //-------------------------------------------------------------------------------------------------
- DeliverPayloadAIUpdate::DeliverPayloadAIUpdate( Thing *thing, const ModuleData* moduleData ) : AIUpdateInterface( thing, moduleData )
- {
- m_deliverPayloadStateMachine = NULL;
- m_targetPos.zero();
- m_moveToPos.zero();
- m_visibleItemsDelivered = 0;
- m_deliveryDecal.clear();
- m_previousDistanceSqr = 0;
- m_freeToExit = FALSE;
- m_acceptingCommands = TRUE;
- // Added By Sadullah Nader
- // Initialization missing and needed
-
- m_diveState = DIVESTATE_PREDIVE;
-
- // End Add
- }
- //-------------------------------------------------------------------------------------------------
- DeliverPayloadAIUpdate::~DeliverPayloadAIUpdate( void )
- {
- m_deliveryDecal.clear();
- if (m_deliverPayloadStateMachine)
- m_deliverPayloadStateMachine->deleteInstance();
- }
- //-------------------------------------------------------------------------------------------------
- AIFreeToExitType DeliverPayloadAIUpdate::getAiFreeToExit(const Object* exiter) const
- {
- if( getObject()->isEffectivelyDead() )
- return NOT_FREE_TO_EXIT;
-
- return m_freeToExit ? FREE_TO_EXIT : NOT_FREE_TO_EXIT;
- }
- //-------------------------------------------------------------------------------------------------
- void DeliverPayloadAIUpdate::killDeliveryDecal()
- {
- m_deliveryDecal.clear();
- }
- //-------------------------------------------------------------------------------------------------
- Bool DeliverPayloadAIUpdate::isAllowedToRespondToAiCommands(const AICommandParms* parms) const
- {
- if (!m_acceptingCommands)
- {
- return false;
- }
- return AIUpdateInterface::isAllowedToRespondToAiCommands(parms);
- }
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime DeliverPayloadAIUpdate::update( void )
- {
- m_deliveryDecal.update();
- if(!(isAiInDeadState()) && m_deliverPayloadStateMachine)
- m_deliverPayloadStateMachine->updateStateMachine();
- //Handling diving logic (regardless of state)
- if( m_diveState != DIVESTATE_POSTDIVE )
- {
- if( m_diveState == DIVESTATE_PREDIVE )
- {
- //Check to see if we are close enough to start diving.
- Real startDiveDistanceSquared = sqr( getData()->m_diveStartDistance );
- Real currentDistanceSquared = ThePartitionManager->getDistanceSquared( getObject(), getTargetPos(), FROM_CENTER_2D );
- if( currentDistanceSquared <= startDiveDistanceSquared )
- {
- m_diveState = DIVESTATE_DIVING;
- getObject()->getAIUpdateInterface()->getCurLocomotor()->setUsePreciseZPos( true );
- AudioEventRTS soundDive = *(getObject()->getTemplate()->getPerUnitSound("StartDive"));
- if( soundDive.getEventName().isNotEmpty() )
- {
- soundDive.setPosition( getObject()->getPosition() );
- TheAudio->addAudioEvent( &soundDive );
- }
- }
- }
- else
- {
- //Check to see when we shall end diving
- Real endDiveDistanceSquared = sqr( getData()->m_diveEndDistance );
- Real currentDistanceSquared = ThePartitionManager->getDistanceSquared( getObject(), getTargetPos(), FROM_CENTER_3D );
- if( currentDistanceSquared <= endDiveDistanceSquared )
- {
- m_diveState = DIVESTATE_POSTDIVE;
- getObject()->getAIUpdateInterface()->getCurLocomotor()->setUsePreciseZPos( false );
- }
- if( m_data.m_strafingWeaponSlot != -1 )
- {
- //SHOOT!!
- const Coord3D *velocity = getObject()->getPhysics()->getVelocity();
- if( velocity->z < 5.0f )
- {
- //Calc strafe ratio
- Real startDiveDistance = getData()->m_diveStartDistance;
- Real endDiveDistance = sqrt( endDiveDistanceSquared );
- Real currentDistance = sqrt( currentDistanceSquared );
- Real diveRatio = (startDiveDistance - currentDistance) / (startDiveDistance - endDiveDistance);
-
- Coord3D velocity = *getObject()->getPhysics()->getVelocity();
- velocity.z = 0.0f;
- velocity.normalize();
- velocity.scale( diveRatio * 100.0f );
- Coord3D backwards = velocity;
- backwards.scale( 0.33f );
- Coord3D strafePoint = *getTargetPos();
- strafePoint.sub( &backwards );
- strafePoint.add( &velocity );
- strafePoint.z = TheTerrainLogic->getGroundHeight( strafePoint.x, strafePoint.y );
- // lock it just till the weapon is empty or the attack is "done"
- getObject()->setWeaponLock( m_data.m_strafingWeaponSlot, LOCKED_TEMPORARILY );
- getObject()->fireCurrentWeapon( &strafePoint );
- FXList::doFXPos( m_data.m_strafeFX, &strafePoint );
- }
- }
- }
- }
- AIUpdateInterface::update();
- return UPDATE_SLEEP_NONE; // ignore our parent, and never sleep
- }
- //-------------------------------------------------------------------------------------------------
- void DeliverPayloadAIUpdate::deliverPayload(
- const Coord3D *moveToPos,
- const Coord3D *targetPos,
- const DeliverPayloadData *data
- )
- {
- //****************************************************
- //THIS GETS CALLED VIA OBJECT CREATION LISTS ONLY!!!
- //****************************************************
-
- if (m_deliverPayloadStateMachine)
- m_deliverPayloadStateMachine->deleteInstance();
- m_deliverPayloadStateMachine = NULL;
- m_moveToPos = *moveToPos;
- m_targetPos = *targetPos;
- m_data = *data;
-
- m_deliveryDecal.clear();
- m_data.m_deliveryDecalTemplate.createRadiusDecal(*targetPos,
- m_data.m_deliveryDecalRadius, getObject()->getControllingPlayer(), m_deliveryDecal);
- if( m_data.m_diveStartDistance <= 0.0f )
- {
- //We can't dive, so setting it to post-dive will prevent
- //dive logic from being processed.
- m_diveState = DIVESTATE_POSTDIVE;
- }
- else
- {
- m_diveState = DIVESTATE_PREDIVE;
- }
- m_visibleItemsDelivered = 0;
- //If we have visible bones, then show each of those subobjects.
- Drawable *draw = getObject()->getDrawable();
- if( draw )
- {
- for( int i = 1; i <= m_data.m_visibleNumBones; i++ )
- {
- if( m_data.m_visibleSubObjectName.isNotEmpty() )
- {
- AsciiString name;
- name.format( "%s%02d", m_data.m_visibleSubObjectName.str(), i );
- draw->showSubObject( name, true );
- }
- }
- }
- // must make the state machine AFTER initing the other stuff, since it may inquire of its values...
- m_deliverPayloadStateMachine = newInstance(DeliverPayloadStateMachine)( getObject() );
- m_deliverPayloadStateMachine->initDefaultState();
- #ifdef _DEBUG
- m_deliverPayloadStateMachine->setName("DeliverPayloadSpecificAI");
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- void DeliverPayloadAIUpdate::deliverPayloadViaModuleData( const Coord3D *moveToPos )
- {
- const DeliverPayloadAIUpdateModuleData *data = getDeliverPayloadAIUpdateModuleData();
- //****************************************************
- //THIS GETS CALLED FOR SCRIPTED REINFORCEMENTS ONLY!!!
- //These don't and can't use object creation lists.
- //****************************************************
- DeliverPayloadData dpData;
- dpData.m_dropOffset.set( &data->m_dropOffset );
- dpData.m_dropVariance.set( &data->m_dropVariance );
- dpData.m_distToTarget = data->m_maxDistanceToTarget;
- dpData.m_maxAttempts = data->m_maxNumberAttempts;
- dpData.m_dropDelay = data->m_dropDelay;
- dpData.m_deliveryDecalTemplate = data->m_deliveryDecalTemplate;
- dpData.m_deliveryDecalRadius = data->m_deliveryDecalRadius;
- deliverPayload( moveToPos, moveToPos, &dpData );
- }
- //-------------------------------------------------------------------------------------------------
- const ThingTemplate* DeliverPayloadAIUpdate::getPutInContainerTemplateViaModuleData() const
- {
- AsciiString name = getDeliverPayloadAIUpdateModuleData()->m_putInContainerName;
- return name.isEmpty() ? NULL : TheThingFactory->findTemplate( name );
- }
- //-------------------------------------------------------------------------------------------------
- Real DeliverPayloadAIUpdate::calcMinTurnRadius(Real* timeToTravelThatDist) const
- {
- const Locomotor* loco = getCurLocomotor();
- BodyDamageType bdt = getObject()->getBodyModule()->getDamageState();
- /// @todo srj -- this should probably use min-speed, not max-speed... fix after E3
- Real maxSpeed = loco->getMaxSpeedForCondition(bdt); // in dist/frame
- Real maxTurnRate = loco->getMaxTurnRate(bdt); // in rads/frame
- /*
- our minimum circumference will be like so:
-
- Real minTurnCircum = maxSpeed * (2*PI / maxTurnRate);
- so therefore our minimum turn radius is:
-
- Real minTurnRadius = minTurnCircum / 2*PI;
- so we just eliminate the middleman:
- */
- Real minTurnRadius = (maxTurnRate > 0.0f) ? (maxSpeed / maxTurnRate) : 999999.0f;
- if (timeToTravelThatDist)
- *timeToTravelThatDist = minTurnRadius / maxSpeed;
- return minTurnRadius;
- }
- //-------------------------------------------------------------------------------------------------
- Bool DeliverPayloadAIUpdate::isCloseEnoughToTarget()
- {
- // In addition to testing distance, it is also sensitive to being in/outward bound
- ////The new getPreOpenDistance() allows the deliver state to fire early, but only if inbound,
- ////so the doors can open and payload can get ready...
- Real allowedDistanceSqr = sqr( getAllowedDistanceToTarget() );
- Real currentDistanceSqr = ThePartitionManager->getDistanceSquared( getObject(), getTargetPos(), FROM_CENTER_2D );
- Bool inBound = m_previousDistanceSqr > currentDistanceSqr;
- m_previousDistanceSqr = currentDistanceSqr;// for the next test
- if ( inBound )
- allowedDistanceSqr = sqr(getAllowedDistanceToTarget() + getPreOpenDistance());
- //DEBUG_LOG(("Dist to target is %f (allowed %f)\n",sqrt(currentDistanceSqr),sqrt(allowedDistanceSqr)));
- if ( allowedDistanceSqr > currentDistanceSqr )
- return TRUE;
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- Bool DeliverPayloadAIUpdate::isOffMap() const
- {
- Region3D mapRegion;
- TheTerrainLogic->getExtentIncludingBorder( &mapRegion );
- if (!mapRegion.isInRegionNoZ( getObject()->getPosition() ))
- return true;
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void DeliverPayloadAIUpdate::crc( Xfer *xfer )
- {
- // extend base class
- AIUpdateInterface::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void DeliverPayloadAIUpdate::xfer( Xfer *xfer )
- {
- // version
- const XferVersion currentVersion = 3;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
-
- // extend base class
- AIUpdateInterface::xfer(xfer);
- xfer->xferCoord3D(&m_targetPos);
- xfer->xferCoord3D(&m_moveToPos);
- xfer->xferInt(&m_visibleItemsDelivered);
- xfer->xferUser(&m_diveState, sizeof(m_diveState));
- DeliverPayloadData data = m_data;
- xfer->xferAsciiString(&data.m_visibleDropBoneName);
- xfer->xferAsciiString(&data.m_visibleSubObjectName);
- xfer->xferAsciiString(&data.m_visiblePayloadTemplateName);
- xfer->xferReal(&data.m_distToTarget);
- xfer->xferInt(&data.m_maxAttempts);
- xfer->xferCoord3D(&data.m_dropOffset);
- xfer->xferCoord3D(&data.m_dropVariance);
- xfer->xferUnsignedInt(&data.m_dropDelay);
- xfer->xferBool(&data.m_fireWeapon);
- xfer->xferBool(&data.m_selfDestructObject);
- xfer->xferInt(&data.m_visibleNumBones);
- xfer->xferReal(&data.m_diveStartDistance);
- xfer->xferReal(&data.m_diveEndDistance);
- xfer->xferUser(&data.m_strafingWeaponSlot, sizeof(data.m_strafingWeaponSlot));
- xfer->xferInt(&data.m_visibleItemsDroppedPerInterval);
- xfer->xferBool(&data.m_inheritTransportVelocity);
- xfer->xferBool(&data.m_isParachuteDirectly);
- xfer->xferReal(&data.m_exitPitchRate);
- // const FXList *m_strafeFX;
- xfer->xferReal(&data.m_strafeLength);
- AsciiString weaponTemplateName;
- if (data.m_visiblePayloadWeaponTemplate)
- {
- weaponTemplateName = data.m_visiblePayloadWeaponTemplate->getName();
- }
- xfer->xferAsciiString(&weaponTemplateName);
- if( xfer->getXferMode() == XFER_LOAD && weaponTemplateName.isNotEmpty())
- {
- data.m_visiblePayloadWeaponTemplate = TheWeaponStore->findWeaponTemplate(weaponTemplateName);
- }
- data.m_deliveryDecalTemplate.xferRadiusDecalTemplate(xfer);
- xfer->xferReal(&data.m_deliveryDecalRadius);
- *((DeliverPayloadData*)&m_data) = data;
- Bool hasStateMachine = m_deliverPayloadStateMachine!=NULL;
- xfer->xferBool(&hasStateMachine);
- if (hasStateMachine && m_deliverPayloadStateMachine==NULL)
- {
- m_deliverPayloadStateMachine = newInstance(DeliverPayloadStateMachine)( getObject() );
- }
- if (m_deliverPayloadStateMachine && version >= 2)
- {
- xfer->xferSnapshot(m_deliverPayloadStateMachine);
- }
-
- m_deliveryDecal.xferRadiusDecal(xfer);
- if (version >= 2)
- {
- xfer->xferBool(&m_freeToExit);
- }
- if (version >= 3)
- {
- xfer->xferBool(&m_acceptingCommands);
- }
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void DeliverPayloadAIUpdate::loadPostProcess( void )
- {
- // extend base class
- AIUpdateInterface::loadPostProcess();
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- DeliverPayloadStateMachine::DeliverPayloadStateMachine( Object *owner ) : StateMachine( owner, "DeliverPayloadStateMachine" )
- {
- // DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- static const StateConditionInfo considerConditions[] =
- {
- StateConditionInfo(DeliverPayloadStateMachine::isOffMap, RECOVER_FROM_OFF_MAP, NULL),
- StateConditionInfo(NULL, NULL, NULL) // keep last
- };
- // order matters: first state is the default state.
- defineState( APPROACH, newInstance(ApproachState)( this ), DELIVERING, CONSIDER_NEW_APPROACH );
- defineState( DELIVERING, newInstance(DeliveringState)( this ), HEAD_OFF_MAP, CONSIDER_NEW_APPROACH );
- defineState( CONSIDER_NEW_APPROACH, newInstance(ConsiderNewApproachState)( this ), APPROACH, HEAD_OFF_MAP, considerConditions );
- defineState( RECOVER_FROM_OFF_MAP, newInstance(RecoverFromOffMapState)( this ), APPROACH, APPROACH );
- defineState( HEAD_OFF_MAP, newInstance(HeadOffMapState)( this ), CLEAN_UP, CLEAN_UP );
- defineState( CLEAN_UP, newInstance(CleanUpState)( this ), INVALID_STATE_ID, INVALID_STATE_ID );
- }
- //-------------------------------------------------------------------------------------------------
- DeliverPayloadStateMachine::~DeliverPayloadStateMachine()
- {
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void DeliverPayloadStateMachine::crc( Xfer *xfer )
- {
- StateMachine::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void DeliverPayloadStateMachine::xfer( Xfer *xfer )
- {
- XferVersion cv = 1;
- XferVersion v = cv;
- xfer->xferVersion( &v, cv );
- StateMachine::xfer(xfer);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void DeliverPayloadStateMachine::loadPostProcess( void )
- {
- StateMachine::loadPostProcess();
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- /*static*/ Bool DeliverPayloadStateMachine::isOffMap( State *thisState, void* userData )
- {
- Object *owner = thisState->getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return TRUE;
- }
- return ai->isOffMap();
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- StateReturnType ApproachState::onEnter() // Give the move command
- {
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- if (ai->isAiInDeadState())
- {
- return STATE_FAILURE;
- }
- ai->aiMoveToPosition( ai->getMoveToPos(), CMD_FROM_AI );
- return STATE_CONTINUE;
- }
- //-------------------------------------------------------------------------------------------------
- StateReturnType ApproachState::update()
- {
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- if (ai->isAiInDeadState())
- {
- return STATE_FAILURE;
- }
- if (ai->isCloseEnoughToTarget())
- return STATE_SUCCESS;
- if (ai->getAIStateType() != AI_MOVE_TO)
- {
- DEBUG_CRASH(("hmm, bailed from moveto state early... should this be possible?"));
- ai->aiMoveToPosition( ai->getMoveToPos(), CMD_FROM_AI );
- }
- return STATE_CONTINUE;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void DeliveringState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void DeliveringState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferUnsignedInt(&m_dropDelayLeft);
- xfer->xferBool(&m_didOpen);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void DeliveringState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- StateReturnType DeliveringState::onEnter() // Open the pod bay doors, Hal
- {
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- /// @todo srj -- for now, this assumes at most one door
- owner->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_CLOSING, MODELCONDITION_DOOR_1_OPENING );
- m_dropDelayLeft = ai->getDoorDelay();
- m_didOpen = false;
- return STATE_CONTINUE;
- }
- //-------------------------------------------------------------------------------------------------
- StateReturnType DeliveringState::update() // Kick a dude out every so often
- {
- if (m_dropDelayLeft > 0)
- {
- --m_dropDelayLeft;
- return STATE_CONTINUE;
- }
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- ai->friend_setFreeToExit(true);
- m_didOpen = true;
- m_dropDelayLeft = ai->getDropDelay();
- if (!ai->isCloseEnoughToTarget())
- return STATE_FAILURE;
- const ContainedItemsList* items = owner->getContain() ? owner->getContain()->getContainedItemsList() : NULL;
- if( (!items || !items->size()) && ai->getVisibleItemsDelivered() == ai->getData()->m_visibleNumBones )
- {
- //We are out of payload to drop AND our visible payload is empty. It's possible for deliverers to
- //have one or the other or even both.
- return STATE_SUCCESS;
- }
- //Handle contained payload delivery.
- if( items )
- {
- Object* item = items->front();
- if( ai->isFireWeapon() )
- {
- Coord3D pos = *ai->getTargetPos();
- pos.x += ai->getDropOffset().x;
- pos.y += ai->getDropOffset().y;
- pos.z += ai->getDropOffset().z;
- owner->fireCurrentWeapon( &pos );
- TheGameLogic->destroyObject( item );
- }
- else
- {
- AIUpdateInterface* itemAI = item->getAIUpdateInterface();
- if (itemAI)
- {
- itemAI->aiExit(NULL, CMD_FROM_AI);
- }
- Coord3D pos = *item->getPosition();
- if (ai->getDropVariance().x > 0)
- pos.x += GameLogicRandomValueReal(-ai->getDropVariance().x, ai->getDropVariance().x);
- if (ai->getDropVariance().y > 0)
- pos.y += GameLogicRandomValueReal(-ai->getDropVariance().y, ai->getDropVariance().y);
- if (ai->getDropVariance().z > 0)
- pos.z += GameLogicRandomValueReal(-ai->getDropVariance().z, ai->getDropVariance().z);
- pos.x += ai->getDropOffset().x;
- pos.y += ai->getDropOffset().y;
- pos.z += ai->getDropOffset().z;
- item->setPosition(&pos);
- ContainModuleInterface *contain = item->getContain();
- if( ai->getData()->m_isParachuteDirectly && contain )
- {
- contain->setOverrideDestination( ai->getTargetPos() );// go for the direct hit
- }
- else if (itemAI)
- {
- // This will most likely be ignored, as the item is still in the transport, and therefore Held.
- itemAI->aiMoveToPosition( ai->getMoveToPos(), CMD_FROM_AI );
- }
-
- /// @todo srj -- urg. icky.
- static NameKeyType key_GenerateMinefieldBehavior = NAMEKEY("GenerateMinefieldBehavior");
- GenerateMinefieldBehavior* mfb = (GenerateMinefieldBehavior *)item->findUpdateModule(key_GenerateMinefieldBehavior);
- if (mfb)
- {
- mfb->setMinefieldTarget(ai->getMoveToPos());
- }
- if( ai->getData()->m_inheritTransportVelocity )
- {
- Coord3D velocity = *owner->getPhysics()->getVelocity();
- item->getPhysics()->applyForce( &velocity );
- }
- }
- }
- //Handle visible payload delivery.
- if( ai->getVisibleItemsDelivered() < ai->getData()->m_visibleNumBones )
- {
- Int attemptDrops = ai->getData()->m_visibleItemsDroppedPerInterval;
- Drawable *draw = owner->getDrawable();
- Bool updateSubObjects = false;
- while( attemptDrops && ai->getVisibleItemsDelivered() < ai->getData()->m_visibleNumBones )
- {
- //Drop an item!
- if( draw )
- {
- //First hide the subobject as it becomes the delivered payload.
- if( ai->getData()->m_visibleSubObjectName.isNotEmpty() )
- {
- AsciiString name;
- name.format( "%s%02d", ai->getData()->m_visibleSubObjectName.str(), ai->getVisibleItemsDelivered() + 1 );
- draw->showSubObject( name, false );
- updateSubObjects = true;
- }
- //Second, create the subobject for real
- if( ai->getData()->m_visiblePayloadTemplateName.isNotEmpty() )
- {
- const ThingTemplate* thing = TheThingFactory->findTemplate( ai->getData()->m_visiblePayloadTemplateName.str() );
- Object *payload = TheThingFactory->newObject( thing, owner->getControllingPlayer()->getDefaultTeam() );
-
- if( payload )
- {
- payload->setProducer( owner );
- if( ai->getData()->m_visibleDropBoneName.isNotEmpty() )
- {
- Coord3D pos;
- AsciiString bone;
- bone.format( "%s%02d", ai->getData()->m_visibleDropBoneName.str(), ai->getVisibleItemsDelivered() + 1 );
- if( draw->getPristineBonePositions( ai->getData()->m_visibleDropBoneName.str(), ai->getVisibleItemsDelivered() + 1, &pos, NULL, 1 ) > 0 )
- {
- draw->convertBonePosToWorldPos( &pos, NULL, &pos, NULL );
- payload->setPosition( &pos );
- }
- else
- {
- payload->setPosition( owner->getPosition() );
- }
- }
- else
- {
- payload->setPosition( owner->getPosition() );
- }
- payload->setOrientation( owner->getOrientation() );
- //If we want the payload to inherit the transport's velocity, do so now.
- if( ai->getData()->m_inheritTransportVelocity )
- {
- Coord3D startingForce = *owner->getPhysics()->getVelocity();
- startingForce.scale( payload->getPhysics()->getMass() );
- payload->getPhysics()->applyMotiveForce( &startingForce );
- Coord3D backPosition = *owner->getPhysics()->getVelocity();
- backPosition.scale( -1.0f );
- backPosition.add( payload->getPosition() );
- payload->setPosition( &backPosition );
- }
- //Are we firing a missile?
- Bool projectileFired = false;
- for( BehaviorModule** u = payload->getBehaviorModules(); *u; ++u )
- {
- ProjectileUpdateInterface* pui = (*u)->getProjectileUpdateInterface();
- if( pui )
- {
- //Missile!
- const WeaponTemplate *weaponTemplate = ai->getData()->m_visiblePayloadWeaponTemplate;
- if( !weaponTemplate )
- {
- DEBUG_ASSERTCRASH( 0, ("%s tried to fire missile %s via DeliverPayload, and is missing required weapon template in ObjectCreationList.ini entry.",
- owner->getTemplate()->getName().str(), payload->getTemplate()->getName().str() ) );
- break;
- }
- VeterancyLevel v = owner->getVeterancyLevel();
- pui->projectileFireAtObjectOrPosition( NULL, ai->getTargetPos(), weaponTemplate, weaponTemplate->getProjectileExhaust(v) );
- projectileFired = true;
- //damageInfo.in.m_sourceID = pui->projectileGetLauncherID();
- break;
- }
- }
- //If not, are we dropping a bomb?
- if( !projectileFired )
- {
- //Bomb!
- if( ai->getData()->m_exitPitchRate != 0 )
- {
- //If the payload needs to pitch, do so now.
- Real pitchRate = payload->getPhysics()->getCenterOfMassOffset() * ai->getData()->m_exitPitchRate;
- payload->getPhysics()->setPitchRate( pitchRate );
- }
- //If payload can move independently, order the move. A lot of bombs don't have locomotors,
- //in which case the "start move sound" is played, but nothing else actually happens.
- if( payload->getAIUpdateInterface() )
- {
- payload->getAIUpdateInterface()->aiMoveToPosition( ai->getMoveToPos(), CMD_FROM_AI );
- }
- }
- }
- }
- }
-
- //Update the counters
- attemptDrops--;
- ai->setVisibleItemsDelivered( ai->getVisibleItemsDelivered() + 1 );
- }
- if( updateSubObjects )
- {
- draw->updateSubObjects();
- }
- }
- return STATE_CONTINUE;
- }
- //-------------------------------------------------------------------------------------------------
- void DeliveringState::onExit( StateExitType ) // Close the doors
- {
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if (ai)
- ai->friend_setFreeToExit(false);
- DEBUG_ASSERTCRASH(m_didOpen, ("Not enough time for the doors to open to drop anything!"));
- /// @todo srj -- for now, this assumes at most one door
- owner->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_OPENING, MODELCONDITION_DOOR_1_CLOSING );
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void ConsiderNewApproachState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void ConsiderNewApproachState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferInt(&m_numberEntriesToState);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void ConsiderNewApproachState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- StateReturnType ConsiderNewApproachState::onEnter() // Increment local counter o' futility
- {
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- ++m_numberEntriesToState;
- DEBUG_LOG(("Considering approach #%d...\n",m_numberEntriesToState));
- if( m_numberEntriesToState > ai->getMaxNumberAttempts() )
- {
- DEBUG_LOG(("Too many approaches! Time to give up.\n"));
- return STATE_FAILURE;
- }
- // ok, play some games here... the pathfinder is pretty dumb about things with big turning radii,
- // so we have to force the issue here... basically, estimate our necessary turning radius
- // based on loco values, move off far enough so we can turn, then head back. (if we just say
- // "head back directly", the code will just keep turning in circles, not realizing that our
- // turning radius is too large for that ever to work.)
- Real minTurnRadius = ai->calcMinTurnRadius(NULL);
- // how far is "far enough"? we must be at least 2*radius dist away from our target.
- // (we add a little fudge since we may not be able to travel our max speed while
- // turning.)
- const Real DIST_FUDGE = 2.2f;
- Real minReApproachDist = minTurnRadius * DIST_FUDGE;
- const Coord3D* dir = owner->getUnitDirectionVector2D();
- Coord3D reApproachPoint;
- reApproachPoint.x = owner->getPosition()->x + dir->x * minReApproachDist;
- reApproachPoint.y = owner->getPosition()->y + dir->y * minReApproachDist;
- reApproachPoint.z = 0.0f; // yeah, yeah, should be terrainpos, but doesn't really matter here...
- ai->aiMoveToPosition( &reApproachPoint, CMD_FROM_AI );
- // we allow these guys to go to invalid (ie, off-map) positions
- ai->getCurLocomotor()->setAllowInvalidPosition(true);
- return STATE_CONTINUE;
- }
- //-------------------------------------------------------------------------------------------------
- StateReturnType ConsiderNewApproachState::update() // Success if we should try again, failure if we should give up
- {
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- // stay in this state 'till we reach our goal.
- if (ai->getAIStateType() != AI_MOVE_TO)
- {
- return STATE_SUCCESS;
- }
- return STATE_CONTINUE;
- }
- //-------------------------------------------------------------------------------------------------
- void ConsiderNewApproachState::onExit( StateExitType status )
- {
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return;
- }
- // go back to normal.
- ai->getCurLocomotor()->setAllowInvalidPosition(true);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void RecoverFromOffMapState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void RecoverFromOffMapState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferUnsignedInt(&m_reEntryFrame);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void RecoverFromOffMapState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- StateReturnType RecoverFromOffMapState::onEnter() // Increment local counter o' futility
- {
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- // have him hold in place, if possible.
- ai->aiMoveToPosition( owner->getPosition(), CMD_FROM_AI );
- // a little cheesy... make a delay based on turn-radius time to simulate offscreen
- // maneuvering.
- Real timeToTravelThatDist;
- /*Real minTurnRadius =*/ ai->calcMinTurnRadius(&timeToTravelThatDist);
- m_reEntryFrame = TheGameLogic->getFrame() + REAL_TO_INT_CEIL(timeToTravelThatDist);
- // kill its momentum
- PhysicsBehavior* physics = owner->getPhysics();
- physics->resetDynamicPhysics();
- ThePartitionManager->unRegisterObject( owner );
- if( owner->getDrawable() )
- owner->getDrawable()->setDrawableHidden( true );
- return STATE_CONTINUE;
- }
- //-------------------------------------------------------------------------------------------------
- StateReturnType RecoverFromOffMapState::update() // Success if we should try again, failure if we should give up
- {
- if (TheGameLogic->getFrame() < m_reEntryFrame)
- return STATE_CONTINUE;
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- ThePartitionManager->registerObject( owner );
- if( owner->getDrawable() )
- owner->getDrawable()->setDrawableHidden( false );
- Coord3D enterCoord = TheTerrainLogic->findClosestEdgePoint( owner->getPosition() );
- if (owner->isAboveTerrain())
- enterCoord.z = owner->getPosition()->z;
- owner->setPosition(&enterCoord);
- Real enterAngle = atan2(ai->getMoveToPos()->y - enterCoord.y, ai->getMoveToPos()->x - enterCoord.x);
- owner->setOrientation(enterAngle);
- PhysicsBehavior* physics = owner->getPhysics();
- physics->resetDynamicPhysics();
- return STATE_SUCCESS;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- StateReturnType HeadOffMapState::onEnter() // Give move order out of town
- {
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- ai->killDeliveryDecal();
- if ( ai->getData()->m_selfDestructObject ) //Lorenzen... this makes delivery unit pop away like a bubble...
- {
- TheGameLogic->destroyObject( owner );
- return STATE_CONTINUE;
- }
- //Coord3D exitCoord = TheTerrainLogic->findClosestEdgePoint( owner->getPosition() );
- // just keep moving straight ahead till we exit the map.
- Coord3D exitCoord = *owner->getPosition();
- const Coord3D* dir = owner->getUnitDirectionVector2D();
- Region3D terrainExtent;
- TheTerrainLogic->getExtent( &terrainExtent );
- const Real FUDGE = 1.2f;
- Real HUGE_DIST = FUDGE * sqrt(sqr(terrainExtent.hi.x - terrainExtent.lo.x) + sqr(terrainExtent.hi.y - terrainExtent.lo.y));
- exitCoord.x += dir->x * HUGE_DIST;
- exitCoord.y += dir->y * HUGE_DIST;
- ai->getCurLocomotor()->setAllowInvalidPosition(true);
- ai->getCurLocomotor()->setUltraAccurate(true); // set ultra-accurate just so AI won't try to adjust our dest
- ai->aiMoveToPosition( &exitCoord, CMD_FROM_AI );
- // once we get into head-off-map state, we're done... don't respond to anything else.
- // this addresses the bug where a plane delivering team "foo" hasn't made it off the map
- // before all of team "foo" lands, whereupon team "foo" is given a script command to
- // (say) move to a given point... and since the cargo plane might be a member of "foo"
- // it would respond to said command, which sucks and is confusing. might be better
- // to have these guys generally ignore most commands, but this is pretty safe, in that
- // AFAIK nothing with deliverpayload should ever respond to anything, not even IDLE,
- // once they start heading off the map. (srj)
- ai->friend_setAcceptingCommands(false);
- return STATE_CONTINUE;
- }
- //-------------------------------------------------------------------------------------------------
- StateReturnType HeadOffMapState::update()
- {
- Object *owner = getMachineOwner();
- DeliverPayloadAIUpdate *ai = (DeliverPayloadAIUpdate*)owner->getAIUpdateInterface();
- if( !ai )
- {
- return STATE_FAILURE;
- }
- if (ai->isOffMap())
- return STATE_SUCCESS;
- return STATE_CONTINUE;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- StateReturnType CleanUpState::onEnter() // Delete my successful butt
- {
- if( getMachineOwner()->getContain() )
- {
- DEBUG_ASSERTCRASH(getMachineOwner()->getContain()->getContainCount() == 0, ("did not drop all items!"));
- }
- Object *owner = getMachineOwner();
- TheGameLogic->destroyObject( owner );
- return STATE_CONTINUE;
- }
|